【CodeTop】TOP 100 刷题 21-30

文章目录

  • 21. 螺旋矩阵
    • 题目描述
    • 代码与解题思路
  • 22. 反转链表 II
    • 题目描述
    • 代码与解题思路
  • 23. 相交链表
    • 题目描述
    • 代码与解题思路
  • 24. 合并 K 个升序链表
    • 题目描述
    • 代码与解题思路
  • 25. 字符串相加
    • 题目描述
    • 代码与解题思路
  • 26. 最长递增子序列
    • 题目描述
    • 代码与解题思路
  • 27. 重排链表
    • 题目描述
    • 代码与解题思路
  • 28. 环形链表 II
    • 题目描述
    • 代码与解题思路
  • 29. 接雨水
    • 题目描述
    • 代码与解题思路
  • 30. 删除链表的倒数第 N 个结点
    • 题目描述
    • 代码与解题思路

21. 螺旋矩阵

题目链接:54. 螺旋矩阵

题目描述

【CodeTop】TOP 100 刷题 21-30_第1张图片

代码与解题思路

func spiralOrder(matrix [][]int) (res []int) {
    if len(matrix) == 0 {
        return res
    }
    top, bottom, left, right := 0, len(matrix)-1, 0, len(matrix[0])-1
    for top < bottom && left < right {
        for i := left; i < right; i++ { res = append(res, matrix[top][i]) }
        for i := top; i < bottom; i++ { res = append(res, matrix[i][right]) }
        for i := right; i > left; i-- { res = append(res, matrix[bottom][i]) }
        for i := bottom; i > top; i-- { res = append(res, matrix[i][left]) }
        left++
        top++
        right--
        bottom--
    }
    if top == bottom { // 剩余一行
        for i := left; i <= right; i++ { res = append(res, matrix[top][i]) }
    } else if left == right { // 剩余一列
        for i := top; i <= bottom; i++ { res = append(res, matrix[i][right]) }
    }
    return res
}

这个方法是最好理解且最好记忆的,直接根据题意来模拟这样一个过程,一圈一圈的遍历来实现,最后再处理一下剩余的行/列即可。

22. 反转链表 II

题目链接:92. 反转链表 II

题目描述

【CodeTop】TOP 100 刷题 21-30_第2张图片

代码与解题思路

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseBetween(head *ListNode, left int, right int) *ListNode {
    phead := &ListNode{Next: head}
    prev := phead
    for i := 0; i < left-1; i++ { // 找到起始位置
        prev = prev.Next
    }
    cur := prev.Next
    for i := 0; i < right-left; i++ { // 反转链表
        tmp := cur.Next
        cur.Next = tmp.Next
        tmp.Next = prev.Next
        prev.Next = tmp
    }
    return phead.Next
}

核心就在链表反转的逻辑,如果忘记了就画图再复习一遍。

23. 相交链表

题目链接:160. 相交链表

题目描述

【CodeTop】TOP 100 刷题 21-30_第3张图片

代码与解题思路

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    curA, curB := headA, headB
    for curA != curB {
        if curA == nil {
            curA = headB
        } else {
            curA = curA.Next
        }
        if curB == nil {
            curB = headA
        } else {
            curB = curB.Next
        }
    }
    return curA
}

题解区很多用三目运算符的,没必要,if else 就很清晰了,两个指针每次遍历各走一步,如果为空就从对方的链表从头开始,最后相等只有两种情况:

  1. 他们在相交的地方相遇了
  2. 他们都走到各自的 nil 了

24. 合并 K 个升序链表

题目链接:23. 合并 K 个升序链表

题目描述

【CodeTop】TOP 100 刷题 21-30_第4张图片

代码与解题思路

func mergeKLists(lists []*ListNode) *ListNode {
    n := len(lists)
    if n == 0 {
        return nil
    }
    if n == 1 {
        return lists[0]
    }
    left := mergeKLists(lists[:n/2])
    right := mergeKLists(lists[n/2:])
    return mergeTwoLists(left, right)
}

func mergeTwoLists(list1, list2 *ListNode) *ListNode {
    phead := &ListNode{}
    cur := phead
    for list1 != nil && list2 != nil {
        if list1.Val < list2.Val {
            cur.Next = list1
            list1 = list1.Next
        } else {
            cur.Next = list2
            list2 = list2.Next
        }
        cur = cur.Next
    }
    if list1 != nil {
        cur.Next = list1
    } else {
        cur.Next = list2
    }
    return phead.Next
}

这道题我曾经的做法是暴力全部遍历一遍,塞进数组中,然后用库函数排序,然后再将数组转化成链表的形式,采用的是暴力匹配的方式,之后也可以和面试官聊聊这个思路

而现在新学习到的解法是使用分治的解法,通过递归分治,将链表数组对半拆分,直到拆分成两个子数组后调用合并两个链表的方法(这个方法实现起来也不难)将每一个子链表合并成升序链表,最后组合成一个大的升序链表,非常巧妙的分治算法

多学,多用,多思考。

25. 字符串相加

题目链接:415. 字符串相加

题目描述

【CodeTop】TOP 100 刷题 21-30_第5张图片

代码与解题思路

func addStrings(num1 string, num2 string) (ans string) {
    len1, len2 := len(num1)-1, len(num2)-1
    carry := 0
    for len1 >= 0 || len2 >= 0 {
        x, y := 0, 0
        if len1 >= 0 {
            x = int(num1[len1] - byte('0'))
        }
        if len2 >= 0 {
            y = int(num2[len2] - byte('0'))
        }
        sum := x+y+carry
        ans += strconv.Itoa(sum%10)
        carry = sum/10
        len1--
        len2--
    }
    if carry != 0 {
        ans += "1"
    }
    return reverse(ans)
}

func reverse(ans string) string {
    tmp := []byte(ans)
    l, r := 0, len(tmp)-1
    for l < r {
        tmp[l], tmp[r] = tmp[r], tmp[l]
        l++
        r--
    }
    return string(tmp)
}

这道题不难,但是有很多需要注意的地方

  1. 字符串相加这类题型的思路,从后往前遍历,累加成一个字符串,最后反转
  2. reverse 函数的编写需要熟练,用 go 的小缺陷,有些方法需要手撕
  3. strconv.Itoa(int),这个 int 转 string 的方法必须得记住,不然这道题铁定做不出来,go 只能借助调用来 int 转 string

26. 最长递增子序列

题目链接:300. 最长递增子序列

题目描述

【CodeTop】TOP 100 刷题 21-30_第6张图片

代码与解题思路

func lengthOfLIS(nums []int) int {
    ans := 1
    dp := make([]int, len(nums))
    for i, _ := range nums { dp[i]=1 }
    for i := 1; i < len(nums); i++ {
        for j := 0; j < i; j++ {
            if nums[i] > nums[j] { // 只要遇到, 对应的 dp 数组位置的值就+1
                dp[i] = max(dp[j]+1, dp[i])
            }
        }
        ans = max(ans, dp[i])
    }
    return ans
}

能写出动态规划的方法就行了,如果忘了就去看题解区的图解,二分法就不要想了,能做出来就不错了。。。

27. 重排链表

题目链接:143. 重排链表

题目描述

【CodeTop】TOP 100 刷题 21-30_第7张图片

代码与解题思路

第一种方法,用数组存节点,然后通过数组的随机访问性质来重排

func reorderList(head *ListNode)  {
    tmp := []*ListNode{}
    cur := head
    for cur != nil {
        tmp = append(tmp, cur)
        cur = cur.Next
    }
    i, j := 0, len(tmp)-1
    for i < j {
        tmp[i].Next = tmp[j]
        i++
        if i == j {
            break
        }
        tmp[j].Next = tmp[i]
        j--
    }
    tmp[i].Next = nil
}

第二种方法:

  1. 取中点(快慢指针,获得了中点的指针)
  2. 反转右半部分链表(这样就能 O(N) 的进行从后往前的遍历了)
  3. 再通过起点指针和中点指针的配合切换节点位置
func reorderList(head *ListNode)  {
    // 取中点
    slow, fast := head, head
    for fast != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next.Next
    }
    // 反转右半链表
    var pre *ListNode = nil
    var cur = slow
    for cur != nil {
        nxt := cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }
    // 完成链表重排
    left, right := head, pre
    for right.Next != nil {
        tmpl := left.Next
        tmpr := right.Next

        left.Next = right
        right.Next = tmpl
        
        left = tmpl
        right = tmpr
    }
}

到时候如果面试遇到这道题目,可以把第一种思路讲出来,然后再讲第二种思路,最后实现第二种思路,最需要注意的是链表的反转,我突然链表反转老是写不出来,好难受,之后多刷几遍链表反转怎么写,一定要把链表的反转给练熟了

28. 环形链表 II

题目链接:142. 环形链表 II

题目描述

【CodeTop】TOP 100 刷题 21-30_第8张图片

代码与解题思路

func detectCycle(head *ListNode) *ListNode {
    slow, fast := head, head
    for {
        if fast == nil || fast.Next == nil { // 走到尾了,证明没环
            return nil
        }
        slow = slow.Next
        fast = fast.Next.Next
        if slow == fast { // 相遇了,证明有环
            break
        }
    }
    fast = head // 让 fast 回头重新遍历
    for slow != fast {
        slow = slow.Next
        fast = fast.Next
    }
    return fast
}

这道题很操蛋,是一个数学问题,是需要证明的,我是证明不出来这东西,如果真在面试遇到了,我也就只能背代码了。。没办法,平等的对每一个数学问题白痴。。。

29. 接雨水

题目链接:42. 接雨水

题目描述

【CodeTop】TOP 100 刷题 21-30_第9张图片

代码与解题思路

func trap(height []int) (ans int) {
    st := []int{}
    for i, v := range height {
        for len(st) > 0 && height[st[len(st)-1]] < v {
            cur := st[len(st)-1]
            st = st[:len(st)-1]
            if len(st) == 0 {
                break
            }
            l := st[len(st)-1]
            r := i
            h := min(height[l], height[r])-height[cur] // 用矮的柱子去减最低位的值
            ans += (r-l-1)*h
        }
        st = append(st, i)
    }
    return ans
}

经典单调栈题目,多刷几遍就熟练了,非常经典的单调栈思路,栈中的值是不断递减的,如果遇到比栈顶大的值,就证明柱子把这片区域围上了。

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

题目链接:19. 删除链表的倒数第 N 个结点

题目描述

【CodeTop】TOP 100 刷题 21-30_第10张图片

代码与解题思路

func removeNthFromEnd(head *ListNode, n int) *ListNode {
    phead := &ListNode{Next: head}
    slow, fast := phead, head
    for n > 0 { // 让 fast 指针先走 n 步
        fast = fast.Next
        n--
    }
    for fast != nil { // 同时遍历,这样 slow 指针就会离链表尾 n 个身位
        slow = slow.Next
        fast = fast.Next
    }
    slow.Next = slow.Next.Next // 删除节点
    return phead.Next
}

非常经典的快慢指针思路,让快指针先走 n 步,快慢指针同时往后走,那慢指针就能停在结尾倒数第 n 个节点上,然后执行删除操作即可。

你可能感兴趣的:(CodeTop,刷题,leetcode,算法)