双链表

一)链表简介

链表概念:链表是一种物理存储单元上非连续、非顺序的存储结构,也就是说链表存储的数据不一定是有序的。

链表结构:链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

 

双链表:每一个结点中,包含一个数据域,两个指针域,一个指针域指向上一个结点,一个指针域指向下一个结点。

双链表图解:头部结点(first),上一结点指针域(prev),数据域(data)、下一结点指针域(next),尾部结点(last)。头部first结点的prev指向为null,尾部last结点的next指向null

 

二)双链表

第一步:初始化双链表结构

/**
 * 双链表
 * @author ouyangjun
 */
public class DoubleLinkedList<E> {

    /** 双链表结构 */
    static final class Node<E> {
        E data; // 数据域
        Node<E> prev; // 指针域, 指向上一个结点
        Node<E> next; // 指针域, 指向下一个结点
		
        Node(E x) {data = x;}
    }
	
    transient Node<E> first; // 指向双链表头部节点
    transient Node<E> last; // 指向双链表尾部节点
    transient int count; // 双链表结点数量
}

 

第二步:添加结点

场景一:在双链表头部添加结点。新添加结点a,把结点a的next指向结点a1,把结点a1的prev指向结点a,最后把新结点a作为新的头部first结点。

/** 在双链表头部添加结点 */
public boolean addFirst(E e) {
    if (e == null) throw new NullPointerException();
		
    Node<E> newNode = new Node<E>(e); // 新结点
		
    Node<E> f = first; // 声明一个临时指针指向first
    newNode.next = f; // 把新结点的next指向头部first结点
    first = newNode; // 把新结点作为新的头部first结点
    if (last == null) {
        last = newNode; // 当尾部last结点为null时, 新结点即是头部结点也是尾部结点
    } else {
        f.prev = newNode; // 把原先的头部first结点的prev指向新结点
    }
    ++count; // 累计双链表结点数量
    return true;
}

 

场景二:在双链表尾部添加结点。新增结点a,把结点a的prev指向结点a4,把结点a4的next指向结点a,最后把新结点a作为新的尾部last结点。

/** 在双链表尾部添加结点 */
public boolean addLast(E e) {
    if(e == null) throw new NullPointerException();
		
    Node<E> newNode = new Node<E>(e); // 新结点
		
    Node<E> l = last; // 声明一个临时指针指向last
    newNode.prev = l; // 把新结点的prev指向尾部last结点
    last = newNode; // 把新结点作为新的尾部last结点
    if (first == null) {
        first = newNode; // 当头部first结点为null时, 新结点即是头部结点也是尾部结点
    } else {
        l.next = newNode; // 把原先的尾部last结点的next指向新结点
    }
    ++count; // 累计双链表结点数量
    return true;
}

 

场景三:在指定下标位置添加结点。在结点a2和结点a3之间新增结点a,把结点a的prev指向结点a2,把结点a结点next指向结点a3,把结点a2的next指向结点a,把结点a3的prev指向结点a。需考虑在头部、在中间、在尾部添加的情况。

/** 在指定下标位置添加结点 */
public void add(int index, E e) {
    if(index < 0 || index > count) throw new IllegalArgumentException();
    if(e == null) throw new NullPointerException();
		
    if (index == 0) { // 在头部first结点添加
        addFirst(e); 
    } else if (index == count) { // 在尾部last结点添加
        addLast(e);
    } else {
        Node<E> newNode = new Node<E>(e); // 新结点
			
        int position = 0; // 从头开始
        Node<E> f = first; // 声明一个临时指针指向first
        while (f != null) {
            if (index == position++) { // 查找对应的插入位置
                Node<E> p = f.prev; // 记录当前结点的prev
					
                newNode.prev = p; // 把新节点的prev指向p
                newNode.next = f; // 把新节点的next指向f
					
                p.next = newNode; // 把p的next指向新结点
                f.prev = newNode; // 把当前结点的prev指向新结点
					
                ++count;
                break;
            } else {
                f = f.next;
            }
        }
    }
}

 

第三步:删除结点

场景一:从双链表头部first删除结点。把原先头部first结点a1的next指向null,相当于断开结点a1,再把结点a2的prev指向null,并把结点a2作为新的头部first结点。

/** 从双链表头部first删除结点 */
public boolean removeFirst() {
    Node<E> f = first;
    if(f == null) return false;
		
    Node<E> n = f.next; // 获取头部first结点的next结点
    f.data = null; // 把头部first结点的data置null, 相当于回收
    f.next = f; // 把头部first结点断开
		
    first = n; // 把n结点作为新的头部结点
    if (n == null) { // 判断是否需要清除结点n的prev
        last = null;
    } else {
        n.prev = null;
    }
		
    --count; // 累减双链表结点数量
    return true;
}

 

场景二:从双链表尾部last删除结点。把原先尾部last结点a4的prve指向null,相当于断开结点a4,再把结点a3的next指向null,并把结点a3作为新的尾部last结点。

/** 从双链表尾部last删除结点 */
public boolean removeLast() {
    Node<E> l = last;
    if(l == null) return false;
		
    Node<E> p = l.prev; // 获取尾部last结点的prev结点
    l.data = null; // 把尾部last结点的data置null, 相当于回收
    l.prev = l; // // 把尾部last结点断开
		
    last = p; // 把p结点作为新的尾部结点
    if (p == null) { // 判断是否需要清除结点p的next
        first = null;
    } else {
        p.next = null;
    }
		
    --count; // 累减双链表结点数量
    return true;
}

 

场景三:删除双链表中指定data的结点。删除结点a2,把结点a2的prev和next指向结点都置null,相当于断开,把结点a1的next指向结点a3,把结点a3的prev指向结点a1。需考虑在头部、在中间、在尾部删除的情况。

/** 删除双链表中指定data的结点 */
public boolean remove(E e) {
    if(e == null) throw new NullPointerException();
		
    Node<E> deleteNode = new Node<E>(e); // 需删除的结点
		
    Node<E> f = first;
    Node<E> l = last;
    if (f == null || deleteNode.data.equals(f.data)) { // 删除头部结点
        return removeFirst();
    } else if (l == null || deleteNode.data.equals(l.data)) { // 删除尾部结点
        return removeLast();
    } else {
        while (f != null) {
            if (deleteNode.data.equals(f.data)) {
                Node<E> p = f.prev; // 获取当前节点的prev
                Node<E> n = f.next; // 获取当前节点的next
					
                p.next = n; // 把p节点的next指向n
                n.prev = p; // 把n节点的prev指向n
                
                f.data = null; // 置null
                f.prev = f; // 指向本身,相当于断开
                f.next = f; // 指向本身,相当于断开
					
                --count;
                break;
            }
            f = f.next;
        }
    }
    return true;
}

 

第四步:打印双链表结点信息

/** 打印双链表结点信息 */
public void printDoubleLinkedList() {
    System.out.println("----从头部first结点打印----begin");
    Node<E> f = first;
    while (f != null) {
        System.out.println(f.data);
        f = f.next;
    }
    System.out.println("----从头部first结点打印----end");
		
    System.out.println("----从尾部last结点打印----begin");
    Node<E> l = last;
    while (l != null) {
        System.out.println(l.data);
        l = l.prev;
    }
    System.out.println("----从尾部last结点打印----end");
}

 

识别二维码关注个人微信公众号

本章完结,待续,欢迎转载!
 
本文说明:该文章属于原创,如需转载,请标明文章转载来源!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值