一文搞定链表算法面试

269 阅读5分钟

leetcode 21 合并两个有序链表

题解分析: 分别遍历2个有序链表,创建一个新的链表,遍历的时候进行比较,谁小就加入新的链表,这个题目主要就是需要你自己加一个虚拟头节点,也就是代码中的res,这样最后输出就会简单很多

func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    head := &ListNode{}
    res := &ListNode{} 
    res = head    
    for l1 != nil && l2 != nil {
        if l1.Val > l2.Val {
            head.Next = l2
            l2 = l2.Next
        }else {
            head.Next = l1
            l1 = l1.Next
        }
        head = head.Next   //这里不要忘记了
    }
    if l1 == nil {
        head.Next = l2
    }
    if l2 == nil {
        head.Next = l1
    }
    return res.Next


}

23. 合并K个升序链表

题解分析:这里我们用到了归并的一种思想(可以先去看看归并排序,这样理解的更清楚)。

我们可以先将整个数组merge来进行划分,分完后再进行合并,这个时候我们可以发现合并不就是和上面的题目的要求是一样的吗?这个时候我们直接引用上面的mergeTwoLists来进行合并。(先把归并排序搞懂就直接明白了)

需要注意的是写递归的时候一定一定要注意递归终止的条件。

func mergeKLists(lists []*ListNode) *ListNode {
    if lists == nil || len(lists) == 0 {
        return nil 
    }
    return merge(lists,0,len(lists)-1)

      

}
func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    head := &ListNode{}
    res := &ListNode{} 
    res = head    
    for l1 != nil && l2 != nil {
        if l1.Val > l2.Val {
            head.Next = l2
            l2 = l2.Next
        }else {
            head.Next = l1
            l1 = l1.Next
        }
        head = head.Next   //这里不要忘记了
    }
    if l1 == nil {
        head.Next = l2
    }
    if l2 == nil {
        head.Next = l1
    }
    return res.Next


}

//将数组进行划分
func merge(l []*ListNode,left int,right int) *ListNode {
    if left == right {  //这里很容易漏
        return l[left]
    }
    if left > right{
        return nil 
    }
    mid := (left + right) / 2
    leftNode:=merge(l,left,mid)
    rightNode:=merge(l,mid+1,right)
   return mergeTwoLists(leftNode,rightNode)
}

141. 环形链表

题解分析:就像一个操场,如果有环的话,一个人跑的快,一个慢,那么肯定会相遇,我们设置2个不同速度的节点,就可以知道有没有环。

func hasCycle(head *ListNode) bool {
	slow, fast := head, head
	for fast != nil && fast.Next !=nil{ // 快指针指向真实节点
		slow, fast = slow.Next, fast.Next.Next // 慢的走一步 快的走两步
		if slow == fast {                      // 快慢指针相遇,有环
			return true
		}
	}
	return false // fast走出去了,没有环
}

142. 环形链表 II

解题思路:

截屏2021-10-02 下午7.45.38.png

在相遇的时候我让慢指针从相遇点走,再让head从开始走,那么当他们相遇的时候,就到了环的起始的位置。 (图片是从leetcode里面截取的)

func detectCycle(head *ListNode) *ListNode {
	if head == nil || head.Next == nil {
		return nil
	}
	slow, fast := head, head

	for fast != nil && fast.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
        if slow == fast {
            break
        }
	}
    if fast == nil || fast.Next == nil {
        return nil
    }

	tmp := head
	for tmp != slow {
		tmp = tmp.Next
		slow = slow.Next
	}
	return tmp
}

876. 链表的中间结点

题解分析:这道题还是只使用快慢指针,比较简单,但是需要注意的是当链表只有一个元素的时候,返回的是这个元素。

func middleNode(head *ListNode) *ListNode {
    if head == nil{   //注意这里就好
        return nil
    }
    
    fast := head
    slow := head
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
    }
    return slow

}

160. 相交链表

题解思路: 开两个指针分别遍历这两个链表,在第一次遍历到尾部的时候,指向另一个链表头部继续遍历,这样会抵消长度差。

如果链表有相交,那么会在中途相等,返回相交节点;

如果链表不相交,那么最后会 nil == nil,返回 nil;

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    a, b := headA, headB
    for a != b {
        if a == nil {
            a = headB
        } else {
            a = a.Next
        }

        if b == nil {
            b = headA
        } else {
            b = b.Next
        }
    }
    return a
}

19. 删除链表的倒数第 N 个结点

解题思路: 双指针,一个在前,一个在后,前面的先走N步,然后两个指针一起往后走每次一步,直到 first 走成nil也就是到尾巴了。

那么要删除的就是 slow.Next 所以我们只要让 slow.Next = slow.Next.Next 就行了

func removeNthFromEnd(head *ListNode, n int) *ListNode {
     newHead := &ListNode{
        Next: head,
    }
    slow,fast := newHead,newHead
    for i:=0;i<n;i++{
        fast = fast.Next
    }
    for fast.Next!=nil{
        fast = fast.Next
        slow = slow.Next
    }
    slow.Next = slow.Next.Next
    return newHead.Next 

}

剑指 Offer 24. 反转链表

题解思路: 这道题递归建议去leetcode看看有图示的解法,迭代解法只需要我们

递归解法:只需要我们拿出一张纸,自己话一遍就OK了

func reverseList(head *ListNode) *ListNode { 
     if head == nil  || head.Next == nil  {
        return head
    }
    last := reverseList(head.Next) 
    head.Next.Next = head
    head.Next = nil
    return last
}

迭代解法:

func reverseList(head *ListNode) *ListNode {
    var prev *ListNode //最前节点
    curr := head    
    for curr != nil {
        next := curr.Next
        curr.Next = prev
        prev = curr
        curr = next
    }
    return prev
}

92. 反转链表 II

题解分析: 这道题比较困难,可以这样理解,当左边为1的时候,解法是什么样的?然后再使用递归来进行题解。

func reverseBetween(head *ListNode, left int, right int) *ListNode {
    if left == 1 {
        return reverse(head,right)
    }
    head.Next = reverseBetween(head.Next,left-1,right-1)
    return head


}
//这不就是反转从开始到指定位置的函数吗?
var success *ListNode
func reverse(head *ListNode,n int) *ListNode {
   
    if n == 1 {
        success = head.Next
        return head
    }
    last := reverse(head.Next,n-1)
    head.Next.Next = head
    head.Next = success
    return last
}

25. K 个一组翻转链表

func reverseKGroup(head *ListNode, k int) *ListNode {
    if head == nil {
        return nil
    }
    a := head
    b := head
    for i := 0; i < k; i ++ {
        if b == nil {
            return head
        }
        b = b.Next
    }
    newHead := reverse(a,b)
    a.Next = reverseKGroup(b,k)
    return newHead

}
//反转从 head 到right 的链表
func reverse (head *ListNode,right *ListNode) *ListNode {
    pre := &ListNode{}
    cur := head
    nxt := head
    for cur != right {
        nxt = cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }
    return pre 
}

234. 回文链表

解题思路:

我们使用快慢指针,找到链表的中间节点,然后将后半部分进行反转链表,之后再进行比较。

func isPalindrome(head *ListNode) bool {
    if head == nil {
        return false
    }
    fast := head
    slow := head
    //找到中间节点
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
    }
    slow = reserve(slow)
    for slow != nil  {
        if head.Val != slow.Val {
            return false
        }
        head = head.Next
        slow = slow.Next
        
    }
    return true

}
//递归反转链表
func reserve(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    last := reserve(head.Next)
    head.Next.Next = head
    head.Next = nil
    return last

}

203. 移除链表元素

题解思路: 这道题不难,主要是为了联系递归。

func removeElements(head *ListNode, val int) *ListNode {
    if head == nil  {
        return head
    }
    head.Next = removeElements(head.Next,val)
    if(head.Val == val){
        return head.Next;
    } else {
        return head;
    }
}