part2
24.两两交换链表中的节点
分析
- 设置虚拟头结点,虚拟头结点的下一个是头结点
- 虚拟头结点的下一个为头结点的下一个
- 头结点的下一个为脖节点的下一个
- 虚拟头结点的下一个结点的下一结点设置为头结
代码
go
func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
dum := &ListNode{Next: head}
cur := dum
for cur.Next != nil && cur.Next.Next != nil {
first := cur.Next
second := cur.Next.Next
first.Next = second.Next
cur.Next = second
second.Next = first
cur = first
}
return dum.Next
}
19.删除链表的倒数第N个节点
分析
- 双指针
- 见代码行间注释
代码
go
func removeNthFromEnd(head *ListNode, n int) *ListNode {
// 创建一个虚拟头节点
dummy := &ListNode{Next: head}
// 初始化快慢指针,都指向虚拟头节点
s, f := dummy, dummy
// 先让快指针 f 前进 n 步
for n > 0 && f.Next != nil {
f = f.Next
n--
}
// 快指针再前进一步,使得快慢指针之间的距离为 n+1
// 这样当快指针到达链表末尾时,慢指针正好指向倒数第 n+1 个节点
f = f.Next
// 快慢指针同时前进,直到快指针到达链表末尾
for f != nil {
s = s.Next
f = f.Next
}
// 此时慢指针 s 指向倒数第 n+1 个节点
// 删除倒数第 n 个节点(即 s.Next)
s.Next = s.Next.Next
// 返回真正的头节点
return dummy.Next
}
面试题 02.07. 链表相交
分析
- 相遇法,走过你来时的路
- 差值双指针
- 哈希映射法
代码
go
// 相遇法
// 4 0 5 1 2 8
// 3 1 2 8
// 4 0 5 1 2 8 3 1 2 8
// 3 1 2 8 4 0 5 1 2 8
func getIntersectionNode(headA, headB *ListNode) *ListNode {
l1, l2 := headA, headB
for l1 != l2 {
if l1 != nil {
l1 = l1.Next
} else {
l1 = headB
}
if l2 != nil {
l2 = l2.Next
} else {
l2 = headA
}
}
return l1
}
// 哈希映射
func getIntersectionNode1(headA, headB *ListNode) *ListNode {
// 创建一个哈希集合,用于存储链表A中的所有节点
nodeSet := make(map[*ListNode]bool)
// 将链表A中的所有节点加入哈希集合
curA := headA
for curA != nil {
nodeSet[curA] = true
curA = curA.Next
}
// 遍历链表B,检查节点是否在哈希集合中
curB := headB
for curB != nil {
if nodeSet[curB] {
return curB // 找到相交节点
}
curB = curB.Next
}
return nil // 没有相交节点
}
// 双指针差值法
func getIntersectionNode2(headA, headB *ListNode) *ListNode {
// 计算两个链表的长度
lenA, lenB := 0, 0
curA, curB := headA, headB
for curA != nil {
lenA++
curA = curA.Next
}
for curB != nil {
lenB++
curB = curB.Next
}
// 让较长的链表先走差值步
curA, curB = headA, headB
if lenA > lenB {
for i := 0; i < lenA-lenB; i++ {
curA = curA.Next
}
} else {
for i := 0; i < lenB-lenA; i++ {
curB = curB.Next
}
}
// 同时遍历两个链表,直到找到相交节点
for curA != curB {
curA = curA.Next
curB = curB.Next
}
return curA // 如果没有相交,curA和curB最终都为nil
}
142.环形链表 II
分析
- 见代码分析
- 数学题
代码
go
func detectCycle(head *ListNode) *ListNode {
// 处理空链表或只有一个节点的情况
if head == nil || head.Next == nil {
return nil
}
// 初始化快慢指针
s, f := head, head
// 第一阶段:检测是否有环
for f != nil && f.Next != nil {
// 先移动指针
s = s.Next
f = f.Next.Next
// 然后检查是否相遇
if s == f {
// 找到环,进入第二阶段
// 重置一个指针到链表头
p := head
// 两个指针以相同速度移动,相遇点即为环的入口
for p != s {
p = p.Next
s = s.Next
}
return p
}
}
// 没有环
return nil
}
总结
- 双指针
- 模拟行为
- 哈希表
- 数学思维