文章目录

  • 大纲图
  • 双向链表
  • 双向链表的基本结构
  • 双向链表的基本操作
    • 头插
    • 尾插
    • 中间部位插入
    • 删除头部
    • 删除尾部
    • 删除中间位置的数据
    • 查找
    • 更新
    • Code
  • 总结


大纲图


双向链表

Algorithms_基础数据结构(02)_链表&链表的应用案例之单向链表中梳理了 单向链表的基本操作,接下来我们继续来看下双向链表吧。


双向链表的基本结构

单向链表只有一个方向,结点只有一个后继指针next指向后面的结点。

双向链表,顾名思义,它支持两个方向,每个结点不止有一个后继指针next指向后面的结点,还有一个前驱指针prev指向前面的结点。

双向链表需要额外的两个空间来存储后继结点前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。

虽然两个指针比较浪费存储空间,但可以支持双向遍历,这样也带来了双向链表操作的灵活性。那相比单链表,双向链表适合解决哪种问题呢?

-----> B+Tree:Mysql索引 叶子节点 双向链表


双向链表的基本操作

头插


尾插


中间部位插入


删除头部


删除尾部


删除中间位置的数据


查找

通常,双向链表同单链表一样,都仅有一个头指针。所以双链表查找指定元素的实现同单链表类似,都是从表头依次遍历表中元素,直到找到对应的元素为止。


更新

更改双链表中指定结点数据域的操作那必须要先查找到该节点,因此是在查询的基础上完成的。------>即:通过遍历找到存储有该数据元素的结点,直接更改其数据域即可。


Code

/*** @author 小工匠* @version v1.0* @create 2020-01-03 06:08* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class ArtisanDoubleLinkedList {private ArtisanNode head; // head节点private ArtisanNode tail; // tail节点  为了方便直接获取tail节点,省去每一次都要遍历的操作private int size; // 链表元素数量/*** 双向链表初始化*/public ArtisanDoubleLinkedList() {this.head = null;this.tail = null;}/*** 头插* @param data*/public  void add2Head(Object data) {ArtisanNode node = new ArtisanNode(data); // 新的Nodeif (this.head == null) { // 如果head节点为null,  head和tail节点均为这个新的node节点this.tail = node;} else {// 将原来的头节点的前驱节点指向node, 将新节点的后驱节点指向headthis.head.pre = node;node.next = head;}this.head = node; // 将新的节点置为head节点size++;}/*** 尾插 (低效)** @param data*/public void add2Tail(Object data) {// 从头部遍历,找到最后的节点,然后加到尾部ArtisanNode node = new ArtisanNode(data); // 要加入的节点ArtisanNode currentNode = head;if (currentNode == null){add2Head(data);}while(currentNode !=null){if (currentNode.next == null){ // 说明找到了当前的tail节点currentNode.next = node ;// 将当前tail节点的next指针指向新的tail节点node.pre = currentNode; //新的tail节点的pre指向当前tail节点节点this.tail = node;break;}else{currentNode = currentNode.next;}}size++;}/*** 尾插 (利用tail 无需遍历 效率更高)** @param data*/public void add2Tail2(Object data) {// 已经设置tail了,直接用即可,效率更高ArtisanNode node = new ArtisanNode(data); // 要加入的节点if (this.head == null){add2Head(data);}else {tail.next = node;node.pre = tail;tail = node;}}/**** @param postition* @param data*/public void add2Nth(int postition ,Object data) {ArtisanNode newNode = new ArtisanNode(data); // 新的NodeArtisanNode currentNode = head;if (postition == 0 ){ // 如果是0 ,添加到头节点add2Head(data);}else {for (int i = 1; i < postition; i++) { // 找到要插入的位置的前面的节点currentNode = currentNode.next;}// 与后继节点建立双层逻辑关系newNode.next = currentNode.next;currentNode.next.pre = newNode;// 与前置节点建立双层逻辑关系currentNode.next = newNode;newNode.pre = currentNode;}size++;}/*** 根据value 查找元素* @param data* @return*/public ArtisanNode find(Object data){ // 从头遍历ArtisanNode currentNode = head;while(currentNode != null){if (data.equals(currentNode.data)){printPreAndNextInfo(currentNode);break;}else{currentNode = currentNode.next;}}return currentNode;}/*** 删除头部节点*/public  void deleteHead(){this.head = this.head.next; // 将当前头节点的下一个节点置为头节点this.head.pre = null; // 将前置节点置为nullsize--;}/*** 删除尾部节点*/public void deleteTail(){ArtisanNode currentNode = this.head;ArtisanNode previousNode = null;while (currentNode != null){if (currentNode.next == null){currentNode.pre = null;// 最后一个节点的pre置为置为nullpreviousNode.next = null;// 前置节点的next指针置为nullthis.tail = previousNode; // 将当前节点的前一个节点置为tail节点}else { // 如果当前节点的next指针指向不为空,则把下个节点置为当前节点,继续遍历previousNode = currentNode;// 保存上一个节点的信息currentNode = currentNode.next;}}}/*** 删除指定位置的节点* @param position*/public ArtisanNode deleteNth(int position){ArtisanNode currentNode = this.head;if (position == 0 ){deleteHead();}else {for (int i = 1 ; i < position ; i++){// 找到要删除节点的前一个节点currentNode = currentNode.next;}currentNode.next.next.pre = currentNode; // 将  要删除节点的后一个节点的前驱节点 指向 当前节点(要删除的节点的前一个节点)currentNode.next = currentNode.next.next; // 将 要删除节点的前一个节点的next指针指向 要删除节点的后一个节点}size--;return currentNode.next ; // 返回删除的节点}/*** 获取tail节点* @return tail节点*/public ArtisanNode getTail(){System.out.println("tail节点的值为:" + this.tail.data );return this.tail;}/*** 获取head节点* @return head节点*/public ArtisanNode getHead(){System.out.println("head节点的值为:" + this.head.data );return this.head;}/*** 打印链表中的数据*/public void print() {ArtisanNode currentNode = this.head;// 从head节点开始遍历while (currentNode != null) { // 循环,节点不为空 输出当前节点的数据System.out.print(currentNode.data + " -> ");currentNode = currentNode.next; // 将当前节点移动到下一个节点,循环直到为null}System.out.print("null");System.out.println();}/*** 打印前后节点信息* @param currentNode*/private void printPreAndNextInfo(ArtisanNode currentNode) {System.out.println("当前节点:" + currentNode.data);if (currentNode.pre != null){System.out.println("当前节点【" + currentNode.data + "】的前驱节点:" + currentNode.pre.data);}else{System.out.println("当前节点【"+ currentNode.data + "】为head节点");}if (currentNode.next != null){System.out.println("当前节点【"+ currentNode.data + "】的后继节点:" + currentNode.next.data);}else{System.out.println("当前节点【"+ currentNode.data + "】为tail节点");}}public static void main(String[] args) {ArtisanDoubleLinkedList doubleLinkedList = new ArtisanDoubleLinkedList();doubleLinkedList.add2Head("artisanData96");doubleLinkedList.add2Head("artisanData97");doubleLinkedList.add2Head("artisanData99");doubleLinkedList.add2Head("artisanData98");doubleLinkedList.getTail();doubleLinkedList.add2Tail("artisanData100");doubleLinkedList.getTail();doubleLinkedList.print();doubleLinkedList.getHead();//        doubleLinkedList.add2Nth(2,"addedDataByPos");//        doubleLinkedList.add2Tail2(1);
//        doubleLinkedList.add2Tail2(2);
//        doubleLinkedList.add2Tail2(3);
//        doubleLinkedList.add2Tail2(4);//        doubleLinkedList.print();
//
//        System.out.println("tail:" + doubleLinkedList.tail.data);
//
//        doubleLinkedList.find("artisanData98");
//        doubleLinkedList.deleteHead();
//        doubleLinkedList.print();
//        doubleLinkedList.find("artisanData99");//        System.out.println("被删除节点:" + doubleLinkedList.deleteNth(1).data);
//        doubleLinkedList.print();
//        doubleLinkedList.find("artisanData96");}/*** 双向链表中的节点*/class ArtisanNode {ArtisanNode pre; // 前驱结点Object data; // 数据ArtisanNode next;// 后继节点public ArtisanNode(Object data) {this.data = data;}}
}

总结

重要区别:

  • 1.数组简单易用,在实现上使用的是连续的内存空间,可以借助CPU的缓存机制,预读数组中的数据,所以访问效率更高。

  • 2.链表在内存中并不是连续存储,所以对CPU缓存不友好,没办法有效预读。

  • 3.数组的缺点是大小固定,一经声明就要占用整块连续内存空间。如果声明的数组过大,系统可能没有足够的连续内存空间分配给它, 导致“内存不足(out ofmemory)”。如果声明的数组过小,则可能出现不够用的情况。注意下标越界的问题。

  • 4.动态扩容:数组需再申请一个更大的内存空间,把原数组拷贝进去,非常费时。链表本身没有大小的限制,天然地支持动态扩容,使用的时候也需要考虑占用内存的问题。

Algorithms_基础数据结构(03)_线性表之链表_双向链表相关推荐

  1. Algorithms_基础数据结构(04)_线性表之链表_单向循环链表约瑟夫环问题

    文章目录 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 约瑟夫问题 结构 分析 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 tip:单向链表 约瑟夫问题 N个人围成一圈, ...

  2. Algorithms_基础数据结构(02)_线性表之链表_单向链表

    文章目录 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 约瑟夫问题 顺序表VS 链表 链表的定义 链表的特点 常见的链表结 单向链表 单向链表的查找 单向链表的插入 头插 尾部插入 中间 ...

  3. 数据结构_Java_基于 线性表-单链表的初始化、逆序、去重、非递减序列的合并(开辟新链表先整体插入一个链表全部元素,再遍历另外一个链表寻找合适位置插入 、开辟新链表实现舍弃原链表)等操作实现

    写在前面 不久前学习了数据结构线性表-数组-链表的相关知识,用C/C++语言实现了 单链表的系列相关操作 .见往期博客: 数据结构实验2_C语言_基于顺序表的非递减有序表的合并.线性表元素的增.删.改 ...

  4. 数据结构与算法——线性表(链表篇)

  5. 有十五个数按由大到小顺序存放在一个数组中_数据结构基础 (代码效率优化, 线性表, 栈, 队列, 数组,字符串,树和二叉树,哈希表)...

    作者:张人大 代码效率优化 复杂度 -- 一个关于输入数据量n的函数 时间复杂度 -- 昂贵 与代码的结构设计有着紧密关系 一个顺序结构的代码,时间复杂度是O(1), 即任务与算例个数 n 无关 空间 ...

  6. Java数据结构与算法_线性表_顺序表与链表

    文章目录 线性表 顺序表 顺序表API设计 顺序表的代码实现 链表 单向链表 双向链表 总结 线性表 概述 线性表是最基本.最简单.也是最常用的一种数据结构. 一个线性表是n个具有相同特性的数据元素的 ...

  7. 黑马程序员 C语言数据结构与算法之线性表(链表/栈/队列/顺序表)

    C语言 链表基础知识清晰讲解(黑马) 讲的蛮好,就是音质不太好,有时听不清讲的啥! [黑马]数据结构与算法之线性表(链表/栈/队列/顺序表)[配套源码 嘛蛋,看错了,这是java的... 文章目录 链 ...

  8. 算法实例_线性表 By:比方

    算法实例_线性表 By:比方 什么是线性表? 从线性表的功能逻辑上来看,线性表就是由n(n>=0)个数据元素的排序组合,数据由x1,x2,x3,...,xn结构有序的顺序排列. 线性表的结构和特 ...

  9. C语言数据结构-第二章线性表-电大

    第二章线性表--内容简介 本章将进入线性结构的学习. 线性结构是最简单.最常用的一种数据结构. 本章将学习线性表的定义.顺序和链式两种存储方式及相应存储结构上的运算实现.通过典型示例训练,掌握线性表的 ...

最新文章

  1. Python中怎么判定一个List里面的元素是不是全部一样
  2. 请问:如何实现文件日志功能?要求每天换一个文件。文件名以日期区分
  3. 反调试技术揭秘(转)
  4. java软件开发ea介绍_开发说明 — Eacloud 1.0 documentation
  5. 点餐系统ip地址_spring boot实战——微信点餐系统03:微信授权(用户授权),免费内网穿透(固定ip)...
  6. System Information Viewer系统信息、硬件规格检测器
  7. 设计模式学习笔记(九:中介者模式)
  8. windows7 系统下载(2)
  9. CentOS7配置阿里源
  10. 打开word后发现页面在左侧且只有72%。点击单页虽居中但大小也没法变动,调节右下角的大小变化但页面依然在左侧。
  11. 常见的二十种软件测试方法详解(史上最全)
  12. 常微分方程I ODE的例子3 生态学模型:Malthus增长模型、Lotka-Volterra模型
  13. SQL——查询和1002号的同学学习的课程完全相同的其他同学的学号和姓名
  14. 为什么现在台式计算机无光驱,为何现在大部分电脑(笔记本和台式机)都取消了光驱?...
  15. 纪念一下| 上传资源的创作者等级升级到Lv3
  16. sql2008属于mysql吗_SQL Server 2008 属于( )数据库系统。
  17. matlab中变压器电感基值公式,基于MATLAB的变压器短路阻抗计算.pdf
  18. 电脑启动显示:bootmgr is missing
  19. 【JAVA】-- 期末考试试题含答案(选择、判断、填空、简答)(一)
  20. jquery实现表格列锁定

热门文章

  1. cmake之 ADD_LIBRARY()
  2. rdesktop 登录腾讯云
  3. sql case 语句
  4. Ubuntu 安装redis desktop manager
  5. php连接到mysql数据库,PHP MySQL:连接到MySQL数据库
  6. 91. Leetcode 62. 不同路径 (动态规划-路径规划)
  7. Leetcode 213. 打家劫舍 II (每日一题 20211009)
  8. 强化学习笔记:Q_learning (Q-table)示例举例
  9. 深度学习应用实战案例-员工流失预测模型(Python源代码)
  10. Python应用实战-在Python中进行数据处理操作的几种方法