标题:链表相关算法详解:揭秘链表操作的奥秘
引言:
链表是数据结构中常见的一种形式,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表操作是编程中常用的技术之一,本文将详细介绍链表的常见操作及相关算法,并通过举例说明,帮助读者更好地理解和运用链表。
一、链表的基本操作
1.1 链表的创建
链表的创建可以通过动态分配内存来实现。我们首先创建一个头节点,然后根据需要逐个添加其他节点。
举例说明:
假设我们要创建一个链表存储学生信息,每个节点包含学生的姓名和年龄。我们首先创建一个头节点,然后逐个添加其他节点。
1.2 链表的插入
链表的插入操作是指在链表的指定位置插入一个新节点。插入节点的关键是找到插入位置的前一个节点,然后将新节点的指针指向下一个节点,再将前一个节点的指针指向新节点。
举例说明:
假设我们有一个已经创建好的链表,现在要在第二个节点后插入一个新节点,新节点的值为“张三”,年龄为20。
1.3 链表的删除
链表的删除操作是指删除链表中的某个节点。删除节点的关键是找到要删除节点的前一个节点,然后将前一个节点的指针指向要删除节点的下一个节点。
举例说明:
假设我们有一个已经创建好的链表,现在要删除第三个节点。
1.4 链表的查找
链表的查找操作是指在链表中查找某个特定值的节点。查找节点的关键是遍历链表,逐个比较节点的值,直到找到目标节点或链表结束。
举例说明:
假设我们有一个已经创建好的链表,现在要查找值为“李四”的节点。
二、链表的常见算法
2.1 链表的反转
链表的反转是指将链表中的节点顺序颠倒。反转链表的关键是修改节点的指针指向,使每个节点的指针指向前一个节点。
代码实现:
/**
* 链表反转(递归实现)
*
* @param head 1->2->3->4->5->null
* 1->2<-3<-4<-5
* @return
*/
public static ListNode reverse(ListNode head) {
if (head.next == null) {
return head;
}
ListNode subNode = reverse(head.next);
head.next.next = head;
head.next = null;
return subNode;
}
/**
* 链表反转(迭代实现)
*
* @param head null 1 -> 2 -> 3 -> 4 -> 5 -> null pre:null cur:1 next=2
* null <-1 2 -> 3 -> 4 -> 5 -> null pre:1 cur:2 next=3
* null <-1 <- 2 3 -> 4 -> 5 -> null pre:2 cur:3 next=4
* null <-1 <- 2 <- 3 4->5 -> null pre:3 cur:4 next=5
* null <-1 <- 2 <- 3 <- 4 5 -> null pre:4 cur:5 next=6
* null <-1 <- 2 <- 3 <- 4 <- 5 null pre:5 cur:null
* @return
*/
public static ListNode reverse2(ListNode head) {
ListNode pre = null;
ListNode cur = head;
ListNode next = null;
while (cur != null) {
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
/**
* 链表前m-n个反转(递归实现)
*
* @param head
* @return
*/
public static ListNode reverseMN(ListNode head, int m, int n) {
if (m == 1) {
return reverseN(head, n);
}
head.next = reverseMN(head.next, m - 1, n - 1);
return head;
}
/**
* 链表前m-n个反转(迭代实现)
*
* @param head
* @return null -> 1 -> 2 <- 3 <- 4 5
*/
public static ListNode reverseMN2(ListNode head, int m, int n) {
ListNode pre = null;
ListNode cur = head;
ListNode next = head;
ListNode mNode = null;
int k = 1;
while (cur != null && k <= n) {
next = cur.next;
if (k == m) {
mNode = cur;
}
if (k >= m) {
cur.next = pre;
}
pre = cur;
cur = next;
k++;
}
mNode.next.next = pre;
mNode.next = cur;
return head;
}
/**
* k个一组反转链表(不足k时不反转)
*
* @param head
* @param k
* @return
*/
public static ListNode reverseKGroup(ListNode head, int k) {
int i = 0;
ListNode node = head;
while (i < k) {
if (node == null) {
return head;
}
i++;
node = node.next;
}
ListNode pre = head;
ListNode newNode = reverseN2(head, k);
pre.next = reverseKGroup(node, k);
return newNode;
}
2.2 链表的合并
链表的合并是指将两个有序链表合并成一个有序链表。合并链表的关键是比较两个链表的节点值,将较小值的节点插入到新链表中。
代码实现:
/**
* 合并有序链表(非递归)
*
* @param aNode a链表
* @param bNode b链表
* @return 合并链表
*/
public static LinkNode<Integer> mergeOrderLinkedListA(LinkNode<Integer> aNode, LinkNode<Integer> bNode) {
LinkNode<Integer> tempNode = new LinkNode<Integer>();
LinkNode<Integer> mergeNode = tempNode;
while (aNode != null || bNode != null) {
if (aNode == null) {
tempNode.next = bNode;
tempNode = tempNode.next;
bNode = bNode.next;
continue;
}
if (bNode == null) {
tempNode.next = aNode;
tempNode = tempNode.next;
aNode = aNode.next;
continue;
}
if (aNode.value >= bNode.value) {
tempNode.next = bNode;
bNode = bNode.next;
} else {
tempNode.next = aNode;
aNode = aNode.next;
}
tempNode = tempNode.next;
}
return mergeNode.next;
}
/**
* 合并有序链表(递归)
*
* @param aNode a链表
* @param bNode b链表
* @return 合并链表
*/
public static LinkNode<Integer> mergeOrderLinkedListB(LinkNode<Integer> aNode, LinkNode<Integer> bNode) {
LinkNode<Integer> mergeNode = null;
if (aNode != null && bNode != null) {
if (aNode.value >= bNode.value) {
mergeNode = bNode;
mergeNode.next = mergeOrderLinkedListB(aNode, bNode.next);
} else {
mergeNode = aNode;
mergeNode.next = mergeOrderLinkedListB(aNode.next, bNode);
}
}
if (aNode == null) {
mergeNode = bNode;
}
if (bNode == null) {
mergeNode = aNode;
}
return mergeNode;
}
/**
* 合并多个有序链表(递归)
*
* @param linkNodes 多个链表
* @return 排序链表
*/
public static LinkNode<Integer> mergeMoreOrderLinkedList(List<LinkNode<Integer>> linkNodes) {
LinkNode<Integer> mergeNode = null;
for (int i = 0; i < linkNodes.size(); i++) {
if (linkNodes.get(i) == null) {
linkNodes.remove(i);
}
}
if (linkNodes.size() == 1) {
mergeNode = linkNodes.get(0);
return mergeNode;
}
if (linkNodes.isEmpty()) {
mergeNode = null;
return mergeNode;
}
Integer min = linkNodes.get(0).value;
int minKey = 0;
for (int i = 0; i < linkNodes.size(); i++) {
if (linkNodes.get(i) != null && linkNodes.get(i).value < min) {
min = linkNodes.get(i).value;
minKey = i;
}
}
mergeNode = linkNodes.get(minKey);
LinkNode minLinkNode = linkNodes.get(minKey);
minLinkNode = minLinkNode.next;
if (minLinkNode == null) {
linkNodes.remove(minKey);
} else {
linkNodes.set(minKey, minLinkNode);
}
mergeNode.next = mergeMoreOrderLinkedList(linkNodes);
return mergeNode;
}
2.3 链表的环检测
链表的环检测是指判断链表中是否存在环。环检测的关键是使用快慢指针,如果两个指针相遇,则链表中存在环。
代码实现:
/**
* 判断链表是否有环
*
* @param listNode
* @return
*/
public static boolean isCycleLinkedNode(ListNode listNode) {
if (listNode == null) {
return false;
}
ListNode slow = listNode;
ListNode fast = listNode;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
结论:
本文详细介绍了链表的基本操作和常见算法,并通过举例说明,帮助读者更好地理解和运用链表。掌握链表操作的技巧,对于解决实际问题和提高编程能力都具有重要意义。希望本文能为读者提供帮助,欢迎大家留言交流。