数据结构
链表是一种重要的数据结构,以下是关于它的全面介绍:
定义与概念
链表是由一系列节点组成的数据结构,每个节点包含数据元素和指向下一个节点的指针(在单向链表中)或指向前一个节点和后一个节点的指针(在双向链表中)。它通过指针将各个节点链接在一起,形成一种链式存储结构。
链表的类型
• 单向链表:每个节点只有一个指向下一节点的指针,表头节点的指针指向第一个节点,最后一个节点的指针指向空值。
• 双向链表:每个节点有两个指针,一个指向前驱节点,一个指向后继节点,表头节点的前驱指针和表尾节点的后继指针通常指向空值。
• 循环链表:可以是单向循环链表或双向循环链表。在单向循环链表中,最后一个节点的指针指向表头节点,形成一个环;双向循环链表中,表头节点的前驱指针指向表尾节点,表尾节点的后继指针指向表头节点。
链表的操作
• 插入节点:在链表中指定位置插入新节点。需修改相关节点的指针,使其正确链接。
• 删除节点:删除指定节点,同样要调整相关节点的指针,以保持链表的完整性。
• 遍历链表:从链表的头节点开始,依次访问每个节点,直到到达链表末尾。
链表的应用场景
• 数据动态存储:适合处理数据量不确定、需要频繁插入和删除的数据。如在操作系统的进程调度中,可使用链表来管理进程控制块。
• 实现栈和队列:可以用链表来实现栈和队列数据结构。如用单向链表实现栈,新元素入栈时在链表头部插入节点,出栈时删除头部节点。
• 图的邻接表表示:在图的邻接表存储结构中,每个顶点的邻接顶点可以用链表来存储。
链表的优缺点
• 优点:插入和删除操作效率高,只需修改指针,不需要移动大量元素;内存利用率高,按需分配内存。
• 缺点:随机访问效率低,需从表头开始遍历;不能直接通过下标访问元素;每个节点需额外存储指针,增加了内存开销。
链表20个常见的操作
以下是 Java 实现链表(以单向链表为例)的 20 个常见操作及代码示例:
-
定义链表节点类
class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
} -
创建空链表
class LinkedList {
private Node head; // 头节点指针public LinkedList() {
this.head = null;
}
} -
在头部插入节点
public void insertAtHead(int data) {
Node newNode = new Node(data);
newNode.next = head;
head = newNode;
} -
在尾部插入节点
public void insertAtTail(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
return;
}
Node temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = newNode;
} -
在指定位置插入节点(位置从 1 开始)
public void insertAtPosition(int data, int position) {
if (position < 1) return;
if (position == 1) {
insertAtHead(data);
return;
}
Node newNode = new Node(data);
Node prev = getNodeAt(position - 1);
if (prev == null) return; // 位置超出链表长度
newNode.next = prev.next;
prev.next = newNode;
} -
删除头部节点
public int deleteAtHead() {
if (head == null) throw new IllegalStateException("链表为空");
int data = head.data;
head = head.next;
return data;
} -
删除尾部节点
public int deleteAtTail() {
if (head == null) throw new IllegalStateException("链表为空");
if (head.next == null) { // 只有一个节点
int data = head.data;
head = null;
return data;
}
Node prev = head;
while (prev.next.next != null) {
prev = prev.next;
}
int data = prev.next.data;
prev.next = null;
return data;
} -
删除指定位置的节点(位置从 1 开始)
public int deleteAtPosition(int position) {
if (position < 1 || head == null) throw new IllegalStateException("位置无效或链表为空");
if (position == 1) return deleteAtHead();
Node prev = getNodeAt(position - 1);
if (prev == null || prev.next == null) throw new IllegalStateException("位置超出范围");
int data = prev.next.data;
prev.next = prev.next.next;
return data;
} -
删除指定值的第一个节点
public void deleteByValue(int value) {
if (head == null) return;
// 删除头节点
if (head.data == value) {
head = head.next;
return;
}
// 删除中间节点
Node prev = head;
while (prev.next != null && prev.next.data != value) {
prev = prev.next;
}
if (prev.next != null) { // 找到节点
prev.next = prev.next.next;
}
} -
获取链表长度
public int getLength() {
int count = 0;
Node temp = head;
while (temp != null) {
count++;
temp = temp.next;
}
return count;
} -
检查链表是否为空
public boolean isEmpty() {
return head == null;
} -
查找指定值是否存在
public boolean contains(int value) {
Node temp = head;
while (temp != null) {
if (temp.data == value) return true;
temp = temp.next;
}
return false;
} -
获取指定位置的节点值(位置从 1 开始)
public int getValueAtPosition(int position) {
Node node = getNodeAt(position);
if (node == null) throw new IllegalStateException("位置无效");
return node.data;
}
// 辅助方法:获取指定位置的节点(位置从 0 开始为头节点,1 为第二个节点...)
private Node getNodeAt(int index) { // index 从 0 开始
if (index < 0) return null;
Node temp = head;
for (int i = 0; i < index && temp != null; i++) {
temp = temp.next;
}
return temp;
}
14. 反转链表
public void reverse() {
Node prev = null;
Node current = head;
while (current != null) {
Node nextTemp = current.next;
current.next = prev;
prev = current;
current = nextTemp;
}
head = prev;
}
15. 打印链表所有节点值
public void printList() {
Node temp = head;
while (temp != null) {
System.out.print(temp.data + " -> ");
temp = temp.next;
}
System.out.println("NULL");
}
16. 清空链表
public void clear() {
head = null;
}
17. 判断链表是否有环(快慢指针法)
public boolean hasCycle() {
if (head == null || head.next == null) return false;
Node slow = head;
Node fast = head.next;
while (fast != null && fast.next != null) {
if (slow == fast) return true;
slow = slow.next;
fast = fast.next.next;
}
return false;
}
18. 合并两个有序链表(递归法)
public static Node mergeSortedLists(Node list1, Node list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
if (list1.data < list2.data) {
list1.next = mergeSortedLists(list1.next, list2);
return list1;
} else {
list2.next = mergeSortedLists(list1, list2.next);
return list2;
}
}
19. 查找链表中间节点(快慢指针法)
public Node findMiddleNode() {
if (head == null) return null;
Node slow = head;
Node fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
20. 复制链表(深拷贝)
public LinkedList copyList() {
LinkedList newList = new LinkedList();
Node current = head;
while (current != null) {
newList.insertAtTail(current.data);
current = current.next;
}
return newList;
}
使用示例
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.insertAtTail(1);
list.insertAtTail(2);
list.insertAtHead(0);
list.printList(); // 输出: 0 -> 1 -> 2 -> NULL
System.out.println("长度:" + list.getLength()); // 3
list.deleteAtPosition(2);
list.printList(); // 输出: 0 -> 2 -> NULL
list.reverse();
list.printList(); // 输出: 2 -> 0 -> NULL
}
说明
• 双向链表:需在 Node 类中增加 prev 指针,并在操作中维护前驱和后继指针。
• 循环链表:插入/删除时需将头尾节点指针相连(如尾节点 .next = head)。
• 复杂操作(如排序、合并)可结合递归或迭代实现,注意边界条件(空链表、单节点)。

浙公网安备 33010602011771号