数据结构 - [LinkedList]

链表的数据结构

简介

链表是一种常见的数据结构,它通过节点(Node)来存储元素,并使用指针或引用将这些节点链接在一起。链表的主要优点是插入和删除操作非常高效,因为它不需要移动其他元素。

Java中LinkedList使用的链表类型

  • 双向链表:Java中的LinkedList实现基于双向链表。这意味着每个节点不仅知道下一个节点的地址,还知道前一个节点的地址。这种设计使得从列表两端进行插入和删除操作都同样高效。

链表的基本操作及其时间复杂度

  1. 插入
    • 在给定位置插入元素的时间复杂度为O(n),因为可能需要遍历链表找到正确的插入点。
    • 但在头尾插入时,由于LinkedList是双向链表,所以时间复杂度为O(1)。
  2. 删除
    • 删除特定值的元素通常需要遍历链表以找到该元素,因此时间复杂度为O(n)。
    • 如果已经知道了要删除的节点的位置,则可以在O(1)时间内完成删除。
  3. 获取元素
    • 获取指定索引处的元素需要遍历链表直到找到目标位置,这导致了O(n)的时间复杂度。

使用链表的最佳场景

  • 当你的应用程序频繁地在列表中间执行插入或删除操作时,链表是一个很好的选择。这是因为数组在这种情况下效率较低,因为它需要移动大量元素来维持顺序。
  • 对于实现队列或栈等数据结构,特别是当你希望保持高效的添加和移除操作时,链表也非常适用。
  • 在内存分配不连续的情况下,链表可以提供更好的性能,因为它不需要像数组那样要求连续的内存空间。

相关的实现代码

LinkedList.java

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
/**
* @Author: 无糖茶 wiretender.top
* @CreateTime: 2025-09-26
* @Description:
* @Version: 1.0
*/
package linked_list;

public class LinkedListTest<E> implements List<E> {

transient int size = 0;

transient Node<E> first;

transient Node<E> last;

public LinkedListTest() {}

void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null) {
last = newNode;
} else {
f.prev = newNode;
}
size ++;
}

void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null) {
first = newNode;
} else {
l.next = newNode;
}
size ++;
}

@Override
public boolean add(E e) {
linkLast(e);
return true;
}

@Override
public boolean addFirst(E e) {
linkFirst(e);
return true;
}

@Override
public boolean addLast(E e) {
linkLast(e);
return true;
}

@Override
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}

E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;

if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}

if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}

x.item = null;
size --;
return element;
}
@Override
public E get(int index) {
return node(index).item;
}

@Override
public void printLinkList() {
if (this.size == 0) {
System.out.println("链表为空");
} else {
Node<E> temp = first;
System.out.println("目前的列表,头节点:" + first.item + " 尾节点:" + last.item + " 整体:");
while(temp != null) {
System.out.println(temp.item + ",");
temp = temp.next;
}
System.out.println();
}
}

Node<E> node(int index) {
// 这里是 size >> 1 是右移一位,也就是除以二,看距离头节点近还是尾节点近
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i ++) {
x = x.next;
}
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i --) {
x = x.prev;
}
return x;
}
}
/*
链表节点的私有类
*/
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;


/**
* 节点的构造函数
* @param element 传入的节点的值
* @param prev 前置节点的指针
* @param next 后置节点的指针
*/
public Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.prev = prev;
this.next = next;
}
}


}

Link.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package linked_list;

/**
* @Author: 无糖茶 wiretender.top
* @CreateTime: 2025-09-26
* @Description: List 接口
*/
public interface List<E> {

boolean add(E e);

boolean addFirst(E e);

boolean addLast(E e);

boolean remove(Object o);

E get(int index);

void printLinkList();

}