[Go] LeetCode第四天|24.两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题02.07.链表相交 142.环形链表 II
24.两两交换链表中的节点
题目链接: https://leetcode.cn/problems/swap-nodes-in-pairs/
关键词:单链表
解法一:双指针
设置虚拟头结点,统一操作
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
dummy := &ListNode{
Next: head,
}
temp := dummy
for temp.Next != nil && temp.Next.Next != nil { //如果temp.Next 为 nil,则 temp.Next.Next != nil不会执行,避免invalid memory address or nil pointer dereference
node1 := temp.Next
node2 := temp.Next.Next
temp.Next = node2
node1.Next = node2.Next
node2.Next = node1
temp = node1
}
return dummy.Next
}
解法二:递归
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func swapPairs(head *ListNode) *ListNode {
if head == nil || head.Next == nil {
return head
}
newHead := head.Next
head.Next = swapPairs(newHead.Next)
newHead.Next = head
return newHead
}
19.删除链表的倒数第N个节点
题目链接: https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
关键词:单链表
解法一:双指针
让 node1 与 node2 相离 n 个结点 。当 node2 走到最后一结点的 Next 时,node1 到达第倒数 n+1 的位置
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummyNode := &ListNode{
Next: head,
}
node1 := dummyNode
node2 := dummyNode
for node2 != nil && n >= 0 { //使 node2 与 node1 相隔 n 个结点
node2 = node2.Next
n--
}
for node2 != nil { //让 node2 的位置为最后的一个结点的 Next,若判断条件为 node2.Next != nil 则可能会空指针异常(如:只有一个结点但要求删掉倒数第二个结点时)
node1 = node1.Next
node2 = node2.Next
}
node1.Next = node1.Next.Next
return dummyNode.Next
}
解法二:暴力法
计算出链表结点数
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
if n <= 0 {
return head
}
count := 0
temp := head
for temp != nil {
count++
temp = temp.Next
}
dummy := &ListNode{
Next: head,
}
temp = dummy
for i := 0; i < count - n; i++ {
temp = temp.Next
}
temp.Next = temp.Next.Next
return dummy.Next
}
解法三:栈
将原链表入栈,出栈时倒数即是正数
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
nodes := []*ListNode{}
dummy := &ListNode{0, head}
for node := dummy; node != nil; node = node.Next {
nodes = append(nodes, node)
}
prev := nodes[len(nodes)-1-n]
prev.Next = prev.Next.Next
return dummy.Next
}
面试题02.07.链表相交
题目链接: https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/description/
关键词:相交链表
解法一:计算链表长度
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
cur1 := headA
cur2 := headB
count1 := 0
count2 := 0
for cur1 != nil {
cur1 = cur1.Next
count1++
}
for cur2 != nil {
cur2 = cur2.Next
count2++
}
cur1 = headA
cur2 = headB
minLength := 0
if count1 <= count2 {
minLength = count1
for i := 0; i < count2-count1; i++ {
cur2 = cur2.Next
}
} else {
minLength = count2
for i := 0; i < count1-count2; i++ {
cur1 = cur1.Next
}
}
for i := 0; i < minLength; i++ {
if cur1 == cur2 {
return cur1
}
cur1 = cur1.Next
cur2 = cur2.Next
}
return nil
}
解法二:拼接链表
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
if headA == nil || headB == nil {
return nil
}
pa := headA
pb := headB
for pa != pb { //有相交才会跳出循环
if pa == nil { //拼接headB
pa = headB
}else {
pa = pa.Next
}
if pb == nil { //拼接headA,让拼接后的两个链表长度相同,且若有相交部分,则两个链表会走到同一地址
pb = headA
}else {
pb = pb.Next
}
}
return pa
}
解法三:哈希表
建立 *ListNode : bool 的键值对,涉及重复结点的题可以试试这个方法
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func getIntersectionNode(headA, headB *ListNode) *ListNode {
vis := map[*ListNode]bool{} //键值对 *ListNode : bool
for tmp := headA; tmp != nil; tmp = tmp.Next {
vis[tmp] = true
}
for tmp := headB; tmp != nil; tmp = tmp.Next {
if vis[tmp] {
return tmp
}
}
return nil
}
142.环形链表 II
题目链接: https://leetcode.cn/problems/linked-list-cycle-ii/description/
关键词:环形链表
解法一:哈希表
与面试题02.07.链表相交思路一致
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
vis := map[*ListNode]bool{}
for cur := head; cur != nil; cur = cur.Next {
if vis[cur] {
return cur
}
vis[cur] = true
}
return nil
}
解法二:快慢指针
fast 指针每次走两步,slow 指针每次走一步,n为快指针走的圈数
任意时刻,fast 指针走过的距离都为 slow 指针的 2 倍。因此:a+n*(b+c) = 2*(a+b) ⟹ a=c+(n−1)(b+c)
因此,当 slow 与 fast 相遇时,再使用一个指针 p := head,此时,让 p 与 slow 一起移动,相遇时即使环的入口
- 有环则快慢指针一定相遇
- 快指针停下时刚好碰上慢指针
- 快指针停下时越过慢指针一步,然后轮到慢指针走一步,此时相遇
- 慢指针进入环后走完第一圈前一定会和快指针相遇
最坏的情况,慢指针刚在环的入口时,快指针在入口后的一步。此时慢指针走半圈时,快指针走了一圈。还剩半圈,快指针一定能追上慢指针。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func detectCycle(head *ListNode) *ListNode {
slow := head
fast := head
for fast != nil {
if fast.Next == nil {
return nil
}
fast = fast.Next.Next
slow = slow.Next
if fast == slow {
p := head
for p != slow {
p = p.Next
slow = slow.Next
}
return p
}
}
return nil
}