• 数据结构之双向链表


    目录

    • 一、双向链表是什么?
    • 二、双向链表自实现
    •         1.node方法
    •         2.add
    •         3.remove
    •         4.clear分析
    • 三、双向链表 vs 单向链表
    • 四、双向链表 vs 动态数组

    一、双向链表是什么?

    使用双向链表是可以提升链表的综合性能的。一张图就可以表示得很清楚了

     

    二、双向链表自实现

    1.node方法(根据下标查找元素)

    1. private Node node(int index) {
    2. rangeCheck(index);//这是自己封装的一个检查函数,用来检查index的合法性,否则抛出异常
    3. if (index < (size >> 1)) {
    4. Node node = first;
    5. for (int i = 0; i < index; i++) {
    6. node = node.next;
    7. }
    8. return node;
    9. } else {
    10. Node node = last;
    11. for (int i = size - 1; i > index; i--) {
    12. node = node.prev;
    13. }
    14. return node;
    15. }
    16. }

    根据下标找出对应的节点,如果是单链表,则要从头到尾挨个找,但是对于双链表,我们可以先判断下标索引值与链表长度的一半进行对比,如果小于长度的一半,则可以从链表的头开始查找,如果大于了长度的一半,则可以从尾开始查找。

    2.add (添加元素)

    1. public void add(int index, E element) {
    2. rangeCheckForAdd(index); //这是自己封装的一个检查函数,用来检查index的合法性,否则抛出异常
    3. // size == 0
    4. // index == 0
    5. if (index == size) { // 往最后面添加元素
    6. Node oldLast = last;
    7. last = new Node<>(oldLast, element, null);
    8. if (oldLast == null) { // 这是链表添加的第一个元素
    9. first = last;
    10. } else {
    11. oldLast.next = last;
    12. }
    13. } else {
    14. Node next = node(index);
    15. Node prev = next.prev;
    16. Node node = new Node<>(prev, element, next);
    17. next.prev = node;
    18. if (prev == null) { // index == 0
    19. first = node;
    20. } else {
    21. prev.next = node;
    22. }
    23. }
    24. size++;
    25. }

    3.remove(删除元素)

    1. public E remove(int index) {
    2. rangeCheck(index); //这是自己封装的一个检查函数,用来检查index的合法性,否则抛出异常
    3. Node node = node(index);
    4. Node prev = node.prev;
    5. Node next = node.next;
    6. if (prev == null) { // index == 0
    7. first = next;
    8. } else {
    9. prev.next = next;
    10. }
    11. if (next == null) { // index == size - 1
    12. last = prev;
    13. } else {
    14. next.prev = prev;
    15. }
    16. size--;
    17. return node.element;
    18. }

      4.clear分析 

    1. public void clear() {
    2. size = 0;
    3. first = null;
    4. last = null;
    5. }

     按照之前咱们一般的理解,此时虽然已经把first和last置空了,与链表失去了联系,但是链表中的各个节点都相互指向,都被引用着,所以认为此时双链表不会被JVM回收。错了!!!此时链表相互指向的引线的性质已经变了,他们已经不再是gc root对象了。对于什么是gc root对象,简单的理解就是在栈中被局部变量指向的对象。所以此时clear这个函数是可以的。

     

    三、双向链表 vs 单向链表 

     粗略对比一下删除的操作数量:

    单向链表: 1 + 2 + 3 + ... + n = (1+n) ∗ n/ 2 = n/2 + n^ 2/ 2 ,除以n平均一下是 1/2 + n/2
    双向链表: (1 + 2 + 3 + ... + n/2 ) * 2 = ((1+ n/2)∗ n/2 )/2 * 2 = n/2 + n^2/4 ,除以n平均一下是 1/2 + n/4
    经过对比, 操作数量减少了一半

    四、双向链表 vs 动态数组 

    ◼动态数组 :开辟、销毁内存空间的次数相对较少,但可能造成内存空间浪费
    双向链表 :开辟、销毁内存空间的次数相对较多,但不会造成内存空间的浪费
    如果频繁在 尾部 进行 添加 删除 操作, 动态数组 双向链表 均可选择
    如果频繁在 头部 进行 添加 删除 操作,建议选择使用 双向链表
    如果有频繁的( 在任意位置 添加 删除 操作,建议选择使用 双向链表
    如果有频繁的 查询 操作(随机访问操作),建议选择使用 动态数组

     

  • 相关阅读:
    java97-中断线程的另一种处理
    RHCSA之linux的简单使用
    Java面试八股文整理
    时序分析基础(6)——input delay时序分析
    java集合专题List接口ArrayList/Vector/LinkedList底层结构和源码分析
    在AndroidR user版本的设备上,如何默认打开USB调试,如何去掉USB调试确认弹窗
    18.从组件外部调用一个方法
    Vue项目实战之人力资源平台系统(一)框架介绍及项目环境搭建
    计算机毕业设计之java+ssm学术成果管理系统
    【Apollo】Apollo的入门介绍
  • 原文地址:https://blog.csdn.net/crazy_xieyi/article/details/126153401