链表——双链表

简介: 双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点

🟡前言


21天挑战赛第三周,本文将介绍有关双链表的知识


活动地址:CSDN21天学习挑战赛


🟡概述


1️⃣定义


双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点


2️⃣示意图


f67e43e332184521815d561572242b50.png


🟡API设计


1.节点API设计


1️⃣构造方法


Node(T t,Node pre,Node next):创建Node对象


2️⃣成员变量


  • T item:存储数据
  • Node next:指向下一个结点
  • Node pre:指向上一个结点


2.双链表API设计


1️⃣构造方法


TowWayLinkList():创建LinkList对象


2️⃣成员方法


  • public void clear():将一个线性表置为空表


  • public boolean isEmpty():判断当前线性表是否为空表


  • public int length():获取线性表的长度


  • public T get(int i):获取当前索引对应元素


  • public void insert(int i, T t):向线性表中索引值为i处前添加元素t


  • public void insert(T t):向线性表中添加元素t


  • public T remove(int i):删除i处元素值,并返回该元素


  • public int indexOf(T t) :查找t元素第一次出现位置


  • public T getFirst():获取第一个元素


  • public T getLast():获取最后一个元素


3️⃣成员内部类


private class Node:节点类


4️⃣成员变量


  • private Node head:记录头节点
  • private Node last:记录尾结点
  • private int N:记录链表长度


🟡代码实现


public class TowWayLinkList<T> implements Iterable<T> {
    //首结点
    private Node head;
    //最后一个结点
    private Node last;
    //链表的长度
    private int N;
    //结点类
    private class Node{
        public Node(T item, Node pre, Node next) {
            this.item = item;
            this.pre = pre;
            this.next = next;
        }
        //存储数据
        public T item;
        //指向上一个结点
        public Node pre;
        //指向下一个结点
        public Node next;
    }
    public TowWayLinkList() {
       //初始化头结点和尾结点
        this.head = new Node(null,null,null);
        this.last=null;
        //初始化元素个数
        this.N=0;
    }
    //清空链表
    public void clear(){
        this.head.next=null;
        this.head.pre=null;
        this.head.item=null;
        this.last=null;
        this.N=0;
    }
    //获取链表长度
    public int length(){
        return N;
    }
    //判断链表是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //获取第一个元素
    public T getFirst(){
        if (isEmpty()){
            return null;
        }
        return head.next.item;
    }
    //获取最后一个元素
    public T getLast(){
        if (isEmpty()){
            return null;
        }
        return last.item;
    }
    //插入元素t
    public void insert(T t){
        if (isEmpty()){
            //如果链表为空:
            //创建新的结点
            Node newNode = new Node(t,head, null);
            //让新结点称为尾结点
            last=newNode;
            //让头结点指向尾结点
            head.next=last;
        }else {
            //如果链表不为空
            Node oldLast = last;
            //创建新的结点
            Node newNode = new Node(t, oldLast, null);
            //让当前的尾结点指向新结点
            oldLast.next=newNode;
            //让新结点称为尾结点
            last = newNode;
        }
        //元素个数+1
        N++;
    }
    //向指定位置i处插入元素t
    public void insert(int i,T t){
        //找到i位置的前一个结点
        Node pre = head;
        for(int index=0;index<i;index++){
            pre=pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点
        Node newNode = new Node(t, pre, curr);
        //让i位置的前一个结点的下一个结点变为新结点
        pre.next=newNode;
        //让i位置的前一个结点变为新结点
        curr.pre=newNode;
        //元素个数+1
        N++;
    }
    //获取指定位置i处的元素
    public T get(int i){
        Node n = head.next;
        for(int index=0;index<i;index++){
            n=n.next;
        }
        return n.item;
    }
    //找到元素t在链表中第一次出现的位置
    public int indexOf(T t){
        Node n = head;
        for(int i=0;n.next!=null;i++){
            n=n.next;
            if (n.next.equals(t)){
                return i;
            }
        }
        return -1;
    }
    //删除位置i处的元素,并返回该元素
    public T remove(int i){
        //找到i位置的前一个结点
        Node pre = head;
        for(int index=0;index<i;index++){
            pre=pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nextNode= curr.next;
        //让i位置的前一个结点的下一个结点变为i位置的下一个结点
        pre.next=nextNode;
        //让i位置的下一个结点的上一个结点变为i位置的前一个结点
        nextNode.pre=pre;
        //元素的个数-1
        N--;
        return curr.item;
    }
    @Override
    public Iterator<T> iterator() {
        return new TIterator();
    }
    private class TIterator implements Iterator{
        private Node n;
        public TIterator(){
            this.n=head;
        }
        @Override
        public boolean hasNext() {
            return n.next!=null;
        }
        @Override
        public Object next() {
            n=n.next;
            return n.item;
        }
    }
}


🟡结语


双链表的实现与单链表不同,对于删除元素后的指向会比较容易搞错,所以比较难以理解,下一篇文章将讲述有关快慢指针的问题

相关文章
|
6月前
特殊链表(循环单链表,循环双链表,静态链表)
特殊链表(循环单链表,循环双链表,静态链表)
49 3
|
6月前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
算法
使用双链表来分隔原始链表
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
60 0
LeetCode刷题Day04——链表(设计单/双链表、移除、翻转、交换链表节点)
迭代法:首先创建一个临时的节点p用于遍历链表,其开始可以指向头节点,也可以让其next节点指向头节点(如果p指向头节点则while循环的判断条件是p!=null,反之则是p.next!=null),随后p不断地向后移动,在这个过程中进行要求的操作。如果结果要返回头指针,可以实现创建一个节点让其next指向头指针。 如果是要删除元素,那么需要拥有前一个节点的指针,让其指向要删除的元素的下一个元素,所以此时则不能让p指向头节点,而应该是让next去指向,即判断的是下一个元素的值,这样才能够实现删除。 如果是要翻转链表,那么需要不断改变指针的方向,即让next等于之前的元素,所以需要一个变量prev
|
存储 缓存
链表(二) 双链表操作详解
链表(二) 双链表操作详解
36 0
|
存储 算法 Java
Java数据结构与算法分析(三)链表(单链表、双链表、环形链表)
通过前篇文章《[数组](https://blog.csdn.net/gozhuyinglong/article/details/109702860)》了解到数组的存储结构是一块连续的内存,插入和删除元素时其每个部分都有可能整体移动。为了避免这样的线性开销,我们需要保证数据可以不连续存储。本篇介绍另一种数据结构:链表。
218 0
|
存储 Java
【DS】链表的介绍和实现(单/双链表)
【DS】链表的介绍和实现(单/双链表)
92 0
【DS】链表的介绍和实现(单/双链表)
|
存储 Java
java实现双向循环链表(循环双链表)
线性表是我们最常用的一种数据结构,线性表包含顺序表和链表,顺序表典型应用就是我们常用的ArrayList,链表的典型应用其中就有我们常用的LinkedList。LinkedList他的底层就是使用链表来存储数据元素的。这篇文章用以总结链表中的双向循环链表,为单链表的结点增加一个指向前驱的指针域,单链表就变成了双链表,将双链表的头尾相连,双链表就成了双向循环链表。
271 0
java实现双向循环链表(循环双链表)
|
存储 C语言
C语言双链表,循环链表,静态链表讲解(王道版)
目录 一、双链表 初始化(带头结点): 双链表的插入: 双链表的遍历 循环链表 循环单链表的初始化 循环双链表的初始化 双链表的插入 双链表的删除 静态链表 定义静态链表 插入 删除
175 0
C语言双链表,循环链表,静态链表讲解(王道版)