• 03-java实现双向链表


    本人git https://github.com/bigeyes-debug/Algorithm

    一丶双向链表

    • 单向链表只能通过node单向next从头遍历链表,只能直接获得后继,无法获得前驱
    • 双向链表增加prev属性,prev属性指向前驱
    • 双向链表可以从first 和last两个方向开始查找

    二丶双向链表接口设计

    • 相较于单项链表,双向链表需要重写查找节点、插入节点、删除节点、清空节点四个方法。

    三丶双向链表的实现

    3.1 构造方法

    • 在双向链表属性中增加last属性记录尾节点。在Node属性中增加prev属性记录上一个节点。

    9411bebb17a014beb37d4871fb778cb9.png

    public class LinkedList<E> extends AbstractList<E> {    
        private Node<E> first;
        //增加last节点
        private Node<E> last; 
    	
        private static class Node<E> {
            E element;
            //增加上一个节点
            Node<E> prev; 
            Node<E> next;
            public Node(Node<E> prev, E element, Node<E> next) {
                this.prev = prev;
                this.element = element;
                this.next = next;
            }
        }
    }
    
    

    3.2 查找节点

    • 我们可以从 first也可以从last开始查找节点,,选择哪种查找方式,根据节点的下标决定
    • 如果要查找的索引大于size/2,就从last开始查找
    • 如果要查找的索引小于size/2,就从first开始查找

    tips:乘除法较慢,我们使用位运算来代替,右移一位就相当于/2,反正,就*2
    例如 seize>>1 就等价于size/2

    private Node<E> node(int index) {
        rangeCheck(index);
    		
        // 判断节点是在链表前一半还是后一半
        if (index < (size >> 1)) {
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        } else {
            Node<E> node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
            return node;
        }
    }
    
    

    3.3 清空节点

    public void clear() {
        size = 0;
        first = null;
        last = null;
    }
    
    

    3.4 插入节点

    • 普通情况
    newNode.next=prev.next;
    newNode.prev=prev;
    oldNode=prev.next;
    oldNode.prev=newNode;
    prev.next=newNode;
    
    • 特殊情况

    头尾节点的情况

    public void add(int index, E element) {
        rangeCheckForAdd(index);
    
        // 往最后面添加元素
        if (index == size) { 
            Node<E> oldLast = last;
            last = new Node<>(oldLast, element, null);
            // 这是链表添加的第一个元素
            if (oldLast == null) { 
                first = last;
            } else {
                oldLast.next = last;
            }
        } else {
            //插入位置的原节点,即为新节点的next节点。
            Node<E> next = node(index); 
            //新添加节点的上一个节点,即为该位置原节点的上一个节点。
            Node<E> prev = next.prev; 
            //创建新添加节点。
            Node<E> node = new Node<>(prev, element, next);
            //原节点的上一个节点,为新添加节点。
            next.prev = node;
            // index == 0
            if (prev == null) { 
                first = node;
            } else {
                //原节点上一个节点的next,即为新添加节点。
                prev.next = node;
            }
        }
        size++;
    }
    
    

    3.5 删除节点

    • 删除节点, 只需要让被删除节点的前一个节点与后一个节点之间链接, 同时去掉被删除节点引用即可。
    • 需要注意的是, 第0个节点和最后一个节点要特殊处理。

    四、双向链表vs动态数组

  • 相关阅读:
    【BZOJ4445】【SCOI2015】—小凸想跑步(半平面交)
    【BZOJ4444】【SCOI2015】—国旗计划(倍增+贪心)
    【BZOJ4443】【SCOI2015】—小凸玩矩阵(二分+最大匹配)
    【BZOJ4518】【SDOI2016】—征途(斜率优化dp)
    【BZOJ4199】【NOI2015】—品酒大会(后缀数组)
    【BZOJ3160】【2013湖北互测week1】—万径人踪灭(FFT+Manacher)
    ifconfig 查看网卡信息
    rm:删除目录和文件
    查看进程:ps
    wget 下载命令
  • 原文地址:https://www.cnblogs.com/bianzhuo/p/13488243.html
Copyright © 2020-2023  润新知