【LeetCode】链表经典问题解析:环形、回文与相交
文章目录
- 一、判断环形链表
- 问题描述
- 解题思路
- 代码实现
- 二、环形链表 II
- 问题描述
- 解题思路
- 代码实现
- 三、判断链表的回文结构
- 问题描述
- 解题思路
- 代码实现
- 四、相交链表
- 问题描述
- 解题思路
- 代码实现
一、判断环形链表
问题描述
给定一个链表的头节点 head
,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。
解题思路
- 初始化快指针
fast
和慢指针slow
都指向头节点head
。 - 在循环中,快指针每次移动两步,慢指针每次移动一步。
- 若快慢指针相遇,说明链表存在环,返回
true
;若快指针走到末尾(fast
或fast.next
为null
),说明链表无环,返回false
。
代码实现
public class Solution {public boolean hasCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if (slow == fast) {return true;}}return false;}
}
二、环形链表 II
问题描述
给定一个链表的头节点 head,返回链表开始入环的第一个节点。如果链表无环,则返回 null。
解题思路
- 首先用快慢指针法判断链表是否有环,若快慢指针相遇,说明有环。
- 若有环,假设起点到环起点距离为X,环的距离为C,相遇点到环起点距离为Y
- 因为快指针速度为慢指针2倍,所以到相遇,快指针路程也是慢指针的2倍,经过计算得到X == Y。
- 将慢指针重新指向头节点,然后快慢指针每次都移动一步,当它们再次相遇时,相遇节点就是入环的第一个节点。
代码实现
public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast = head;ListNode slow = head;// 判断是否有环while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;if (slow == fast) {break;}}// 无环情况if (fast == null || fast.next == null) {return null;}// 找入环第一个节点slow = head;while (slow != fast) {fast = fast.next;slow = slow.next;}return slow;}
}
三、判断链表的回文结构
问题描述
对于一个链表,设计时间复杂度为 (O(n))、额外空间复杂度为 (O(1)) 的算法,判断其是否为回文结构。
解题思路
-
用快慢指针找到链表的中间节点。
-
反转中间节点之后的链表部分。
-
从原来链表的头和尾依次比较节点值是否相等,若都相等则为回文链表。
代码实现
public class Solution {public boolean chkPalindrome(ListNode A) {if (A == null || A.next == null) {return true;}ListNode slow = A;ListNode fast = A;// 找中间节点while (fast != null && fast.next != null) {fast = fast.next.next;slow = slow.next;}// 反转后半部分链表ListNode cur = slow.next;ListNode curNext;while (cur != null) {curNext = cur.next;cur.next = slow;slow = cur;cur = curNext;}// 比较原链表前半部分和反转后的后半部分ListNode first = A;while (first != slow) {if (first.val != slow.val) {return false;}// 处理偶数长度链表的终止情况if (first.next == slow) {return true;}first = first.next;slow = slow.next;}return true;}
}
注意:
反转后半个列表后slow就在原列表尾部,所以由first
从前往后,slow
从后往前遍历列表
- 奇数个节点:当slow.val == first.val即为遍历完成
- 偶数个节点:不可以直接按照奇数个的情况来判断,因为会出现
first
和slow
互换的情况。当first.next==slow
即遍历完成
四、相交链表
问题描述
给你两个单链表的头节点 headA
和 headB
,找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
。
解题思路
- 分别计算两个链表的长度
lenA
和lenB
。 - 让较长的链表先移动长度差的步数,使得两个链表剩余部分长度相同。
- 然后同时移动两个链表的指针,若指针相遇,则相遇节点为相交起始节点;若遍历结束都未相遇,则无相交节点。
代码实现
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA == null || headB == null) {return null;}ListNode pl = headA;ListNode ps = headB;int lenA = 0;int lenB = 0;while (pl != null) {lenA++;pl = pl.next;}while (ps != null) {lenB++;ps = ps.next;}int len = lenA - lenB;pl = headA;ps = headB;if (len < 0) {len = -len;pl = headB;ps = headA;}for (int i = 0; i < len; i++) {pl = pl.next;}while (ps != pl) {ps = ps.next;pl = pl.next;}return pl;}
}