链表应用 II
链表应用 II
应用 2:Leetcode 25. K 个一组翻转链表
题目
输入:
,
输出:
分析
这里,我们以前面题目中的用例,来说明算法的步骤。
为了避免讨论边界条件,这里,我们使用一个
首先,使用三个指针用于遍历主链表时记录子链表在主链表中的位置:
-
:记录子链表的首节点的前一个节点; -
记录子链表的首节点; -
记录子链表的尾节点。
然后,我们遍历主链表,当tail向后移动k步之后,tail所指的元素就是子链表的尾节点,如下图所示:
此时,
然后,我们对子链表进行倒序,我们定义一个两个指针:
-
记录子链表的当前节点的前一个节点; -
记录子链表的当前节点;
对子链表进行倒序操作:
子链表倒序的终止条件是
倒序完成后,重新将
将前驱指针
将子链表的尾结点的
重新指针
因此,我们可以总结出算法步骤如下:
-
使用一个指针
,先让它指向 节点,并用它来遍历主链表,使用如下四个指针,记录子链表在主链表中的位置:-
:前驱指针,用于记录子链表首节点的前一个节点; -
:记录子链表的首节点; -
:记录子链表的尾节点; -
:后驱指针,用于记录子链表尾节点的后一个节点;
-
-
指针
每移动 步:-
对
至 所在的子链表进行倒序操作; -
然后,重新将
指向倒序后的子链表尾节点,将 指向倒序后的子链表首节点; -
将倒序后的子链表重新接入主链表,即:
-
将前驱节点
的 指针指向的节点指向倒序后的子链表头节点 ; -
将倒序后的子链表尾节点
的 指针指向的节点指向后驱节点 ;
-
-
重新将
指向当前子链表的尾节点 ,即下一个子链表的前驱节点; -
重新将
指向当前子链表的尾节点 的下一个节点,即下一个子链表的头节点;
-
-
若指针
移动的步数小于 步,则直接返回;
代码实现
class Solution { public ListNode reverseKGroup(ListNode head, int k) { int n = listLength(head); if (n <= 1) { return head; } ListNode dummy = new ListNode(-1); dummy.next = head; // 记录子链表的前驱节点 ListNode subListPrev = dummy; ListNode subListHead = head; while (subListHead != null) { // 从前驱节点开始计算子链表的长度 ListNode subListTail = subListPrev; // 查看剩余部分的长度是否大于等于 k for (int count = 0; count < k; count++) { subListTail = subListTail.next; if (subListTail == null) { return dummy.next; } } // 记录子链表的后驱节点 ListNode subListPost = subListTail.next; // 子链表倒序 ListNode [] nodes = reverse(subListHead, subListTail); subListHead = nodes[0]; subListTail = nodes[1]; // 子链表的前驱节点指向子链表的头部 subListPrev.next = subListHead; // 子链表的尾节点指向子链表的后驱节点 subListTail.next = subListPost; // 更新前驱节点的位置 subListPrev = subListTail; // 更新子链表的头节点的位置 subListHead = subListTail.next; } return dummy.next; } private int listLength(ListNode head) { int length = 0; while (head != null) { length++; head = head.next; } return length; } private ListNode [] reverse(ListNode start, ListNode end) { ListNode p1 = null, p2 = start; ListNode temp; while (p1 != end) { temp = p2.next; p2.next = p1; p1 = p2; p2 = temp; } return new ListNode[]{end, start}; } }
应用2:Leetcode 234. 回文链表
题目
分析
方法一:数组
思路:
-
将链表的值复制到数组中;
-
使用双指针判断是否是回文。
方法二:递归
略。
方法三:快慢指针
算法个步骤:
-
找到前半部分链表的尾节点;
-
反转后半部分链表;
-
判断两个子链表是否回文;
-
恢复后面一个子链表;
-
返回结果。
代码实现
方法一
class Solution: def isPalindrome(self, head: Optional[ListNode]) -> bool: array = list() current = head while current: array.append(current.val) current = current.next return array == array[::-1]
复杂度:
-
时间复杂度:
-
空间复杂度:
方法二
class Solution: def __init__(self): self.front_pointer = None def isPalindrome(self, head: Optional[ListNode]) -> bool: self.front_pointer = head return self.check(head) def check(self, head): if head: if not self.check(head.next): return False if head.val != self.front_pointer.val: return False self.front_pointer = self.front_pointer.next return True
复杂度:
-
时间复杂度:
-
空间复杂度:
方法三
class Solution { public boolean isPalindrome(ListNode head) { ListNode preNode = getMidOfList(head); ListNode tailNode = reverseList(preNode.next); boolean result = true; ListNode p1 = head, p2 = tailNode; while (p1 != null && p2 != null) { if (p1.val != p2.val) { result = false; break; } p1 = p1.next; p2 = p2.next; } preNode.next = reverseList(tailNode); return result; } private ListNode reverseList(ListNode head) { if (head == null) { return null; } ListNode p1 = null, p2 = head; ListNode temp = null; while (p2 != null) { temp = p2.next; p2.next = p1; p1 = p2; p2 = temp; } return p1; } private ListNode getMidOfList(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } }
复杂度:
-
时间复杂度:
-
空间复杂度:
应用3:Leetcode 143. 重排链表
题目
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
请将其重新排列后变为:
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
分析
假设链表长度为
其中,寻找链表中点的方法,可以使用快慢指针的方式查找,当快指针到达链表末尾,慢指针刚好到达链表左半段的最后一个节点。
注意,如果链表长度为奇数时,前半段会多一个节点,只需要将其连接到新链表的末尾即可。
代码实现
class Solution { public void reorderList(ListNode head) { ListNode mid = getMidNode(head); // 将链表分成两个子链表 ListNode tailNode = reverse(mid.next); mid.next = null; // 将两个子链表按要求合并 ListNode dummy = new ListNode(-1); dummy.next = head; ListNode p = dummy; ListNode p1 = head, p2 = tailNode; while (p1 != null && p2 != null) { p.next = p1; p = p.next; p1 = p1.next; p.next = p2; p = p.next; p2 = p2.next; } // 如果节点为奇数,需要将剩余节点添加到末尾 if (p1 != null) { p.next = p1; } } private ListNode getMidNode(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; } private ListNode reverse(ListNode head) { ListNode p1 = null, p2 = head; ListNode temp; while (p2 != null) { temp = p2.next; p2.next = p1; p1 = p2; p2 = temp; } return p1; } }
本文作者:LARRY1024
本文链接:https://www.cnblogs.com/larry1024/p/17311327.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步