[Golang修仙之路] 算法专题:一句话记忆hot100

39 阅读16分钟

1. 编辑距离

// dp数组含义: 
// 下标从1开始有意义. dp[0][i] 代表 word1 取前0个字符, word2取前i个字符, 需要的最⼩编辑步骤.
// 遍历顺序:
// 两层循环, word1 的每个字符, word2的每个字符.
// 转移⽅程:
// 1. 如果当前两个字符相等, 那么不需要额外编辑, 当前状态可以由dp[i-1][j-1]得到.
// 2. 如果当前两个字符不等, 那么肯定要编辑(在原状态上+1), 但是原状态我也要找⼀个最⼩的: 插
// ⼊(dp[i-1][j]), 删除(dp[i][j-1], 替换(dp[i-1][j-1])
func minDistance(word1 string, word2 string) int {
    len1, len2 := len(word1), len(word2)
    dp := make([][]int, len1 + 1)
    for i := range dp {
        dp[i] = make([]int, len2 + 1)
    }
    for i := 0; i <= len1; i++ {
        dp[i][0] = i
    }
    for i := 0; i <= len2; i++ {
        dp[0][i] = i
    }
    for i := 0; i < len1; i++ {
        for j := 0; j < len2; j++ {
            if word1[i] == word2[j] {
                dp[i+1][j+1] = dp[i][j]
            } else {
                dp[i+1][j+1] = min(dp[i][j], min(dp[i][j+1], dp[i+1][j])) + 1
            }
        }
    }
    return dp[len1][len2]
}

2. 合并区间

func merge(intervals [][]int) (ans [][]int) {
    // 1. 根据起点排序
    slices.SortFunc(intervals, func(p, q []int) int {
        return p[0] - q[0]
    })
    // 2. 遍历排序后区间
    for _, interval := range intervals {
        ansLen := len(ans)
        // 3.如果当前区间与答案末尾区间有重叠,则更新答案末尾区间终点
        if ansLen > 0 && interval[0] <= ans[ansLen-1][1] {
            ans[ansLen-1][1] = max(interval[1], ans[ansLen-1][1])
        } else { 
            // 否则向答案中添加一个新的区间,
            // 后续区间一定不会对更早的区间产生影响了,因为已经按照起点排过序了
            ans = append(ans, interval)
        }
    }
    return ans
}

3. 滑动窗口最大值

// 一句话:单调队列
func maxSlidingWindow(nums []int, k int) []int {
    // 0. 创建答案数组,长度为4的数组,k=3,那么大小为3的区间有2个。
    ans := make([]int, len(nums) - k + 1)
    
    // 1. 单调队列,队列中存下标。
    q := []int{}

    for i, num := range nums {
        
        // 2. 从队尾到队头递增。如果当前元素大于队尾,队尾就弹出。
        for len(q) > 0 && num > nums[q[len(q)-1]] {
            q = q[:len(q)-1]
        }

        // 3. 当前元素入队。
        q = append(q, i)

        // 4. 窗口长度超长了,弹出队头
        if i - q[0] > k - 1 {
            q = q[1:]
        }

        // 5. 窗口右端点大于等于k-1,就要开始填写答案
        if i >= k - 1 {
            ans[i-k+1] = nums[q[0]]
        }
    }
    
    return ans
}

4. 手撕快速排序

// 一句话:先右后左带等于,闭区间递归
func proc(a []int, l, r int) {
    if l >= r {
        return
    }
    pivot := a[l]
    // 细节1, left=l 而不是l+1
    left, right := l, r
    for left < right {
        for left < right && a[right] >= pivot {
            right--
        }
        // 细节2: 因为left=l,所以为了让left指针能「起步」,比较的时候要带上等于
        for left < right && a[left] <= pivot {
            left++
        }
        a[left], a[right] = a[right], a[left]
        // fmt.Println(a)
    }
    a[l], a[right] = a[right], a[l]
    proc(a, l, left-1)
    proc(a, left+1, r)
}

5. 岛屿数量

// 一句话:一岛一深搜,陆地变海洋
// 每个岛屿分别dfs,把陆地改成海洋。
func numIslands(grid [][]byte) int {
    m, n := len(grid), len(grid[0])
    var dfs func(i, j int)
    dfs = func(i, j int) {
        if i < 0 || i >= m {
            return
        }
        if j < 0 || j >= n {
            return
        }
        if grid[i][j] == '0' {
            return
        }
        grid[i][j] = '0'
        dir := [][]int{{0,1},{1,0},{-1,0},{0,-1}}
        for idx := 0; idx < 4; idx++ {
            dfs(i + dir[idx][0], j + dir[idx][1])
        }
    }
    cnt := 0
    for i := 0; i < m; i++ {
        for j := 0; j < n; j++ {
            if grid[i][j] == '1' {
                cnt++
                dfs(i, j)
            }
        }
    }
    return cnt
}

6. 三数之和

// 一句话:二三分左右,去重比前者
func threeSum(nums []int) (ans [][]int) {
    sort.Ints(nums)
    n := len(nums)
    for i := 0; i <= n - 3; i++ {
        n1 := nums[i]
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        l, r := i + 1, n - 1
        for l < r {
            n2, n3 := nums[l], nums[r]
            if n1 + n2 + n3 > 0 {
                r--
            } else if n1 + n2 + n3 < 0 {
                l++
            } else {
                ans = append(ans, []int{n1, n2, n3})
                for l < r && nums[l] == n2 {
                    l++
                }
                for l < r && nums[r] == n3 {
                    r--
                }
            }
        }
    }
    return
}

7. 组合总和2

// 一句话:组合排序标起点,去重比前者
func combinationSum2(candidates []int, target int) (ans [][]int) {
    // 排序:应对元素有重复的情况
    sort.Ints(candidates)
    path := []int{}

    // 组合:dfs的for循环i从start开始,而不是0开始(排列)。
    var dfs func(a []int, start int, sum int)
    dfs = func(a []int, start int, sum int) {
        if sum > target {
            return
        }
        if sum == target {
            ans = append(ans, append([]int(nil), path...))
            return
        }
        for i := start; i < len(a); i++ {
            // 与前一个相同则continue:应对元素有重复的情况
            if i > start && a[i] == a[i-1] {
                continue
            }
            path = append(path, a[i])
            dfs(a, i + 1, sum + a[i])
            path = path[:len(path)-1]
        }
    }
    dfs(candidates, 0, 0)
    return
}

8. TOP-K

// 一句话:从底构建堆,弹出k-1次
func findKthLargest(nums []int, k int) int {
    n := len(nums)
    // 自底向上构建堆
    for i := n - 1; i >= 0; i-- {
        sink(nums, i, n)
    }

    // 弹出k-1次,因为第1大的其实就是堆顶,不需要弹出
    for i := 0; i < k - 1; i++ {
        nums[0], nums[n-1] = nums[n-1], nums[0]
        n--
        sink(nums, 0, n)
    }

    // 剩下的堆顶就是答案
    return nums[0]
}


// 标准的sink过程
func sink(heap []int, i int, heapSize int) {
    for {
        l, r := i * 2 + 1, i * 2 + 2
        if l >= heapSize {
            break
        }
        larger := l
        if r < heapSize && heap[r] > heap[l] {
            larger = r
        }
        if heap[larger] > heap[i] {
            heap[larger], heap[i] = heap[i], heap[larger]
            i = larger
        } else {
            break
        }
    }
}

9. LRU

// 一句话:小写为包大写类,链表 map 和 容量,链表节点是 entry
type LRUCache struct {
    l *list.List
    cache map[int]*list.Element
    cap int
}

type entry struct {
    key, value int
}

func Constructor(capacity int) LRUCache {
    return LRUCache{
        l : list.New(),
        cache: make(map[int]*list.Element),
        cap: capacity,
    }
}

func (this *LRUCache) Get(key int) int {
    node, ok := this.cache[key]
    if ok {
        this.l.MoveToBack(node)
        return node.Value.(*entry).value
    }
    return -1
}

func (this *LRUCache) Put(key int, value int)  {
    node, ok := this.cache[key]
    if ok {
        this.l.MoveToBack(node)
        node.Value.(*entry).value = value
        this.cache[key] = node
        return
    }

    newNode := this.l.PushBack(&entry{key, value})
    this.cache[key] = newNode
    if this.l.Len() > this.cap {
        tmp := this.l.Front()
        this.l.Remove(tmp)
        delete(this.cache, tmp.Value.(*entry).key)
    }
}

10. k个一组反转链表

// 一句话:取得总长度,算出反转数,保存虚假头
func reverseKGroup(head *ListNode, k int) *ListNode {
    dummy := &ListNode{
        Next: head,
    }
    var t, pre, cur *ListNode
    t, pre, cur = dummy, nil, head
    p := head
    length := 0
    for p != nil {
        length++
        p = p.Next
    }
    for i := 0; i < length / k; i++ {
        for i := 0; i < k; i++ {
            nxt := cur.Next
            cur.Next = pre
            pre = cur
            cur = nxt
        }
        tmp := t.Next
        t.Next.Next = cur
        t.Next = pre
        t = tmp
    }
    return dummy.Next
}

11. 最长递增子序列

// 一句话:dp[i]表示以i结尾的最长递增序列长度,要向前遍历
// dp[i] 表示:以nums[i]结尾的最长递增子序列长度
// 递推公式:[0,i-1] 中,找结尾小于nums[i]的最长的子序列,dp[i]在此基础上+1
func lengthOfLIS(nums []int) int {
    n := len(nums)
    dp := make([]int, n)
    ans := 0
    for i := 0; i < n; i++ {
        maxLen := 0
        for j := i - 1; j >= 0; j-- {
            if nums[i] > nums[j] {
                maxLen = max(maxLen, dp[j])
            }
        }
        dp[i] = maxLen + 1
        ans = max(ans, dp[i])
    }
    return ans
}

12. 最长回文子串

// 一句话:初始化长度为1和2的自串,dp[i][j]表示[i,j]最长回文子串的长度
func longestPalindrome(s string) string {
    n := len(s)
    dp := make([][]int, n)
    for i := range dp {
        dp[i] = make([]int, n)
    }
    ans := s[:1]
    for i := 0; i < n; i++ {
        dp[i][i] = 1
        if i+1 < n && s[i] == s[i+1] {
            dp[i][i+1] = 2
            ans = s[i:i+2]
        }
    }
    for step := 2; step < n; step++ {
        for i := 0; i+step < n; i++ {
            j := i + step
            
            // 子问题是否也是回文,靠长度来判断
            if s[i] == s[j] && dp[i+1][j-1] == j - 1 - i {
                dp[i][j] = dp[i+1][j-1] + 2
                ans = s[i:j+1]
            } else {
                dp[i][j] = 0
            }
        }
    }
    return ans
}

13. 相交链表(找交点)

// 一句话:从我家到终点,再从你家出发
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    a, b := headA, headB
    flagA, flagB := true, true
    for {
        // 首次为nil,换头
        if a == nil && flagA {
            a = headB
            flagA = false
        }
        if b == nil && flagB {
            b = headA
            flagB = false
        }
        // 二次为nil,不相交
        if a == nil || b == nil {
            return nil
        }
        // 相遇,找到交点
        if a == b {
            break
        }
        a = a.Next
        b = b.Next
    }
    return a
}

14. 环形链表(找交点)

// 一句话:快慢指针,相遇一次快的回起点,一起走
func detectCycle(head *ListNode) *ListNode {
    f, s := head, head
    if f == nil || f.Next == nil {
        return nil
    }
    for f != nil && f.Next != nil {
        f = f.Next.Next
        s = s.Next
        if s == f {
            break
        }
    }
    if f == nil || f.Next == nil {
        return nil
    }
    f = head
    for f != s {
        f = f.Next
        s = s.Next
    }
    return f
}

15. 链表倒数第k个元素

// 一句话:前后指针,从假脑袋开始,拉开k的差距,一起走
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    d := &ListNode{Next: head}
    l, r := d, d
    for i := 0; i < n; i++ {
        r = r.Next
    }
    for r.Next != nil {
        r = r.Next
        l = l.Next
    }
    l.Next = l.Next.Next
    return d.Next
}

16. 二叉树直径

// 一句话:nil是-1根是0,返回最长链,更新直径
func diameterOfBinaryTree(root *TreeNode) (ans int) {
    // dfs更新的是「直径」
    // dfs返回的是「最长链」
    var dfs func(root *TreeNode) int
    dfs = func(root *TreeNode) int {
        if root == nil {
            return -1
        }
        if root.Left == nil && root.Right == nil {
            return 0
        }
        leftLen := dfs(root.Left)
        rightLen := dfs(root.Right)
        ans = max(ans, leftLen + rightLen + 2)
        return max(leftLen, rightLen) + 1
    }
    _ = dfs(root)
    return
}

17. 二叉树右视图

// 一句话:层序遍历,取右边,外层队列不空,内层队列长度快照
func rightSideView(root *TreeNode) []int {
    if root == nil {
        return []int{}
    }
    help := make([][]int, 0)
    q := []*TreeNode{root}
    for len(q) > 0 {
        // 长度快照
        lenQ := len(q)
        tmp := make([]int, lenQ)
        for i := 0; i < lenQ; i++ {
            curNode := q[0]
            tmp[i] = curNode.Val
            q = q[1:]
            if curNode.Left != nil {
                q = append(q, curNode.Left)
            }
            if curNode.Right != nil {
                q = append(q, curNode.Right)
            }
        }
        help = append(help, tmp)
    }
    ans := make([]int, len(help))
    for i := 0; i < len(help); i++ {
        ans[i] = help[i][len(help[i])-1]
    }
    return ans
}

18. 最近公共祖先

// 一句话:分类讨论。当前为p、q返回当前,递归左右子树。
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    if root == p || root == q || root == nil {
        return root
    }
    left := lowestCommonAncestor(root.Left, p, q)
    right := lowestCommonAncestor(root.Right, p, q)
    if left != nil && right != nil {
        return root
    }
    if left != nil {
        return left
    }
    return right
}

19. 买股票的最佳时机1

// 一句话:一次卖出机会,不用dp,记录左边最低价格。
func maxProfit(prices []int) int { 
    lmin := prices[0] 
    ans := 0 
    for _, v := range prices { 
        ans = max(ans, v - lmin) 
        lmin = min(lmin, v) 
    } 
    return ans 
}

20. 买股票的最佳时机2

// 一句话:状态机dp,dp[i][0]表示第i天不持有股票,dp[i][1]表示持有
// dp[i][0] 表示 [0:i]天,不持有股票的最大利润
// dp[i][1] 表示 [0:i]天,持有股票的最大利润
// 不持有 = max(前一天不持有, 前一天持有 + 今天价格)
// 持有 = max(前一天持有, 前一天不持有 - 今天价格)  
func maxProfit(prices []int) int {
    n := len(prices)
    dp := make([][2]int, n)
    dp[0][0] = 0
    dp[0][1] = -prices[0]
    for i := 1; i < n; i++ {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
    }
    return dp[n-1][0]
}

21. 全排列

// 一句话:元素无重,不可复选,used 记录选过的
func permute(nums []int) (res [][]int) {
    n := len(nums)
    used := make([]bool, n)
    track := []int{}
    dfs(nums, track, used, &res)
    return
}

func dfs(nums []int, track []int, used []bool, res *[][]int) {
    if len(track) == len(nums) {
        tmp := make([]int, len(track))
        copy(tmp, track)
        *res = append(*res, tmp)
        return
    }
    for i, _ := range nums {
        if used[i] {
            continue
        }
        track = append(track, nums[i])
        used[i] = true
        dfs(nums, track, used, res)
        track = track[:len(track)-1]
        used[i] = false
    }
}

22. 无重复的最长子串

// 一句话:滑动窗口,map,枚举右端点
func lengthOfLongestSubstring(s string) int {
    w := [128]bool{}
    l := 0
    ans := 0
    for r := 0; r < len(s); r++ {
        ch := s[r]
        for w[ch] {
            w[s[l]] = false
            l++
        }
        w[ch] = true
        ans = max(ans, r - l + 1)
    }
    return ans
}

23. 接雨水

// 一句话:栈顶向右,来大的,弹出栈顶,栈还不空,计算宽高,更新答案
func trap(height []int) int {
    st := []int{}
    n := len(height)
    ans := 0
    for i := 0; i < n; i++ {
        cur := height[i]
        // 新来的高,则弹出栈顶
        for len(st) > 0 && cur > height[st[len(st)-1]] {
            bottomH := height[st[len(st)-1]]
            st = st[:len(st)-1]
            // 栈顶下一个存在,则计算水池高,更新答案
            if len(st) > 0 {
                left := st[len(st)-1]
                tall := min(cur, height[left]) - bottomH
                width := i - left - 1
                ans += tall * width
            }
        }
        st = append(st, i)
    }
    return ans
}

24. 手撕归并排序

// 一句话:标准merge + 闭区间分治
func sortArray(nums []int) []int {
    return proc(nums, 0, len(nums)-1)
}

func proc(nums []int, l, r int) []int {
    if l == r {
        return nums[l:l+1]
    }
    mid := (l + r) / 2
    left := proc(nums, l, mid)
    right := proc(nums, mid+1, r)
    return merge(left, right)

}

func merge(a, b []int) []int {
    lenA, lenB := len(a), len(b)
    c := make([]int, lenA + lenB)
    i, j, k := 0, 0, 0
    for i < lenA && j < lenB {
        if a[i] < b[j] {
            c[k] = a[i]
            i++
        } else {
            c[k] = b[j]
            j++
        }
        k++
    }
    for i < lenA {
        c[k] = a[i]
        i++
        k++
    }
    for j < lenB {
        c[k] = b[j]
        j++
        k++
    }
    return c
}

25. 合并k个升序链表

// 一句话:归并 + 闭区间分治
func mergeKLists(lists []*ListNode) *ListNode {
    if len(lists) == 0 {
        return nil
    }
    return proc(lists, 0, len(lists)-1)
}

// 闭区间[l, r]
func proc(lists []*ListNode, l, r int) *ListNode {
    if l == r {
        return lists[l]
    }
    mid := (l + r) / 2
    left, right := proc(lists, l, mid), proc(lists, mid+1, r)
    return merge(left, right)
}

func merge(a, b *ListNode) *ListNode {
    dummy := new(ListNode)
    p := dummy
    for a != nil && b != nil {
        if a.Val < b.Val {
            p.Next = a
            a = a.Next
        } else {
            p.Next = b
            b = b.Next
        }
        p = p.Next
    }
    for a != nil {
        p.Next = a
        a = a.Next
        p = p.Next
    } 
    for b != nil {
        p.Next = b
        b = b.Next
        p = p.Next
    }
    return dummy.Next
}

26. 复原IP

27. x的平方根

// 一句话:二分,闭区间,小于等于更新答案
func mySqrt(x int) int {
    l := 0
    r := x
    ans := -1
    // 闭区间
    for l <= r {
        mid := (l + r) / 2
        // 小于等于,更新答案和左端点
        if mid * mid <= x {
            ans = mid
            l = mid + 1
        } else {
            r = mid - 1
        }
    }
    return ans
}

28. 从前序中序构造二叉树

// 一句话:找到根节点,分治,前序长度=0,返回nil
func buildTree(preorder []int, inorder []int) *TreeNode {
    if len(preorder) == 0 {
        return nil
    }
    idx := slices.Index(inorder, preorder[0])
    leftLen := idx
    root := &TreeNode {Val: preorder[0]}
    root.Left = buildTree(preorder[1:1+leftLen], inorder[:leftLen])
    root.Right = buildTree(preorder[1+leftLen:], inorder[1+leftLen:])
    return root
}

29. 栈实现队列

// 一句话v1:2个栈,出队时如果输出栈为空,则把输入栈清空压入输出栈
// 一句话v2:想象你有一串珠子,2个烧杯
type MyQueue struct {
    in, out []int
}

func Constructor() MyQueue {
    return MyQueue {
        in: make([]int, 0),
        out: make([]int, 0),
    }
}

func (this *MyQueue) Push(x int)  {
    this.in = append(this.in, x)
}

func (this *MyQueue) Pop() int {
    ans := this.Peek()
    this.out = this.out[:len(this.out)-1]
    return ans
}

func (this *MyQueue) Peek() int {
    if len(this.out) == 0 {
        // 细节:一定不要一边修改,一边用len作为循环边界
        // 应该:清空栈之前,对栈的长度做一次快照
        inStkLen := len(this.in)
        for i := 0; i < inStkLen; i++ {
            tmp := this.in[len(this.in)-1]
            this.out = append(this.out, tmp)
            this.in = this.in[:len(this.in)-1]
        }
    }
    ans := this.out[len(this.out)-1]
    return ans
}

func (this *MyQueue) Empty() bool {
    return (len(this.in) == 0) && (len(this.out)==0)
}

30. 队列实现栈

// 一句话:保障后来的元素在队列头部
type MyStack struct {
    q []int
}

func Constructor() MyStack {
    return MyStack {
        q : make([]int, 0),
    }
}

// 每次把队列清空,临时保存到tmp中,把后来的元素插入队列,再把之前的队列插入到该元素后面
// 这样就实现了:后来的元素在队列的头部
func (this *MyStack) Push(x int)  {
    tmp := make([]int, len(this.q))
    copy(tmp, this.q)
    this.q = []int{x}
    lenTmp := len(tmp)
    for i := 0; i < lenTmp; i++ {
        this.q = append(this.q, tmp[0])
        tmp = tmp[1:]
    }
}

func (this *MyStack) Pop() int {
    ans := this.q[0]
    this.q = this.q[1:]
    return ans
}

func (this *MyStack) Top() int {
    return this.q[0]
}

func (this *MyStack) Empty() bool {
    return len(this.q) == 0
}

31. 带缓存的LRU

// 一句话记忆:entry加个ttl,惰性删除过期节点
import (
	"container/list"
	"fmt"
	"time"
)

type entry struct {
	key int
	value int
	expTime time.Time
}

type LRU struct {
	cap int
	cache map[int]*list.Element
	l *list.List
	ttl time.Duration
}

func CreateLRU(capacity int, ttl time.Duration) *LRU {
	return &LRU{
		cap: capacity,
		cache: make(map[int]*list.Element),
		l: list.New(),
		ttl: ttl,
	}
}

func (this *LRU) Put(key int, value int) {
	node, ok := this.cache[key]
	
	// 存在,更新时间和value,更新位置
	if ok {
		this.l.MoveToBack(node)
		node.Value.(*entry).value = value
		node.Value.(*entry).expTime = time.Now().Add(this.ttl)
		this.cache[key] = node
		return
	}

	// 新增,添加到队尾
	newEntry := &entry{ key, value, time.Now().Add(this.ttl) }
	newNode := this.l.PushBack(newEntry)
	this.cache[key] = newNode

	// 超过容量,删除队头
	if this.l.Len() > this.cap {
		frontNode := this.l.Front()
		this.l.Remove(frontNode)
		delete(this.cache, frontNode.Value.(*entry).key)
	}

	return
}


func (this *LRU) Get(key int) int {
	node, ok := this.cache[key]

	// 不存在,返回-1
	if !ok {
		return -1
	}

	// 存在但过期,返回-1
	e := node.Value.(*entry)
	if time.Now().After(e.expTime) {
		this.l.Remove(node)
		delete(this.cache, key)
		return -1
	}

	// 更新位置
	this.l.MoveToBack(node)
	return e.value
}

32. 最长有效括号

func longestValidParentheses(s string) int {
    maxLen := 0

    // 栈中元素是下标
    st := make([]int, 0)

    // 加一个“底”
    st = append(st, -1)
    for i, ch := range s {

        // 左括号入栈
        if ch == '(' {
            st = append(st, i)
        } else {
            // 遇到右括号,先出栈,当前最长有效括号就是“底”到当前右括号的距离
            st = st[:len(st)-1]
            if len(st) > 0 {
                length := i - st[len(st)-1]
                if length > maxLen {
                    maxLen = length
                }
            } else { // 如果栈空了,右括号作为新的“底”
                st = append(st, i)
            }
        }
    }
    return maxLen
}

33. 反转链表2

反转部分[left, right]

func reverseBetween(head *ListNode, left int, right int) *ListNode {
    dummy := &ListNode{Next:head}
    p0 := dummy
    for i := 0; i < left - 1; i++ {
        p0 = p0.Next
    }

    var pre, cur *ListNode = nil, p0.Next // pre 为nil, 而不是p0
    for i := 0; i <= right - left; i++ {
        nxt := cur.Next
        cur.Next = pre
        pre = cur
        cur = nxt
    }

    p0.Next.Next = cur
    p0.Next = pre
    return dummy.Next // 必须返回dummy.Next 而不是 head
}

34. 有效括号

只是判断括号是否匹配,用栈即可。

func isValid(s string) bool {
    stack := make([]byte,0)
    for i := 0; i < len(s); i++ {
        if s[i] == '(' || s[i] == '{' || s[i] == '[' {
            stack = append(stack, s[i])
        } else {
            switch s[i] {
                case ')':
                if len(stack) <= 0 || stack[len(stack)-1] != '(' {
                    return false
                } else {
                    stack = stack[:len(stack)-1]
                }
                case '}':
                if len(stack) <= 0 || stack[len(stack)-1] != '{' {
                    return false
                } else {
                    stack = stack[:len(stack)-1]
                }
                case ']':
                if len(stack) <= 0 || stack[len(stack)-1] != '[' {
                    return false
                } else {
                    stack = stack[:len(stack)-1]
                }
            }
        }
    }
    return len(stack) == 0
}

35. 小于等于N的最大数

// 一句话:
// 参数pass=true,表示最高位没有或者前一位选择了严格小于的数。
// 返回值=false,表示无法找到符合要求的最大数。
// 时间复杂度是O(N*M) N是targetNumber的位数,M是input的长度。
package main

import (
	"fmt"
	"sort"
	"strconv"
)

func main() {
	input := []int{5, 7, 8}
	targetNumber := 6999

	tarNumStr := strconv.Itoa(targetNumber)

	ans := -1
	sort.Ints(input)

	// 递归函数的时间复杂度是O(N*M) N是targetNumber的位数,M是input的长度
	var dfs func(idx int, tmp int, input []int, pass bool) bool
	dfs = func(idx int, tmp int, input []int, pass bool) bool {
		if idx == len(tarNumStr)  {
			ans = tmp
			return true
		}

		// pass=true 对应最高位没有数或者上一位数严格小于给定值
		// 这样就可以直接从当前位开始填入input中最大的
		if pass {
			return dfs(idx+1, tmp*10+input[len(input)-1], input, true)
		} else {
			// 上一位有数,而且是等于
			// 这样就需要找input中小于或等于当前位的数
			digit := int(tarNumStr[idx] - '0')
			for j := len(input) - 1; j >= 0; j-- {
				if input[j] == digit {
					// 细节,如果dfs返回false,会继续寻找下一个数
					if dfs(idx+1, tmp*10+input[j], input, false) {
						return true
					}
				} else if input[j] < digit {
					if dfs(idx+1, tmp*10+input[j], input, true) {
						return true
					}
				}
			}
		}

		// 没有找到小于或等于当前位的数
		// 说明当前位不能填入任何数
		// 但是如果当前位是最高位,那么可以填入0
		if idx != 0 {
			return false
		}
		return dfs(idx+1, tmp, input, true)
	}

	_ = dfs(0, 0, input, false)
	fmt.Println(ans)
}

36. 搜索旋转排序数组

// 一句话:二分闭区间找最小,二分左闭右开搜索两边,右边的话返回记得加上初始偏移量。
func search(nums []int, target int) int {
    minIdx := findMin(nums)
    // fmt.Println(minIdx)
    l := findTar(nums[:minIdx], target)
    if l != -1 {
        return l
    }
    r := findTar(nums[minIdx:], target)
    if r != -1 {
        return r + minIdx
    }
    return -1
}

// 返回目标值的下标,没有返回-1
func findTar(a []int, tar int) int {
    l, r := 0, len(a)
    for l < r {
        mid := (l + r) / 2
        if a[mid] > tar {
            r = mid
        } else if a[mid] < tar {
            l = mid + 1
        } else {
            return mid
        }
    }
    return -1
}

// return 最小值的下标,闭区间
func findMin(a []int) int {
    l, r := 0, len(a) - 1
    for l < r {
        mid := (l + r) / 2
        if a[mid] < a[r] {
            r = mid
        } else {
            l = mid + 1
        }
    }
    return l
}

37. 对称二叉树

// 一句话:一棵树分成2棵树,a左=b右,a右=b左,a根=b根
func isSymmetric(root *TreeNode) bool {
    return dfs(root.Left, root.Right)
    
}

func dfs(a *TreeNode, b *TreeNode) bool {
    if a == nil || b == nil {
        return a == b
    }
    return a.Val == b.Val && dfs(a.Left, b.Right) && dfs(a.Right, b.Left)
}

38. 验证搜索二叉树

// 一句话:限定左右子树范围:左子树为(负无穷,根节点值) 右子树为 (根节点值,正无穷)
func isValidBST(root *TreeNode) bool {
    // 此题思路:前序遍历,参数传入合法区间
    return dfs(root, math.MinInt, math.MaxInt)
}

func dfs (root *TreeNode, lower, upper int) bool {
    if root == nil {
        return true
    }
    x := root.Val
    return x > lower && x < upper && dfs(root.Left, lower, x) && dfs(root.Right, x, upper)
}

39. 翻转二叉树

// 一句话:先交换再递归处理,还是先递归处理,再交换,都行。
func invertTree(root *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    root.Left, root.Right = root.Right, root.Left
    invertTree(root.Left)
    invertTree(root.Right)
    return root
}

40. 二叉树最大路径和

// 一句话:递归返回左右子树中正的最大链路(如果为负,通过返回0来表示不选),枚举每一个节点为拐弯点,更新答案。
func maxPathSum(root *TreeNode) int {
    ans := math.MinInt
    var dfs func(root *TreeNode) int
    dfs = func(root *TreeNode) int {
        if root == nil {
            return 0
        }
        l := dfs(root.Left)
        r := dfs(root.Right)
        ans = max(ans, l + r + root.Val)
        sum := max(l,r) + root.Val
        if sum < 0 {
            sum = 0
        }
        return sum
    }
    dfs(root)
    return ans
}

41. 下一个排列

// 一句话:从右往左找上升,从右往左找第一个大于,交换,翻转右侧
func nextPermutation(nums []int)  {
    // 从后往前找第一个上升
    n := len(nums)
    p := -1
    for i := n - 2; i >= 0; i-- {
        if nums[i] < nums[i+1] {
            p = i
            break
        }
    }

    // 如果全局降序,则直接反转
    if p == -1 {
        slices.Reverse(nums)
        return
    }

    // 从右向左找第一个大于p的
    q := -1
    for i := n - 1; i > p; i-- {
        if nums[i] > nums[p] {
            q = i
            break
        }
    }

    // 交换
    nums[p], nums[q] = nums[q], nums[p]

    // 反转右侧
    slices.Reverse(nums[p+1:])
}