图解双向链表(Java)

前言

双向链表中比较难懂的地方是节点间的引用关系。

本文主要分析双向链表添加和删除,使用图片来解释引用间的指向关系,希望能帮助各位理清思路。
在这里插入图片描述

基础要点

(1)双向链表既可从头找到尾,也可以从尾找到头。

(2)双向链表中每个节点自身存储有数据和俩个引用,一个指向前面的节点(prev),一个指向后面的节点(next)。

基本结构

(1)firstlast分别作为链表的头部和尾部。

(2)通过对象引用找到对方。​

(3)Node类主要用来将数据包起来成为一个节点,并存储前后节点的引用。​

public class LinkedList<T> {

    /** 记录链表头部的节点 */
    private Node<T> first;

    /** 记录链表尾部的节点 */
    private Node<T> last;

    /** 链表中节点的总数量 */
    private int size;
    
    
    private class Node<T> {
        // 前一个节点的引用
        private Node<T> prev;
        // 节点存储的数据
        private T val;
        // 后一个节点的引用
        private Node<T> next;

        public Node() {
        }

        public Node(Node<T> prev, T val, Node<T> next) {
            this.prev = prev;
            this.val = val;
            this.next = next;
        }
    }
}

在这里插入图片描述

添加节点

添加第一个节点(头插尾插都行),存储数据:1。
第一个加进来的新节点既是头也是尾,前后都没有节点。
在这里插入图片描述


接着上面,继续向头节点追加第二个节点(addFirst),存储数据:2

(1)新节点的next引用 指向 头节点。
(2)头节点的prev引用 指向 新节点,完成双向引用的绑定。
(3)记录头节点的first引用 指向 新节点(新节点的prev节点为null,可以继续连接后续新节点),向前移一步。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


接着上面,继续向尾节点追加第三个节点(addLast),存储数据:3

(1)创建新节点,新节点的prev引用 指向 尾节点,成为链表中的一员。

(2)尾节点的next引用 指向 新节点,这一步完成双向引用绑定。

(3)记录尾节点的last引用 指向 新节点(新节点的next节点为null,可以继续连接后续新节点),向后移一步。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


思路理清楚后,转化成代码:

/**
  * 向链表头部追加节点
  *
  * @return
  */
public boolean addFirst(T val) {
    // 临时变量 获取当前first头节点的引用指向
    Node<T> f = first;

    // 创建新节点
    // prev:null,用来指向后续的新节点
    // val:要存储的数据
    // next:存放头节点的引用,将新节点的next节点 --> 头节点(相当于在头部节点前追加了一个元素)
    Node<T> newNode = new Node<>(null, val, f);

    // 判断尾节点是否为空
    if(last == null) {
        // 证明这个节点在链表当中是第一个加进来的(第一个加进来的既是头也是尾)
        last = newNode;
    } else {
        // 新节点 <-- 头节点的prev引用(这一步完成双向引用绑定)
        f.prev = newNode;
    }

    // 头节点的引用指向新节点
    // first的prev节点引用是null的,prev节点引用会指向后续的新节点
    first = newNode;

    // 节点数量+1
    size++;
    return true;
}


/**
 * 向链表尾部追加节点
 *
 * @return
 */
public boolean addLast(T val) {
    // 临时变量 存储尾节点的引用
    Node<T> l = last;

    // 创建新节点
    // prev:存放尾节点的引用,尾节点 <-- 新节点的prev节点(新节点的prev节点指向尾节点,相当于往尾部追加了一个新节点)
    // val:要存储的数据
    // next:null,用来指向后续的新节点
    Node<T> newNode = new Node<>(l, val, null);

    // 判断头节点是否为空
    if(first == null) {
        // 证明这个节点在链表当中是第一个加进来的(第一个加进来的既是头也是尾)
        first = newNode;
    } else {
        // 尾节点的next引用 --> 新节点(这一步完成双向引用绑定)
        l.next = newNode;
    }

    // 改变尾节点在链表中的引用位置
    // 尾节点引用 指向 新节点引用
    last = newNode;

	// 节点数量+1
    size++;
    
    return true;
}

删除节点

删除节点主要有三种情况:

(1)删除的节点是头节点。

(2)删除的节点是尾节点。

(3)删除的节点是俩个节点之间的节点。

现在链表中有5个节点。
在这里插入图片描述


删一个头节点,该节点存储的数据:4

cur:当前被删除的节点。

(1)记录头节点的first引用 指向 cur的next节点。

(2)cur的next节点的prev引用 指向 cur前面的null节点。

(3)cur的next引用 指向 null,这样节点就从链表中删了(更确切的说是节点的next和prev引用都为null,然后被回收了)。

(分清next引用与节点)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


接着上面,继续删除一个尾节点,该节点存储的数据:5

cur:当前被删除的节点。

(1)记录尾节点的last引用 指向 cur的prev节点。

(2)cur的prev节点的next引用 指向 cur后面的null节点。

(3)cur的prev引用 指向 null,从链表中移除,完成删除。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接着上面,继续 删除俩个节点之间的节点,该节点存储的数据:1

cur:当前被删除的节点。

(1)cur的 prev节点的next引用 指向 cur的 next节点。

(2)cur的 prev引用 指向 null。

(3)cur的 next节点的prev引用 指向 cur的 prev节点。

(4)cur的 next引用 指向 null,完成删除。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路理清楚后,转化成代码:

/**
  * 删除节点
  *
  * @param cur 当前要删除的节点
  * @return
  */
public T remove(Node<T> cur) {
    T curVal = cur.val;

    // 获取当前节点的前一个节点
    Node<T> prev = cur.prev;
    // 获取当前节点的后一个节点
    Node<T> next = cur.next;

    if (prev == null) {
        // 证明是头节点
        // 记录头节点的引用 改变为 当前节点的后一个节点
        first = next;
    } else {
        // 证明当前节点 前面有节点
        // 当前节点前一个节点 的next引用 指向 当前节点的后一个节点
        prev.next = next;

        // 当前节点的prev引用 指向 null
        cur.prev = null;
    }

    if (next == null) {
        // 证明是尾节点
        // 记录尾节点的引用 改变为 当前节点的前一个节点
        last = prev;
    } else {
        // 证明当前节点 后面有节点
        // 当前节点后一个节点 的prev引用 指向 当前节点的前一个节点
        next.prev = prev;

        // 当前节点的next引用 指向 null
        cur.next = null;
    }
    
    // 当前节点的值置为空
    cur.val = null;

    // 节点数量减一
    size--;

    return curVal;
}

查找相对简单,这里就不继续展开了。

在这里插入图片描述





如有错误,欢迎指正!

码字不易,觉得不错点个赞再走呗!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rococy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值