张小驰出没 张小驰出没
首页
  • Spring合集
  • SpringMVC合集
  • Mybatis合集
  • Spring Boot合集
  • Mybatis-plus合集
  • Spring Security合集
  • Vue合集
  • Redis合集
  • RabbitMQ合集
  • 数据结构

    • 数据结构
  • 算法解析

    • 日常算法
    • 剑指Offer
    • LeetCode
  • 技术与Bug

    • 技术文档
    • Bug解决方法
  • 个人博客

    • Hexo博客搭建
  • 我的项目
  • 我的面试
  • 分类
  • 标签
  • 归档
友链
关于
Hexo
GitHub

MoYu

何德何能,何其荣幸
首页
  • Spring合集
  • SpringMVC合集
  • Mybatis合集
  • Spring Boot合集
  • Mybatis-plus合集
  • Spring Security合集
  • Vue合集
  • Redis合集
  • RabbitMQ合集
  • 数据结构

    • 数据结构
  • 算法解析

    • 日常算法
    • 剑指Offer
    • LeetCode
  • 技术与Bug

    • 技术文档
    • Bug解决方法
  • 个人博客

    • Hexo博客搭建
  • 我的项目
  • 我的面试
  • 分类
  • 标签
  • 归档
友链
关于
Hexo
GitHub
  • 数据结构

    • 顺序表
    • 链表
      • 1.单向链表
      • 2.双向链表
      • 3.循环链表
  • 算法解析

  • 算法
  • 数据结构
MoYu
2022-01-25

链表

# 链表

现虽然顺序表的查询很快,时间复杂度为 O(1) , 但是增删的效率是比较低的,因为每一次增删操作都伴随着大量的数据元素移动。

所以可以使用另外一种存储结构实现线性表,链式存储结构。

链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只管的表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成。

1 2 3

# 1.单向链表

单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。

链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。

4

# 1.1 单链表实现

public class LinkList<T> implements Iterable<T> {
    //记录头节点
    private Node head;
    //记录元素个数
    private int n;
    public class Node {
        //存储元素
        public T item;
        //指向下一个结点
        public Node next;

        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
    //构造函数
    public LinkList() {
        //初始化头节点
        this.head = new Node(null, null);
        //初始化个数
        this.n = 0;
    }
    //清空链表
    public void clear() {
        this.head = null;
        this.n = 0;
    }
    //获取链表的长度
    public int length() {
        return n;
    }
    //判断链表是否为空
    public boolean isEmpty() {
        return n == 0;
    }
    //获取指定位置i出的元素
    public T get(int i) {
        Node m = head.next;
        //通过头结点,循环往后找到第i个元素
        for (int j = 0; j < i; j++) {
            m = m.next;
        }
        return m.item;
    }
    //向链表中添加元素t
    public void insert(T t) {
        //找到当前最后一个结点
        Node m = head;
        while (m.next != null) {
            m = m.next;
        }
        //创建新节点,值为t
        Node tNode = new Node(t, null);
        //最后一个元素指向新节点
        m.next = tNode;
        //元素个数+1
        n++;
    }
    //向指定位置i出,添加元素t
    public void insert(int i, T t) {
        //找出i之前的结点
        Node pre = this.head;
        for (int j = 0; j < i; j++) {
            pre = pre.next;
        }
        //指向i结点
        Node curr = pre.next;
        //创建结点,值为t,指向curr
        Node node = new Node(t, curr);
        //i之前的结点pre,指向node
        pre.next = node;
        //元素数+1
        n++;
    }
    //删除指定位置i处的元素,并返回被删除的元素
    public T remove(int i) {
        //找到i位置的前一个节点
        Node pre = head;
        for (int index = 0; index <= i - 1; i++) {
            pre = pre.next;
        }
        //要找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nNode = curr.next;
        //前一个结点指向下一个结点
        pre.next = nNode;
        //元素个数-1
        n--;
        return curr.item;
    }
    //查找元素t在链表中第一次出现的位置
    public int indexOf(T t) {
        //从头结点开始,依次找到每一个结点,取出item,和t比较,如果相同,就找到了
        Node n = head;
        for (int i = 0; n.next != null; i++) {
            n = n.next;
            if (n.item.equals(t)) {
                return i;
            }
        }
        return -1;
    }
    @Override
    public Iterator<T> iterator() {
        return new Literator();
    }
    public class Literator implements Iterator{

        private Node n;
        public Literator(){
            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

# 1.2 单链表测试

public static void main(String[] args) {
    //创建单向链表对象
    LinkList<String> sl = new LinkList<>();
    //测试插入
    sl.insert("T1");
    sl.insert("T2");
    sl.insert("T3");
    sl.insert(1, "T4");
    for (String s : sl) {
        System.out.println(s);
    }
    System.out.println("------------------------------------------");
    //测试获取
    String getResult = sl.get(1);
    System.out.println("获取索引1处的结果为:" + getResult);
    //测试删除
    String removeResult = sl.remove(0);
    System.out.println("删除的元素是:" + removeResult);
    //测试清空
    sl.clear();
    System.out.println("清空后的线性表中的元素个数为:" + sl.length());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

4

# 2.双向链表

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

链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。

6

# 2.1 双向链表实现

public class TowWayLinkList<T> implements Iterable<T> {
    //首结点
    private Node head;
    //尾结点
    private Node last;
    //元素总个数
    private int n;
    private class Node {
        //存储数据
        public T item;
        //指向上一个结点
        public Node pre;
        //指向下一个结点
        public Node next;
        public Node(T item, Node pre, Node next) {
            this.item = item;
            this.pre = pre;
            this.next = 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 nNode = new Node(t, head, null);
            //让新结点成为尾结点
            last = nNode;
            //让头结点指向尾结点
            head.next = last;
        } else {
            //链表不为空
            Node oldLast = last;
            //创建新结点
            Node nNode = new Node(t, oldLast, null);
            //让当前尾结点指向新结点
            oldLast.next = nNode;
            //让新结点成为尾结点
            last = nNode;
        }
        //元素个数+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 nNode = new Node(t, pre, curr);
        //让i位置的前一个结点的下一个结点指向新结点
        pre.next = nNode;
        //让i位置的前一个结点变为新结点
        curr.pre = nNode;
        //元素个数+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.item.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 nNode = curr.next;
        //i位置的前一个结点 的下一个结点 指向 i位置的下一个结点 的 前一个结点
        pre.next = nNode;
        //i位置的下一个结点 的前一个结点 指向 i位置的上一个结点 的 下一个结点
        nNode.pre = pre;
        //元素个数-1
        n--;
        return curr.item;
    }
    @Override
    public Iterator<T> iterator() {
        return new tIterator();
    }
    public class tIterator implements Iterator {
        private Node node;
        public tIterator() {
            this.node = head;
        }
        @Override
        public boolean hasNext() {
            return node.next != null;
        }
        @Override
        public Object next() {
            node=node.next;
            return node.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

# 2.2 双向链表测试

public static void main(String[] args) {
    //创建双向链表对象
    TowWayLinkList<String> sl = new TowWayLinkList<>();
    //测试插入
    sl.insert("T1");
    sl.insert("T2");
    sl.insert("T3");
    sl.insert(1, "T4");
    for (String s : sl) {
        System.out.println(s);
    }
    System.out.println("--------------------------------------");
    
    System.out.println("第一个元素是:" + sl.getFirst());
    System.out.println("最后一个元素是:" + sl.getLast());
    
    System.out.println("------------------------------------------");
    //测试获取
    String getResult = sl.get(1);
    System.out.println("获取索引1处的结果为:" + getResult);
    //测试删除
    String removeResult = sl.remove(0);
    System.out.println("删除的元素是:" + removeResult);
    //测试清空
    sl.clear();
    System.out.println("清空后的线性表中的元素个数为:" + sl.length());
}
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
7

# 3.循环链表

循环链表,链表整体要形成一个圆环状。在单向链表中,最后一个节点的指针为null,不指向任何结点,因为没有下一个元素了。

实现循环链表,只需要让单向链表的最后一个节点的指针指向头结点即可。

8

简单构建

public class CircularLinkedList {
    public static class Node<T> {
        T item;
        Node<T> next;
        public Node(T item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
    public static void main(String[] args) throws Exception {
        //构建结点
        Node<Integer> first = new Node<Integer>(1, null);
        Node<Integer> second = new Node<Integer>(2, null);
        Node<Integer> third = new Node<Integer>(3, null);
        Node<Integer> fourth = new Node<Integer>(4, null);
        Node<Integer> fifth = new Node<Integer>(5, null);
        Node<Integer> six = new Node<Integer>(6, null);
        Node<Integer> seven = new Node<Integer>(7, null);
        //构建单链表
        first.next = second;
        second.next = third;
        third.next = fourth;
        fourth.next = fifth;
        fifth.next = six;
        six.next = seven;
        //构建循环链表,让最后一个结点指向第一个结点
        seven.next = first;
    }
}
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
#数据结构
顺序表
《日常算法》笔记

← 顺序表 《日常算法》笔记→

最近更新
01
约瑟夫问题
01-25
02
快慢指针
01-25
03
链表反转
01-25
更多文章>
Theme by Vdoing | Copyright © 2021-2022 MoYu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式