• 链表——双链表


    🟡前言

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

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

    🟡概述

    1️⃣定义

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

    2️⃣示意图

    在这里插入图片描述

    🟡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;
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183

    🟡结语

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

  • 相关阅读:
    从零到一搭建个人在线技术文档
    JAVA计算机毕业设计手办销售系统源码+系统+mysql数据库+lw文档
    十四、内置模块path、邂逅Webpack和打包过程、css-loader
    【GB28181】wvp-GB28181-pro快速适配 连接SQlite3数据库
    mysql数据库中的索引-2
    前端笔记(9) Vue3 async await 循环调接口使用案例
    【SQL刷题】Day9----SQL过滤数据专项练习
    再服务器上配置其他版本的DGL
    Docker的运用场景 103.53.124.2
    【开发神器】自动化测试、用 Apipost!
  • 原文地址:https://blog.csdn.net/Alita233_/article/details/126424090