[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 一起移动,相遇时即使环的入口

  • 有环则快慢指针一定相遇
  1. 快指针停下时刚好碰上慢指针
  2. 快指针停下时越过慢指针一步,然后轮到慢指针走一步,此时相遇
  • 慢指针进入环后走完第一圈前一定会和快指针相遇
    最坏的情况,慢指针刚在环的入口时,快指针在入口后的一步。此时慢指针走半圈时,快指针走了一圈。还剩半圈,快指针一定能追上慢指针。
    a, b, c 为图中相应的距离,图来源与LeetCode官方题解
/**
 * 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
}
  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值