Contents

数据结构与算法-刷题-Leetcode

Contents

随机刷

8. 字符串转换整数 (atoi)

8. 字符串转换整数 (atoi)

简单模拟,打败双百

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func myAtoi(s string) int {
    s=strings.TrimSpace(s)
    if s==""{return 0}
    nagative:=false
    if s[0]=='-'||s[0]=='+'{
        if s[0]=='-'{nagative=true}
        s=s[1:]
    }
    ans:=0
    for _,v:=range s{
        if !unicode.IsDigit(v){break}
        ans=ans*10+int(v-'0')
        if ans>math.MaxInt32{break}
    }
    if nagative{ans=-ans}
    if ans>math.MaxInt32{ans=math.MaxInt32}
    if ans<math.MinInt32{ans=math.MinInt32}
    return ans
}

51. N 皇后

51. N 皇后

回溯算法,通过左上对角线元素对应索引lu和右上对角线元素对应索引ru优化冲突检查

/images/数据结构与算法/刷题/N皇后/N皇后.png
N皇后
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func solveNQueens(n int) [][]string {
    colbool:=make([]bool,n)
    leftup:=make([]bool,2*n-1)
    rightup:=make([]bool,2*n-1)
    ans:=make([][]string,0)
    sb:=strings.Builder{}
    
    queue:=make([]int,n)
    var backtrack func(row int)
    backtrack=func(row int){
        //fmt.Println(colbool,leftup,rightup,queue)
        if row==n{
            anstmp:=make([]string,0)
            for _,v:=range queue{
                sb.Reset()
                for i:=0;i<n;i++{
                    if i!=v{
                        sb.WriteByte('.')
                    }else{sb.WriteByte('Q')}
                }
                anstmp=append(anstmp,sb.String())
            }
            ans=append(ans,anstmp)
            return
        }
        for col:=0;col<n;col++{
            lu,ru:=row-col+n-1,row+col
            if !(colbool[col]||leftup[lu]||rightup[ru]){
                colbool[col],leftup[lu],rightup[ru]=true,true,true
                queue[row]=col
                backtrack(row+1)
                colbool[col],leftup[lu],rightup[ru]=false,false,false
            }
        }
    }

    backtrack(0)

    return ans
}

52. N 皇后 II

52. N 皇后 II

逻辑与N皇后一样,但是统计basecase的逻辑改为计数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func totalNQueens(n int) int {
    colbool:=make([]bool,n)
    leftup:=make([]bool,2*n-1)
    rightup:=make([]bool,2*n-1)
    ans:=0

    var backtrack func(row int)
    backtrack=func(row int){
        //fmt.Println(colbool,leftup,rightup)
        if row==n{
            ans++
            return
        }
        for col:=0;col<n;col++{
            lu,ru:=row-col+n-1,row+col
            if !(colbool[col]||leftup[lu]||rightup[ru]){
                colbool[col],leftup[lu],rightup[ru]=true,true,true
                backtrack(row+1)
                colbool[col],leftup[lu],rightup[ru]=false,false,false
            }
        }
    }

    backtrack(0)

    return ans
}

58. 最后一个单词的长度

58. 最后一个单词的长度

简单模拟,从后往前数字母

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func lengthOfLastWord(s string) int {
    s=strings.TrimSpace(s)
    ans:=0
    for i:=len(s)-1;i>=0;i--{
        if unicode.IsLetter(rune(s[i])){
            ans++
        }else{break}
    }
    return ans
}

60. 排列序列

60. 排列序列

数学,从最高位开始往后计算值,对于从右往左第t位每一个值包含的子状态数步长为t!,首先计算每一位的步长sep,然后根据sep从左往右计算t位上的第ans[t]个未使用数字,最后使用onpath数组迭代从左往右求每位的具体值,最后输出值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func getPermutation(n int, k int) string {
    onpath:=make([]bool,n+1)
    ans:=make([]int,n)
    sep:=make([]int,n)
    tmp:=1
    for i,j:=n-1,1;i>=0;{
        tmp*=j
        sep[i]=tmp
        i--
        j++
    }
    //统一结果,-1过后k/v正好就是从左往右第i位取的第ans[i]个未使用数字
    k--
    for i,v:=range sep[1:]{
        ans[i]=k/v
        k-=ans[i]*v
    }
    //fmt.Println("sep",sep)
    sb:=strings.Builder{}
    for _,v:=range ans{
        tmp:=1
        for onpath[tmp]{tmp++}
        for v>0{
            tmp++
            for onpath[tmp]{tmp++}
            v--
        }
        onpath[tmp]=true
        sb.WriteString(strconv.Itoa(tmp))
    }

    //fmt.Println("ans",ans)

    return sb.String()
}

树的直径

树的直径(两次DFS + 树形DP)

两种方法,两遍dfs和树形DP,这里的graph均为邻接表([][]int),深度的定义为从当前根节点到叶子节点的路径的最大值

dfs,O(2N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
visited:=make([]bool,n)

var dfs func(int)int
dfs=func(i int)(int,int){
  visited[i]=true
  //maxLen记录i作为根节点的最大深度,node记录最深的叶子节点
  maxLen,node:=0,i
  for _,j:=range graph[i]{
    if !visited[j]{
      tmp,tmpnode:=dfs(j)+1
      if maxLen<tmp{
        maxLen=tmp
        node=tmpnode
      }
    }
  }
  return maxLen,node
}

_,n1:=dfs(0)
diameter,n2:=dfs(n1)

树形DP(在遍历子树的过程中记录子树的结果并返回给父节点),O(N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
visited:=make([]bool,n)
diameter:=0

var dfs func(int) int
dfs = func(i int) (int) {
  visited[i]=true
  maxLen=0
  for _, j := range graph[i] {
    if !visited[j] {
      tmp := dfs(j)+1
      //max即上面的工具函数,maxLen记录最深子树分支的深度,当前分支的深度,看加起来是否大于当前记录diameter
      diameter = max(diameter, maxLen+tmp)
      //更新maxLen记录
      maxLen = max(maxLen, tmp)
    }
  }
  return
}

27. 移除元素

27. 移除元素

双指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func removeElement(nums []int, val int) int {
    right:=len(nums)-1
    for i,v:=range nums{
        if v==val{
            for right>=0&&nums[right]==val{right--}
            if i>right{break}
            nums[i],nums[right]=nums[right],v
        }
    }
    return right+1
}

41. 缺失的第一个正数

41. 缺失的第一个正数

哈希数组,因为求的是数组中未出现的最小正数,那么对于数组长度为l最小正数的范围只能是[1,l],由于需要空间复杂度为O(1),时间复杂度O(N),我们不能使用map来记录。数组索引到数组元素可以实现一个hash函数,设置hash(i)=i+1i>=0&&i<l,通过这个函数来遍历数组并交换元素使满足条件的元素v=i+1v>0&&v<=l在自己应该处于的位置。然后对不上的元素不在这个范围内,所以第一个对不上的元素的索引值加1就是缺失的第一个正数return i+1

如果全部遍历完都对的上,那么就return l+1

这道题的核心思想就是将数组作为hash函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func firstMissingPositive(nums []int) int {
    l:=len(nums)
    swap:=func(i,j int){nums[i],nums[j]=nums[j],nums[i]}
    for i,_:=range nums{
        for nums[i]>0&&nums[i]<=l&&
        nums[i]!=i+1&&nums[nums[i]-1]!=nums[i]{
            swap(i,nums[i]-1)
        }
    }

    for i,v:=range nums{
        if v!=i+1{return i+1}
    }
    return l+1
}

152. 乘积最大子数组

152. 乘积最大子数组

动态规划,dp[i][0]为以num[i]结尾的最小子串的乘积之和,dp[i][1]为以num[i]结尾的最大子串的乘积之和

状态转移为:

1
2
dp[i][0]=min(dp[i-1][0]* num[i], dp[i-1][1] *num[i], num[i])
dp[i][1]=max(dp[i-1][0]* num[i], dp[i-1][1] *num[i], num[i])
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
func maxProduct(nums []int) int {
    l:=len(nums)
    memo:=make([][]int,l)
    for i,_ := range memo{
        memo[i]=make([]int,2)
        for j,_:=range memo[i]{
            memo[i][j]=math.MaxInt
        }
    }
    memo[0][0]=nums[0]
    memo[0][1]=nums[0]

    ans:=nums[0]
    var dp func(i,j int)int
    dp=func(i,j int)int{
        if memo[i][j]!=math.MaxInt{return memo[i][j]}
        a,b:=dp(i-1,0)*nums[i],dp(i-1,1)*nums[i]
        //fmt.Println(a,b,nums[i],ans)
        ans=max(a,b,nums[i],ans)
        memo[i][0]=min(a,b,nums[i])
        memo[i][1]=max(a,b,nums[i])
        return memo[i][j]
    }

    dp(l-1,1)
    //fmt.Println(memo)
    return ans
}

func max(s ...int)int{
    tmp:=math.MinInt
    for _,v:=range s{
        if tmp<v{tmp=v}
    }
    return tmp
}

func min(s ...int)int{
    tmp:=math.MaxInt
    for _,v:=range s{
        if tmp>v{tmp=v}
    }
    return tmp
}

146. LRU 缓存

146. LRU 缓存

用一个双向链表和一个哈希表来实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
type LRUCache struct {
    h int
    l *list.List
    m map[int]*list.Element
}

type Kv struct{
    k,v int
}


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


func (this *LRUCache) Get(key int) int {
    ans:=-1
    if e,has:=this.m[key];has{
        ans=e.Value.(*Kv).v
        this.l.MoveToFront(e)
    }
    return ans
}


func (this *LRUCache) Put(key int, value int)  {
    e,has:=this.m[key]
    if has{
        kv:=e.Value.(*Kv)
        this.l.MoveToFront(e)
        kv.v=value
    }else{
        t:=this.l.PushFront(&Kv{key,value})
        this.m[key]=t
        for this.l.Len()>this.h{
            r:=this.l.Back()
            rkv:=r.Value.(*Kv)
            delete(this.m,rkv.k)
            this.l.Remove(r)
        }
    }
    //this.show()
}

func (this *LRUCache) show(){
    fmt.Println(this.l.Len())
    for tmp:=this.l.Front();tmp!=nil;tmp=tmp.Next(){
        fmt.Print(tmp.Value.(*Kv)," ")
    }
    fmt.Println()
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * obj := Constructor(capacity);
 * param_1 := obj.Get(key);
 * obj.Put(key,value);
 */

503. 下一个更大元素 II

503. 下一个更大元素 II

复制一倍断环成链,单调栈放下标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func nextGreaterElements(nums []int) []int {
    l:=len(nums)
    tmp:=make([]int,l+l)
    copy(tmp,nums)
    copy(tmp[l:],nums)
    //fmt.Println(tmp)
    stack:=make([]int,0,l+l)
    ans:=make([]int,l+l)
    for i,v:=range tmp{
        for {
            ls:=len(stack)
            if ls==0{break}
            top:=stack[ls-1]
            if tmp[top]>=v{break}
            ans[top]=v
            stack=stack[:ls-1]
        }
        stack=append(stack,i)
    }
    for _,v:=range stack{
        ans[v]=-1
    }
    return ans[:l]
}

115. 不同的子序列

115. 不同的子序列

动态规划

dp[i][j]表示s[:i]中s[:j]出现次数

basecase:dp[…][0]=1,dp[i][j]=0,i<j

选择:

  • 当s[i]==t[j]时,可以选择将s[i]和s[j]作为子序列的一部分,次数为dp[i-1][j-1],还可以选择不作为子序列的一部分,次数为dp[i-1][j]
  • 当s[i]!=t[j]时,只能选择不作为子序列一部分,次数为dp[i-1][j]

转移方程:

1
2
dp[i][j]=dp[i-1][j-1]+dp[i-1][j],s[i]==t[j]
         dp[i-1][j],s[i]!=s[j]

等式右边只有i和i-1,i从小到大遍历 等式右边只有j和j-1,j从小到大遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func numDistinct(s string, t string) int {
    ls,lt:=len(s),len(t)
    dp:=make([][]int,ls+1)
    for i,_:=range dp{
        dp[i]=make([]int,lt+1)
        dp[i][0]=1
    }
    for i,si:=range s{
        i=i+1
        for j,sj:=range t{
            j=j+1
            if si==sj{
                dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
            }else{
                dp[i][j]=dp[i-1][j]
            }
        }
    }
    return dp[ls][lt]
}

188. 买卖股票的最佳时机 IV

188. 买卖股票的最佳时机 IV

状态:dp[i][j][k] 前i天,最大交易数为j,当天是否持有k的最大收益值

状态转移方程

1
2
3
4
5
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max( 今天选择 rest,        今天选择 sell       )

dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
              max( 今天选择 rest,         今天选择 buy         )

basecase:

  • dp[-1][…][0] = dp[…][0][0] = 0
  • dp[-1][…][1] = dp[…][0][1] = -infinity
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
func maxProfit(max_k int, prices []int) int {
    n := len(prices)
    if n <= 0 {
        return 0
    }

    // base case:
    // dp[-1][...][0] = dp[...][0][0] = 0
    // dp[-1][...][1] = dp[...][0][1] = -infinity
    dp := make([][][]int, n)
    for i := 0; i < n; i++ {
        dp[i] = make([][]int, max_k+1)
        for k := 0; k <= max_k; k++ {
            dp[i][k] = make([]int, 2)
        }
    }

    // k = 0 时的 base case
    for i := 0; i < n; i++ {
        dp[i][0][1] = -1 << 31
        dp[i][0][0] = 0
    }

    for i := 0; i < n; i++ {
        for k := max_k; k >= 1; k-- {
            if i-1 == -1 {
                // 处理 i = -1 时的 base case
                dp[i][k][0] = 0
                dp[i][k][1] = -prices[i]
                continue
            }
            // 状态转移方程
            dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])
            dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])
        }
    }
    return dp[n-1][max_k][0]
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

25 K 个一组翻转链表

25 K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

学会东哥反转链表技巧后结合go语言特有的可以返回多个值的特性写出的解法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseKGroup(head *ListNode, k int) *ListNode {
    reverseK:=func (current *ListNode) (Start,Last *ListNode){
        Last=current
        var prev,next *ListNode=nil,nil
        for i:=0;i<k;i++{
            next=current.Next
            current.Next=prev
            prev=current
            current=next
        }
        Start=prev
        return
    }
    dummy:=&ListNode{Next:head}
    cur,prev:=head,dummy
    for cur!=nil{
        i:=0
        for ;i<k&&cur!=nil;i++{
            cur=cur.Next
        }
        if i==k{
            Start,Last:=reverseK(prev.Next)
            prev.Next=Start
            Last.Next=cur
            prev=Last
        }else{break}
    }
    return dummy.Next
}

利用匿名函数特有的闭包特性定义反转函数reverseK,这样就不用在外面定义全局变量了,reverseK函数中可以直接使用reverseKGroup函数的变量。另外该函数返回反转完后新链表的start和last供外界操作。

首先给链表添加一个dummy虚拟头节点,统一链表头和链表头后面的节点的操作逻辑,而且方便最后返回答案。

然后使用prev记录反转初始节点的前一个节点,然后遍历看是否还有k个节点可供翻转,如果不够就直接break并返回dummy.Next;如果够就给prev.Next调用reverseK。

233. 数字 1 的个数

233. 数字 1 的个数

看了三叶姐的题解,枚举每一位成为1的次数

每一位前面的数字和后面的数字分别为front和back,back位数为k,t=10^k

  • front取[0,front-1]时,t1=front*t
  • front取front时看s[i]取值 + s[i]>1时,t2=t + s[i]==1时,t2=back+1,back取[0,back] + s[i]<1时,t2=0

该位为1的次数为t1+t2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func countDigitOne(n int) int {
    s := strconv.Itoa(n)
    l := len(s)
    ans:=0
    for i,v:=range s{
        front,_:=strconv.Atoi(s[:i])
        back,_:=strconv.Atoi(s[i+1:])
        k:=l-i-1
        t:=1
        for i:=0;i<k;i++{
            t*=10
        }
        //fmt.Println(front,back,i,k,t)
        t1:=front*t
        t2:=0
        if v>'1'{
            t2=t
        }else if v=='1'{
            t2=back+1
        }
        ans+=t1+t2
    }
    return ans
}

80. 删除有序数组中的重复项 II

双指针,l为当前插入索引,r为当前读取索引,遍历数组过程种维护当前值tmp和当前值计数tmpc,当计数小于2时插入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func removeDuplicates(nums []int) int {
    l,r:=0,0
    n:=len(nums)
    var tmp int
    tmpc:=0
    for r<n{
        if nums[r]!=tmp{
            tmp=nums[r]
            tmpc=0
        }
        if tmpc<2{
            nums[l]=nums[r]
            l++
        }
        tmpc++
        r++
    }
    return l
}

189. 轮转数组

189. 轮转数组

3次翻转,翻转整个数组s,翻转s[:k%n],翻转s[k%n:]

1
2
3
4
5
6
7
func rotate(nums []int, k int)  {
    f:=func(i,j int)bool{return i>j}
    n:=len(nums)
    sort.SliceStable(nums,f)
    sort.SliceStable(nums[:k%n],f)
    sort.SliceStable(nums[k%n:],f)
}

45. 跳跃游戏 II

45. 跳跃游戏 II

这里用的暴力搜索,memo[i]存储到nums[i]的最小跳跃次数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func jump(nums []int) int {
    n:=len(nums)
    memo:=make([]int,n)
    for i:=1;i<n;i++{
        memo[i]=math.MaxInt
    }
    //fmt.Println(dp)
    for i,v:=range nums{
        for j:=1;j<=v;j++{
            if i+j<n{
                //fmt.Println(i,j,dp[i+j],dp[i]+1)
                memo[i+j]=Min(memo[i+j],memo[i]+1)
            }
        }
    }
    return memo[n-1]
}

func Min(a,b int)int{
    if a>b{return b}
    return a
}

可以剪枝的,不需要计算[1:n-1)的最小跳跃次数,只需要记录本轮最远位置cur的最小跳跃次数step和当前到最远可达位置max,每次更新本轮最远位置时step++,当max大于等于n-1时,step++跳出循环返回step

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func jump(nums []int) int {
    n:=len(nums)
    if n==1{return 0}
    step:=0
    max:=0
    cur:=0
    for i,v:=range nums{
        if i+v>max{
            max=i+v
        }
        if max>=n-1{
            step++
            break
        }
        if i==cur{
            cur=max
            step++
        }
    }
    return step
}

88. 合并两个有序数组

88. 合并两个有序数组

倒序插入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func merge(nums1 []int, m int, nums2 []int, n int)  {
    nums1=nums1[:cap(nums1)]
    cur:=m+n-1
    c1,c2:=m-1,n-1
    for c1>=0&&c2>=0{
        if nums1[c1]>nums2[c2]{
            nums1[cur]=nums1[c1]
            c1--
        }else{
            nums1[cur]=nums2[c2]
            c2--
        }
        cur--
    }
    if c2>=0{copy(nums1,nums2[:c2+1])}
}

可被 K 整除的最小整数

可被 K 整除的最小整数

数论+哈希表去重

pn为余数,n为被除数,i=j+1

pni=(nj* 10+1)%k=((nj%k)* 10+1)%k=(pnj* 10+1)%k

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func smallestRepunitDivByK(k int) int {
    resid:=1%k
    l:=1
    m:=map[int]bool{}
    for resid!=0{
        m[resid]=true
        l++
        resid=(resid*10+1)%k
        if m[resid]{return -1}
    }
    return l
}

官方题解进行数论优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func smallestRepunitDivByK(k int) int {
    if k%2 == 0 || k%5 == 0 {
        return -1
    }
    x := 1 % k
    for i := 1; ; i++ { // 一定有解
        if x == 0 {
            return i
        }
        x = (x*10 + 1) % k
    }
}

1016. 子串能表示从 1 到 N 数字的二进制串

1016. 子串能表示从 1 到 N 数字的二进制串

暴力搜索优化,二进制n的长度stl,记录长度为stl的s子串,然后取长度l的小于等于n的数(即[1«stl-1,n])查看二进制串是不是子串

因为其他子串都是这些串的子串

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func queryString(s string, n int) bool {
    m:=map[string]bool{}
    l:=len(s)
    st:=strconv.FormatInt(int64(n),2)
    stl:=len(st)
    low:=1<<(stl-1)
    for i:=0;i<=l-stl;i++{
        m[s[i:i+stl]]=true
    }
    //fmt.Println(m)
    for i:=low;i<=n;i++{
        if !m[strconv.FormatInt(int64(i),2)]{return false}
    }
    return true
}

16. 最接近的三数之和

16. 最接近的三数之和

排序+双指针,记录最小差值absans以及对应的ans

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func threeSumClosest(nums []int, target int) int {
    sort.Ints(nums)
    n:=len(nums)
    absans:=math.MaxInt
    var ans int
    for i,v:=range nums{
        l,r:=i+1,n-1
        t:=target-v
        for l<r{
            tmp:=nums[l]+nums[r]
            cur:=tmp-t
            abscur:=abs(cur)
            if absans>abscur{
                absans=abscur
                ans=tmp+v
            }
            if cur>0{
                r--
            }else if cur<0{
                l++
            }else{return target}
        }
    }
    return ans
}

func abs(i int)int{
    if i<0{return -i}
    return i
}

2401. 最长优雅子数组

2401. 最长优雅子数组

动态规划

dp[i]表示以i结尾的最长优雅子数组

1
dp[i]=max(i-j),i-1-j<dp[i-1],k[j+1,i-1],nums[k]&nums[i]==0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func longestNiceSubarray(nums []int) int {
    ans:=1
    n:=len(nums)
    dp:=make([]int,n)
    dp[0]=1
    for i:=1;i<n;i++{
        j:=i-1
        for ;i-1-j<dp[i-1];j--{
            if nums[i]&nums[j]!=0{break}
        }
        dp[i]=i-j
        if dp[i]>ans{
            ans=dp[i]
        }
    }
    return ans
}

时间复杂度最长为O(N²)

但是测评结果比O(N)的位运算差不多,应该是实例的问题

位运算,滑动窗口

这里涉及一个正确性证明,因为滑动窗口内的数字两两与等于0,所以每个位最多只有一个数该位为1,所以不需要计数该位为1的数字个数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func longestNiceSubarray(nums []int) int {
    n:=len(nums)
    l,r:=0,0
    cur:=0
    ans:=1
    for r<n{
        for l<r&&cur&nums[r]!=0{
            cur^=nums[l]
            l++
        }
        cur|=nums[r]
        r++
        count:=r-l
        if ans<count{ans=count}
    }
    return ans
}

2645. 构造有效字符串的最少插入数

2645. 构造有效字符串的最少插入数

动态规划

dp[i]表示word[:i+1]能够需要插入最多数

dp[0]=2,任何一个字母要凑足"abc"都需要插入2个字母

选择:

  • word[i]作为之前word[i-1]插入的字母
  • 给word[i]插入2个字母

状态转移方程

1
2
3
4
dp[i]=dp[i-1]+2,word[i]<=word[i-1]
      插入2个字母
      dp[i-1]-1,word[i]>word[i-1]
      作为插入字母
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func addMinimum(word string) int {
    n:=len(word)
    dp:=make([]int,n)
    dp[0]=2
    for i:=1;i<n;i++{
        if word[i]<=word[i-1]{
            dp[i]=dp[i-1]+2
        }else{
            dp[i]=dp[i-1]-1
        }
    }
    return dp[n-1]
}

这题还能贪心,因为对于能生成的"abc"子串数目tmp,插入后字符串的字母数为3*tmp,减去现有字母数即可获得结果

1
2
3
4
5
6
7
8
func addMinimum(word string) int {
    n:=len(word)
    tmp:=1
    for i:=1;i<n;i++{
        if word[i]<=word[i-1]{tmp++}
    }
    return 3*tmp-n
}

每日一题

面试题 17.05. 字母与数字

面试题 17.05. 字母与数字

前缀和+哈希表优化查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func findLongestSubarray(array []string) []string {
    l:=len(array)
    presum:=make([]int,l+1)
    sum:=0
    for i,v:=range array{
        if unicode.IsDigit(rune(v[0])){
            sum++
        }else{sum--}
        presum[i+1]=sum
    }

    //fmt.Println(presum)
    
    ans:=math.MinInt
    var ansi,ansj int
    m:=make(map[int]int)
    for j,v:=range presum{
        if i,has:=m[v];has{
            if ans<j-i{
                ans=j-i
                ansi=i
                ansj=j
            }
        }else{m[v]=j}
    }

    //fmt.Println(ans,ansi,ansj)

    if ans==math.MinInt{return nil} 
    return array[ansi:ansj]
}

1617. 统计子树中城市之间最大距离

1617. 统计子树中城市之间最大距离

枚举子集(二进制枚举)+ 树形DP求树的直径

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
func countSubgraphsForEachDiameter(n int, edges [][]int) []int {
    graph:=make([][]int,n)
    for i,_:=range graph{graph[i]=[]int{}}
    for _,e :=range edges{
        graph[e[0]-1]=append(graph[e[0]-1],e[1]-1)
        graph[e[1]-1]=append(graph[e[1]-1],e[0]-1)
    }
    ans:=make([]int,n)

    //fmt.Println(graph)

    for mask:=3;mask<(1<<n);mask++{
        //fmt.Println("mask:",mask)
        //至少得有2个节点
        if mask&(mask-1)==0{continue}
        diameter,onpath:=0,0
        var dfs func(int)int
        dfs=func(i int)int{
            //fmt.Println("dfs:",i)
            onpath|=1<<i
            ml:=0
            for _,j:=range graph[i]{
                if (onpath>>j)&1!=1&&(mask>>j)&1==1{
                    tmp:=dfs(j)+1
                    if diameter<tmp+ml{diameter=tmp+ml}
                    if ml<tmp{ml=tmp}
                }
            }
            return ml
        }

        start:=0
        //fmt.Println(onpath,mask)
        for mask&(1<<start)==0{start++}
        dfs(start) 
        //fmt.Println(mask,onpath)
        if onpath==mask{
            //fmt.Println(mask)
            ans[diameter]++
        }
    }

    return ans[1:]
}

2383. 赢得比赛需要的最少训练时长

2383. 赢得比赛需要的最少训练时长

简单模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
func minNumberOfHours(initialEnergy int, initialExperience int, energy []int, experience []int) int {
    ans,tmp:=0,0
    for i,exp:=range experience{
        ene:=energy[i]
        //fmt.Println(initialExperience,initialEnergy,exp,ene)
        tmp=exp+1-initialExperience
        if tmp>0{
            ans+=tmp
            initialExperience+=tmp
        }
        tmp=ene+1-initialEnergy
        if tmp>0{
            ans+=tmp
            initialEnergy+=tmp
        }
        initialExperience+=exp
        initialEnergy-=ene
    }
    return ans
}

1605. 给定行和列的和求可行矩阵

1605. 给定行和列的和求可行矩阵

考虑最简单的情况,rowSum和colSum都是0时为最简单情况,所有值取0即可

所以可以尝试将问题状态往最简单状态转换,每取一个元素的值,可直接将该元素所在行和列的Sum值减去取的值

使用贪心策略,按从左往右和从上往下的方向遍历,每次取rowSum和colSum两者的较小值就可以将问题转换为最简单状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func restoreMatrix(rowSum []int, colSum []int) [][]int {
    r,c:=len(rowSum),len(colSum)
    ans:=make([][]int,r)
    for i,_:=range ans{
        ans[i]=make([]int,c)
    }
    
    for row,rowslice :=range ans{
        for col,_:=range rowslice{
            ans[row][col]=Min(rowSum[row],colSum[col])
            rowSum[row]-=ans[row][col]
            colSum[col]-=ans[row][col]
        }
    }
    return ans
}

func Min(a,b int)int{
    if a<b{return a}
    return b
}

1615. 最大网络秩

1615. 最大网络秩

贪心,选择度最大的节点为第一个节点,然后将该节点的邻居节点的度-1并将该节点的度置零,然后再找度最大的节点为第二个节点

由于第一个节点可能有多个,需要对每一个节点都进行计算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func maximalNetworkRank(n int, roads [][]int) int {
    degree:=make([]int,n)
    graph:=make([][]int,n)
    for i,_:=range graph{graph[i]=[]int{}}
    for _,e:=range roads{
        a,b:=e[0],e[1]
        graph[a]=append(graph[a],b)
        graph[b]=append(graph[b],a)
        degree[a]++
        degree[b]++
    }
    //fmt.Println(degree)
    maxdegree:=0
    for _,v:=range degree{
        if v>maxdegree{
            maxdegree=v
        }
    }
    maxdegree2:=0
    for i,v:=range degree{
        if v==maxdegree{
            tmp:=make([]int,n)
            copy(tmp,degree)
            for _,to:=range graph[i]{
                tmp[to]--
            }
            tmp[i]=0
            for _,vv:=range tmp{
                if maxdegree2<vv{
                    maxdegree2=vv
                }
            }
        }
    }
    return maxdegree+maxdegree2
}

2488. 统计中位数为 K 的子数组

2488. 统计中位数为 K 的子数组

官方题解是前缀和+哈希表优化查询。自己做第一时间分析问题后选择记忆化搜索(类似区间动规),但是核心思想和官方题解一致。

数组中一定是有索引为t值为k的数,并且子数组的左端点索引left和右端点索引right一定满足:0<=left<=k<=right<l,并且子数组中大于k的数的数目减去小于k的数的数目为0或1

我的解题思路是首先通过memo[i]记录[i,k]区间大于k的数的数目减去小于k的数的数目的值,并以memo[i]为键i为值存入哈希表m,优化后续查询,然后固定右端点为[k,l)区间的j,使用tmp来记录后面 [k,j]区间大于k的数的数目减去小于k的数的数目的值。然后通过查询m中键为1-tmp或0-tmp的值i,那么[i,j]即为一个满足条件的子数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func countSubarrays(nums []int, k int) int {
    l:=len(nums)
    
    var t int
    for i,v:=range nums{
        if v==k{
            t=i
            break
        }
    }
    
    ans:=1
    
    memo:=make([]int,t+1)
    memo[t]=0
    m:=make(map[int][]int,l)
    m[0]=[]int{t}
    for i:=t-1;i>=0;i--{
        if nums[i]>k{
            memo[i]=memo[i+1]+1
        }else{memo[i]=memo[i+1]-1}
        m[memo[i]]=append(m[memo[i]],i)
        if memo[i]==0||memo[i]==1{
            ans++
            //fmt.Println(i,t)
        }
    }
    //fmt.Println(memo)
    //fmt.Println(m)
    tmp:=0
    for j:=t+1;j<l;j++{
        if nums[j]>k{
            tmp++
        }else{tmp--}
        ans+=len(m[0-tmp])+len(m[1-tmp])
        //fmt.Println(tmp)
    }
    
    return ans
}

后续按官方题解优化代码,除了使用preSum,还有哈希表的值不使用数组,直接计数即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func countSubarrays(nums []int, k int) int {
    l:=len(nums)
    
    preSum:=make([]int,l)
    m:=make(map[int]int,l)
    flag:=false
    ans:=0
    
    tmp:=0
    for i,v:=range nums{
        if v>k{tmp++}else if v<k{tmp--}
        preSum[i]=tmp
        if v==k{flag=true}
        if flag{
            if tmp==0||tmp==1{ans++}
            //tmp-target=1||tmp-target=0
            ans+=m[tmp-1]+m[tmp]
        }else{m[tmp]++}
    }

    //fmt.Println(preSum)
    //fmt.Println(m)
    
    return ans
}

2389. 和有限的最长子序列

2389. 和有限的最长子序列

贪心,排序+前缀和+二分搜索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func answerQueries(nums []int, queries []int) []int {
    sort.Ints(nums)
    lq,ln:=len(queries),len(nums)
    preSum:=make([]int,ln)
    ans:=make([]int,lq)
    
    tmp:=0
    for i,v:=range nums{
        tmp+=v
        preSum[i]=tmp
    }
    
    bs:=func(t int)int{
        l,r,m:=0,ln-1,0
        for l<=r{
            m=l+(r-l)/2
            if preSum[m]>t{
                r=m-1
            }else if preSum[m]<t{
                l=m+1
            }else {r=m-1}
        }
        if l<ln&&preSum[l]==t{l++}
        return l
    }
    
    for i,v:=range queries{
        ans[i]=bs(v)
    }
    //fmt.Println(preSum)
    //fmt.Println(queries)
    return ans
}

官方题解用了sort.SearchInts方法,此方法用于找到最左值或插入点,另外巧妙的使用了目标值+1的特殊值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func answerQueries(nums []int, queries []int) []int {
    sort.Ints(nums)
    n := len(nums)
    f := make([]int, n)
    f[0] = nums[0]
    for i := 1; i < n; i++ {
        f[i] = f[i-1] + nums[i]
    }
    ans := []int{}
    for _, q := range queries {
        idx := sort.SearchInts(f, q+1)
        ans = append(ans, idx)
    }
    return ans
}

1616. 分割两个字符串得到回文串

1616. 分割两个字符串得到回文串

双指针

从两边往中间判断,当字符不相同时找到分裂点,此时分两种情况,一种是公共部分是a的子数组,另一种是公共部分是b的子数组,两种情况都得计算

时间复杂度O(N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func checkPalindromeFormation(a string, b string) bool {
    check:=func(s string,l,r int)bool{
        for l<r{
            if s[l]!=s[r]{
                return false
            }else{
                l++
                r--
            }
        }
        return true
    }
    
    findSplit:=func(a,b string)bool{
        l,r:=0,len(a)-1
        for l<r&&a[l]==b[r]{
            l++
            r--
        }
        //fmt.Println(a,l,r)
        return check(a,l,r)||check(b,l,r)
    }
    return findSplit(a,b)||findSplit(b,a)
}

1625. 执行操作后字典序最小的字符串

1625. 执行操作后字典序最小的字符串

暴力搜索dfs,map[string]bool去重

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
func findLexSmallestString(s string, a int, b int) string {
    m:=make(map[string]bool,0)
    ans:=[]byte(s)
    
    cmp:=func(a,b []byte)int{
        for i,_:=range a{
            if a[i]<b[i]{
                return 1
            }else if a[i]>b[i]{
                return -1
            }
        }
        return 0
    }
    
    adda:=func(bs []byte)([]byte){
        ret:=make([]byte,len(bs))
        for i,v:=range bs{
            ret[i]=v
            if i&1==1{
                ret[i]+=byte(a)
                if ret[i]>'9'{
                    ret[i]-='0'
                    ret[i]%=10
                    ret[i]+='0'
                }
            }
        }
        return ret
    }
    
    shift:=func(bs []byte)([]byte){
        ret:=make([]byte,0,len(bs))
        for _,v:=range bs[b:]{
            ret=append(ret,v)
        }
        for _,v:=range bs[:b]{
            ret=append(ret,v)
        }
        return ret
    }
    
    
    var dfs func([]byte)
    dfs=func(bs []byte){
        if m[string(bs)]{return}
        if cmp(ans,bs)==-1{
            ans=bs
        }
        m[string(bs)]=true
        dfs(adda(bs))
        dfs(shift(bs))
    }
    
    dfs([]byte(s))
    
    return string(ans)
}

1012. 至少有 1 位重复的数字

1012. 至少有 1 位重复的数字

问题转换+数位DP,自己没写出来,看的三叶姐的题解灵神的题解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
func numDupDigitsAtMostN(n int) (ans int) {
	s := strconv.Itoa(n)
	m := len(s)
	dp := make([][1 << 10]int, m)
	for i := range dp {
		for j := range dp[i] {
			dp[i][j] = -1 // -1 表示没有计算过
		}
	}
	var f func(int, int, bool, bool) int
	f = func(i, mask int, isLimit, isNum bool) (res int) {
		if i == m {
			if isNum {
				return 1 // 得到了一个合法数字
			}
			return
		}
		if !isLimit && isNum {
			dv := &dp[i][mask]
			if *dv >= 0 {
				return *dv
			}
			defer func() { *dv = res }()
		}
		if !isNum { // 可以跳过当前数位
			res += f(i+1, mask, false, false)
		}
		d := 0
		if !isNum {
			d = 1 // 如果前面没有填数字,必须从 1 开始(因为不能有前导零)
		}
		up := 9
		if isLimit {
			up = int(s[i] - '0') // 如果前面填的数字都和 n 的一样,那么这一位至多填数字 s[i](否则就超过 n 啦)
		}
		for ; d <= up; d++ { // 枚举要填入的数字 d
			if mask>>d&1 == 0 { // d 不在 mask 中
				res += f(i+1, mask|1<<d, isLimit && d == up, true)
			}
		}
		return
	}
	return n - f(0, 0, true, false)
}

2469. 温度转换

2469. 温度转换

模拟

1
2
3
func convertTemperature(celsius float64) []float64 {
    return []float64{celsius+273.15,celsius*1.8+32}
}

1626. 无矛盾的最佳球队

1626. 无矛盾的最佳球队

排序+DP

状态转移方程为 dp[i]=max(dp[j])+scores[i],j<i&&scores[i]<=scores[j],方程比较简单就直接自底向上求解即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func bestTeamScore(scores []int, ages []int) int {
    type pair struct{age,score int}
    l:=len(scores)
    s:=make([]pair,0,l)
    for i,v:=range scores{
        s=append(s,pair{ages[i],v})
    }
    sort.Slice(s,func (i,j int)bool{
        if s[i].age<s[j].age{
            return true
        }else if s[i].age==s[j].age{
            if s[i].score<s[j].score{
                return true
            }
        }
        return false
    })
    //fmt.Println(s)

    ans:=0
    memo:=make([]int,l)
    for i,vi:=range s{
        tmp:=0
        for j,vj:=range s[:i]{
            if vi.score>=vj.score&&tmp<=memo[j]{tmp=memo[j]}
        }
        memo[i]=tmp+vi.score
        if ans<memo[i]{ans=memo[i]}
    }
    return ans
}

1630. 等差子数组

1630. 等差子数组

模拟,对每个子数组判断是否是等差子数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func checkArithmeticSubarrays(nums []int, l []int, r []int) []bool {
    ans:=make([]bool,len(l))
    
    check:=func(nums []int)bool{
        l:=len(nums)
        max:=math.MinInt
        min:=math.MaxInt
        m:=map[int]bool{}
        for _,v:=range nums{
            if min>v{min=v}
            if max<v{max=v}
            m[v]=true
        }
        sep:=(max-min)/(l-1)
        if min+sep*(l-1)!=max{return false}
        for tmp:=min+sep;tmp<max;tmp+=sep{
            if _,has:=m[tmp];!has{return false}
        }
        return true
    }
    for i,_:=range l{
        ans[i]=check(nums[l[i]:r[i]+1])
    }
    return ans
}

1032. 字符流

1032. 字符流

后缀树优化暴力搜索,每加入一个字符则从后往前遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
type m map[byte]*m

type StreamChecker struct {
    s []byte
    trie *m
}


func Constructor(words []string) StreamChecker {
    ret:=StreamChecker{}
    ret.trie=&m{}
    for _,s:=range words{
        tmp:=ret.trie
        for i:=len(s)-1;i>=0;i--{
            v:=s[i]
            if _,has:=(*tmp)[v];!has{
                (*tmp)[v]=&m{}
            }
            tmp=(*tmp)[v]
        }
        (*tmp)['0']=&m{}
    }
    return ret
}


func (this *StreamChecker) Query(letter byte) bool {
    this.s=append(this.s,letter)
    tmp:=this.trie
    for i:=len(this.s)-1;i>=0;i--{
        v:=this.s[i]
        if next,has:=(*tmp)[v];has{
            tmp=next
            if (*tmp)['0']!=nil{
                return true
            }
        }else{return false}
    }
    return false
}


/**
 * Your StreamChecker object will be instantiated and called as such:
 * obj := Constructor(words);
 * param_1 := obj.Query(letter);
 */

1574. 删除最短的子数组使剩余数组有序

1574. 删除最短的子数组使剩余数组有序

分析问题,数组长度为l,对于从最左边往右找的最长递增子数组索引为[0,i)和最右边往左找的最长递增子数组为 [j+1,l),那么删除的子数组的左索引 left 取值为 [0,i],右索引 right 取值为[j,l)。

然后就可以通过滑动窗口来找到答案了,由于要求剩余的子数组为最长,所以满足条件的最小滑动窗口是最终答案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func findLengthOfShortestSubarray(arr []int) int {
    l:=len(arr)
    i,j:=1,l-2
    for i<l{
        if arr[i]<arr[i-1]{break}
        i++
    }
    if i==l{return 0}
    for j>0{
        if arr[j]>arr[j+1]{break}
        j--
    }
    left,right:=-1,j+1
    //fmt.Println(left,right)
    ans:=math.MaxInt
    for left<i{
        for right<l&&left>=0&&arr[left]>arr[right]{
            right++
        }
        if ans>right-left-1{ans=right-left-1}
        left++
    }
    return ans
}

2395. 和相等的子数组

2395. 和相等的子数组

简单模拟

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func findSubarrays(nums []int) bool {
    l:=len(nums)
    presum:=make([]int,l+1)
    tmp:=0
    for i,_:=range presum[1:]{
        tmp+=nums[i]
        presum[i+1]=tmp
    }

    //fmt.Println(presum)

    m:=map[int]bool{}
    for i,vl:=range presum[:l-1]{
        vr:=presum[i+2]
        if m[vr-vl]{return true}
        m[vr-vl]=true
    }
    return false
}

1638. 统计只差一个字符的子串数目

1638. 统计只差一个字符的子串数目

暴力枚举,确定左端点枚举右端点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func countSubstrings(s string, t string) int {
    ls,lt:=len(s),len(t)
    ans:=0
    for i:=0;i<ls;i++{
        for j:=0;j<lt;j++{
            flag:=false
            for k:=0;i+k<ls&&j+k<lt;k++{
                if s[i+k]!=t[j+k]{
                    if flag{break}
                    flag=true
                }
                if flag{ans++}
            }
        }
    }
    return ans
}

1092. 最短公共超序列

1092. 最短公共超序列

求最长公共子序列(动态规划,我这里用的是自顶向下的方法),倒序遍历构造字符串,最后将字符串翻转即为答案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
func shortestCommonSupersequence(str1 string, str2 string) string {
    //预处理,设置memo数组和memopre数组
    l1,l2:=len(str1),len(str2)
    memo:=make([][]int,l1)
    for i,_:=range memo{
        memo[i]=make([]int,l2)
        for j,_:=range memo[i]{
            memo[i][j]=-1
        }
    }
    memopre:=make([][][]int,l1)
    for i,_:=range memopre{
        memopre[i]=make([][]int,l2)
    }
    
    //basecase
    if str1[0]==str2[0]{
        memo[0][0]=1
        memopre[0][0]=[]int{-1,-1}
    }else{
        memo[0][0]=0
        memopre[0][0]=[]int{-1,0}
    }
    
    //dp函数遍历
    var dp func(i,j int)int
    dp=func(i,j int)int{
        if i<0||j<0{return 0}
        if memo[i][j]!=-1{
            return memo[i][j]
        }
        
        if str1[i]==str2[j]{
            memo[i][j]=dp(i-1,j-1)+1
            memopre[i][j]=[]int{i-1,j-1}
        }else{
            i1j:=dp(i-1,j)
            ij1:=dp(i,j-1)
            max,maxi,maxj:=i1j,i-1,j
            if i1j<ij1{
                max,maxi,maxj=ij1,i,j-1
            }
            memo[i][j]=max
            memopre[i][j]=[]int{maxi,maxj}
        }
        return memo[i][j]
    }
    
    //求解
    dp(l1-1,l2-1)
    
    //fmt.Println(memo)
    //fmt.Println(memopre)
    
    //构造答案
    b:=strings.Builder{}
    i,j:=l1-1,l2-1
    for i>=0&&j>=0{
        if memopre[i][j][0]==i{
            b.WriteByte(str2[j])
        }else{
            b.WriteByte(str1[i])
        }
        i,j=memopre[i][j][0],memopre[i][j][1]
    }
    for i>=0{
        b.WriteByte(str1[i])
        i--
    }
    for j>=0{
        b.WriteByte(str2[j])
        j--
    }
    ansr:=b.String()
    b.Reset()
    for i=len(ansr)-1;i>=0;i--{
        b.WriteByte(ansr[i])
    }
    return b.String()
}

1641. 统计字典序元音字符串的数目

1641. 统计字典序元音字符串的数目

动态规划

basecase:dp[i][j]=1,i=1

1
dp[i][j]=sum(dp[i-1][k-1]),i>1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func countVowelStrings(n int) int {
    dp:=make([][]int,n+1)
    for i,_:=range dp{
        dp[i]=make([]int,5)
    }
    for j,_:=range dp[1]{dp[1][j]=1}
    for i:=2;i<=n;i++{
        tmp:=0
        for j,_:=range dp[i]{
            tmp+=dp[i-1][j]
            dp[i][j]=tmp
        }
    }
    
    ans:=0
    for _,v:=range dp[n]{ans+=v}
    return ans
}

1637. 两点之间不包含任何点的最宽垂直区域

1637. 两点之间不包含任何点的最宽垂直区域

仔细分析题意,排序贪心,遍历选择最大的x间距

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func maxWidthOfVerticalArea(points [][]int) int {
    s:=make([]int,0)
    for _,v :=range points{
        s=append(s,v[0])
    }
    sort.Ints(s)
    ans:=0
    for i:=1;i<len(s);i++{
        ans=max(ans,s[i]-s[i-1])
    }
    return ans
}

func max(a,b int)int{
    if a<b{return b}
    return a
}

2367. 算术三元组的数目

2367. 算术三元组的数目

哈希表优化搜索

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func arithmeticTriplets(nums []int, diff int) int {
    l:=len(nums)
    ans:=0
    m:=make(map[int]int,l)
    for _,v:=range nums{
        m[v]++
    }
    for j:=1;j<l-1;j++{
        v:=nums[j]
        ans+=m[v-diff]*m[v+diff]
    }
    return ans
}

831. 隐藏个人信息

831. 隐藏个人信息

模拟,使用regexp包捕获分组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func maskPII(s string) string {
    ans:=strings.Builder{}
    email,_:=regexp.Compile(`^(\w+)((?i)@\w+\.\w+)$`)
    ss:=email.FindStringSubmatch(s)
    //fmt.Println(ss)
    if len(ss)>0{
        ans.WriteRune(unicode.ToLower(rune(ss[1][0])))
        ans.WriteString("*****")
        ans.WriteRune(unicode.ToLower(rune(ss[1][len(ss[1])-1])))
        ans.WriteString(strings.ToLower(ss[2]))
    }else{
        tmp:=strings.Builder{}
        for _,v:=range s{
            if unicode.IsDigit(v){
                tmp.WriteRune(v)
            }
        }
        t:=tmp.String()
        n:=len(t)
        if n>10{
            ans.WriteRune('+')
            for n>10{
                ans.WriteRune('*')
                n--
            }
            ans.WriteRune('-')
        }
        ans.WriteString("***-***-")
        ans.WriteString(t[len(t)-4:])
    }
    return ans.String()
}

1039. 多边形三角剖分的最低得分

1039. 多边形三角剖分的最低得分

自顶向下动态规划,dp[i][j]表示索引范围[i,j]的最低得分

1
2
dp[i][j]=min(dp[i][k]+dp[k][j])+values[i] * values[k] * values[j],k属于(i,j)
         0,j-i<=1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func minScoreTriangulation(values []int) int {
    l:=len(values)
    if l<3{return 0}
    memo :=make([][]int,l)
    for i,_:=range memo{
        memo[i]=make([]int,l)
    }
    
    var dp func(i,j int)int
    dp=func(i,j int)int{
        if memo[i][j]!=0{
            return memo[i][j]
        }
        min:=math.MaxInt
        for k:=i+1;k<j;k++{
            part1,part3:=0,0
            part2:=values[i]*values[j]*
                 values[k]
            if k-i>1{part1=dp(i,k)}
            if j-k>1{part3=dp(k,j)}
            tmp:=part1+part2+part3
            if min>tmp{min=tmp}
        }
        memo[i][j]=min
        return memo[i][j]
    }
    return dp(0,l-1)
}

1053. 交换一次的先前排列

1053. 交换一次的先前排列

贪心策略

从后往前遍历,取第一个增大的元素x,然后再往后找比x小的最大的元素y,交换这两个的操作就是答案,

如果找不到x则表示已经是最小序列,这里用一个flag来判断,如果找到了flag赋值为true

这里有一个小细节,因为有重复元素,所以y元素必须保证是所有重复元素的最左边的元素,所以添加了一列找最左元素的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func prevPermOpt1(arr []int) []int {
    l:=len(arr)
    var i,j int
    flag:=false
    for i=l-2;i>=0;i--{
        if arr[i]>arr[i+1]{
            flag=true
            for j=i+1;j<l;j++{
                if arr[j]>=arr[i]{
                    break
                }
            }
            j--
            // 找最左元素
            for arr[j]==arr[j-1]{j--}
            break
        }
    }
    if flag{
        arr[i],arr[j]=arr[j],arr[i]
    }
    return arr
}

1000. 合并石头的最低成本

1000. 合并石头的最低成本

动态规划,官方题解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func mergeStones(stones []int, k int) int {
    n := len(stones)
    if (n-1)%(k-1) != 0 {
        return -1
    }

    d := make([][][]int, n)
    for i := range d {
        d[i] = make([][]int, n)
        for j := range d[i] {
            d[i][j] = make([]int, k+1)
            for k := range d[i][j] {
                d[i][j][k] = 1e9
            }
        }
    }
    sum := make([]int, n+1)
    for i, s := 0, 0; i < n; i++ {
        d[i][i][1] = 0
        s += stones[i]
        sum[i+1] = s
    }

    for len := 2; len <= n; len++ {
        for l := 0; l < n && l+len-1 < n; l++ {
            r := l + len - 1
            for t := 2; t <= k; t++ {
                for p := l; p < r; p += k - 1 {
                    d[l][r][t] = min(d[l][r][t], d[l][p][1]+d[p+1][r][t-1])
                }
            }
            d[l][r][1] = min(d[l][r][1], d[l][r][k]+sum[r+1]-sum[l])
        }
    }
    return d[0][n-1][1]
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

状态转移方程太复杂了,自己的代码没有完善

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func mergeStones(stones []int, k int) int {
    l:=len(stones)
    memo:=make([][][]int,l)
    for i,_ :=range memo{
        memo[i]=make([][]int,l)
        for j,_:=range memo[i]{
            memo[i][j]=make([]int,k+1)
            memo[i][i][1]=stones[i]
        }
    }
    presum:=make([]int,l+1)
    tmp:=0
    for i,v:=range stones{
        tmp+=v
        presum[i+1]=tmp
    }

    var dp func(i,j,k int)int
    dp=func(i,j,k int)int{
        if memo[i][j][k]!=0{return memo[i][j][k]}
        if j-i+1<k{return math.MaxInt}
        cost:=presum[j+1]-presum[i]
        min:=math.MaxInt
        for t:=i;t<=j-k;t++{
            tmp:=dp(i,t,1)
        }
    }
}

2427. 公因子的数目

2427. 公因子的数目

模拟,这里使用了最小公因数优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func commonFactors(a int, b int) int {
    g:=gcd(a,b)
    ans:=0
    for i:=1;i<=g;i++{
        if a%i==0&&b%i==0{ans++}
    }
    return ans
}

func gcd(a,b int)int{
    for a!=0{
        a,b=b%a,a
    }
    return b
}

1040. 移动石子直到连续 II

1040. 移动石子直到连续 II

贪心计算最大值,滑动窗口判断最小值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func numMovesStonesII(stones []int) []int {
    sort.Ints(stones)
    l:=len(stones)
    max:=Max(stones[l-1]-stones[1],stones[l-2]-stones[0])-(l-2)
    if stones[l-1]-stones[0]==l-1{return []int{0,0}}
    min,curcount:=math.MaxInt,0
    left,right:=0,0
    fmt.Println(stones)
    for right<l{
        curcount++
        right++
        for left<right&&stones[left]<=stones[right-1]-l{
            curcount--
            left++ 
        }
        tmp:=l-curcount
        if curcount==l-1&&
            stones[right-1]-stones[left]==curcount-1{tmp++}
        //fmt.Println(left,right,tmp,min)
        min=Min(tmp,min)
    }
    return []int{min,max}
}

func Max(s ...int)int{
    tmp:=math.MinInt
    for _,v:=range s{
        if tmp<v{tmp=v}
    }
    return tmp
}

func Min(s ...int)int{
    tmp:=math.MaxInt
    for _,v:=range s{
        if tmp>v{tmp=v}
    }
    return tmp
}

1026. 节点与其祖先之间的最大差值

1026. 节点与其祖先之间的最大差值

树形dp,返回左右子树的最大节点和最小节点,合并最大节点max和最小节点min,ans=Max(ans,Abs(root.Val-min),Abs(root.Val-max))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func maxAncestorDiff(root *TreeNode) int {
    ans:=math.MinInt
    var dfs func(root *TreeNode)(min,max int)
    dfs=func(root *TreeNode)(min,max int){
        if root==nil{return math.MaxInt,math.MinInt}
        lmin,lmax:=dfs(root.Left)
        if lmin==math.MaxInt{
            lmin=root.Val
            lmax=root.Val
        }
        rmin,rmax:=dfs(root.Right)
        if rmin==math.MaxInt{
            rmin=root.Val
            rmax=root.Val
        }
        min=Min(lmin,rmin,root.Val)
        max=Max(lmax,rmax,root.Val)
        ans=Max(abs(root.Val-min),abs(root.Val-max),ans)
        return
    }
    
    dfs(root)
    return ans
}

func abs(i int)int{
  if i<0{i=-i}
  return i
}

func Max(s ...int)int{
    tmp:=math.MinInt
    for _,v:=range s{
        if tmp<v{tmp=v}
    }
    return tmp
}

func Min(s ...int)int{
    tmp:=math.MaxInt
    for _,v:=range s{
        if tmp>v{tmp=v}
    }
    return tmp
}

2418. 按身高排序

2418. 按身高排序

排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func sortPeople(names []string, heights []int) []string {
    l:=len(heights)
    index:=make([]int,l)
    for i:=range index{index[i]=i}
    sort.Slice(index,func (i,j int)bool{
        return heights[index[i]]>heights[index[j]]
    })
    ans:=make([]string,l)
    for i,v:=range index{
        ans[i]=names[v]
    }
    return ans
}

1419. 数青蛙

1419. 数青蛙

模拟,使用max记录最大青蛙数,cur记录当前青蛙数,mi记录字母对应青蛙数,mr记录每个字母对应的前一个字母

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func minNumberOfFrogs(croakOfFrogs string) int {
    mi:=map[rune]int{}
    mr:=map[rune]rune{
        'r':'c',
        'o':'r',
        'a':'o',
        'k':'a',
    }
    max,cur:=0,0
    for _,v:=range croakOfFrogs{
        if v=='c'{
            cur++
            if max<cur{max=cur}
        }else{
            tmp:=mr[v]
            if mi[tmp]<1{return -1}
            mi[tmp]--
        }
        mi[v]++
        if v=='k'{cur--}
    }
    for k,v:=range mi{
        if k=='k'{continue}
        if v!=0{return -1}
    }
    return max
}

2437. 有效时间的数目

2437. 有效时间的数目

分开算小时和分钟的有效值ch和cm,返回他们的乘积

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func countTime(time string) int {
    h:=[]byte(time[:2])
    m:=[]byte(time[3:])
    ch,cm:=0,0
    for i:=0;i<24;i++{
        h0,h1:=byte(i/10)+'0',byte(i%10)+'0'
        if (h[1]=='?'||h[1]==h1)&&(h[0]=='?'||h[0]==h0){ch++}
    }
    for i:=0;i<60;i++{
        m0,m1:=byte(i/10)+'0',byte(i%10)+'0'
        if (m[1]=='?'||m[1]==m1)&&(m[0]=='?'||m[0]==m0){cm++}
    }
    //fmt.Println(ch,cm)
    return ch*cm
}

剑指offer

剑指 Offer II 111. 计算除法

剑指 Offer II 111. 计算除法

官方题解为bfs,floyd算法和带权并查集

我这里使用dfs+回溯优化,耗时0ms,时间复杂度打败100%提交

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
func calcEquation(equations [][]string, values []float64, queries [][]string) []float64 {
    l:=len(equations)
    lq:=len(queries)
    eq:=make(map[string]map[string]float64,0)
    //onpath:=make(map[string]bool,0)
    visited:=make(map[string]bool,0)

    for i:=0;i<l;i++{
        if _,has:=eq[equations[i][0]];!has{eq[equations[i][0]]=make(map[string]float64,2)}
        if _,has:=eq[equations[i][1]];!has{eq[equations[i][1]]=make(map[string]float64,2)}
        eq[equations[i][0]][equations[i][1]]=values[i]
        eq[equations[i][1]][equations[i][0]]=1/values[i]
        //onpath[equations[i][0]]=false
        //onpath[equations[i][1]]=false
        visited[equations[i][0]]=false
        visited[equations[i][1]]=false
    }

    init:=func(){
        for k,_:=range visited{
            //onpath[k]=false
            visited[k]=false
        }
    }

    var dfs func (from,to string)float64
    dfs=func (from,to string)float64{
        if visited[from]{return -1}
        if v,has:=eq[from][to];has{return v}
        //onpath[from]=true
        visited[from]=true
        ret:=-1.0
        for mid,_:=range eq[from]{
            if ret=dfs(mid,to);ret!=-1{
                ret*=eq[from][mid]
                eq[from][to]=ret
                eq[to][from]=1/ret
                break
            }
        }
        //onpath[from]=false
        return ret
    }

    ans:=make([]float64,lq)
    for i:=0;i<lq;i++{
        _,has1:=visited[queries[i][0]]
        _,has2:=visited[queries[i][1]]
        if !(has1&&has2){
            //fmt.Println("aaa")
            ans[i]=-1
            continue
        }
        if queries[i][0]==queries[i][1]{
            ans[i]=1
        }else{
            init()
            ans[i]=dfs(queries[i][0],queries[i][1])
        }
    }
    return ans
}

剑指 Offer II 109. 开密码锁

剑指 Offer II 109. 开密码锁

BFS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
func openLock(deadends []string, target string) int {
    type pair struct{
        s string
        step int
    }
    visited:=make(map[string]bool,0)
    dead:=make(map[string]bool,0)
    for i:=0;i<len(deadends);i++{dead[deadends[i]]=true}
    
    isdead:=func(s string)bool{
        _,ok:=dead[s]
        return ok
    }

    isvisited:=func(s string)bool{
        _,ok:=visited[s]
        return ok
    }

    add:=func (s []byte,i int){
        if s[i]=='9'{
            s[i]='0'
        }else {
            s[i]++
        }
    }

    dec:=func (s []byte,i int){
        if s[i]=='0'{
            s[i]='9'
        }else {
            s[i]--
        }
    }

    if isdead("0000"){return -1}

    q:=list.New()
    q.PushBack(pair{"0000",0})
    visited["0000"]=true

    for q.Len()!=0{
        p:=q.Remove(q.Front()).(pair)
        if p.s==target{return p.step}
        for i:=0;i<4;i++{
            tmp1,tmp2:=[]byte(p.s),[]byte(p.s)
            add(tmp1,i)
            dec(tmp2,i)
            if !isvisited(string(tmp1))&&!isdead(string(tmp1)){
                q.PushBack(pair{string(tmp1),p.step+1})
                visited[string(tmp1)]=true
            }
            if !isvisited(string(tmp2))&&!isdead(string(tmp2)){
                q.PushBack(pair{string(tmp2),p.step+1})
                visited[string(tmp2)]=true
            }
        }
    }
    return -1
}

剑指 Offer II 112. 最长递增路径

剑指 Offer II 112. 最长递增路径

动态规划,树形dp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func longestIncreasingPath(matrix [][]int) int {
    row,col,ans:=len(matrix),len(matrix[0]),1
    dp:=make([][]int,row)
    for i,_:=range dp{
        dp[i]=make([]int,col)
    }

    dirs:=[][]int{{0,1},{1,0},{0,-1},{-1,0}}
    var dfs func(i,j int) int
    dfs=func(i,j int) int{
        if dp[i][j]!=0{return dp[i][j]}
        tmp:=1
        for _,v:=range dirs{
            in,jn:=i+v[0],j+v[1]
            if in>=0&&jn>=0&&in<row&&jn<col&&matrix[i][j]<matrix[in][jn]{
                ret:=dfs(in,jn)+1
                if tmp<ret{tmp=ret}
            }
        }
        dp[i][j]=tmp
        if tmp>ans{ans=tmp}
        return dp[i][j]
    }

    for i,_ :=range matrix{
        for j,_ := range matrix[i]{
            dfs(i,j)
        }
    }

    return ans
}

剑指 Offer 04. 二维数组中的查找

剑指 Offer 04. 二维数组中的查找

贪心,从左下或者右上开始遍历,相当于一个二叉排序树

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func findNumberIn2DArray(matrix [][]int, target int) bool {
    if len(matrix)==0{return false}
    m,n:=len(matrix),len(matrix[0])
    i,j:=m-1,0
    for i>=0&&j<n&&matrix[i][j]!=target{
        if matrix[i][j]<target{
            j++
        }else{i--}
    }
    fmt.Println(i,j)
    if i>=0&&j<n{return true}
    return false
}

剑指 Offer 59 - I. 滑动窗口的最大值

剑指 Offer 59 - I. 滑动窗口的最大值

具有固定长度的队列性质的单调栈,也是存入下标值,时间复杂度O(N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func maxSlidingWindow(nums []int, k int) []int {
    queue:=[]int{}
    ans:= []int{}
    for i:=0;i<len(nums);i++{
        for len(queue)>0 && nums[queue[len(queue)-1]]<=nums[i]{
            queue=queue[:len(queue)-1]
        }
        queue=append(queue,i)
        for queue[0]<i-k+1{
            queue=queue[1:]
        }
        if i>=k-1{
            ans=append(ans,nums[queue[0]])
        }
    }
    return ans
}

优先队列(二叉堆)做法需要在堆中存储对应值的下标,及时将下标小于当前值-k的节点pop掉,但是优先队列插入元素需要O(logN)复杂度所以它的时间复杂度为O(NlogN)

hot100

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

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

删除倒数第n个,所以选择倒数第n+1个节点,删除它的下一个节点即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    dummy:=&ListNode{Next:head}
    fast,slow:=dummy,dummy
    for i:=0;i<n+1;i++{
        if fast==nil{return head}
        fast=fast.Next
    }
    for fast!=nil{
        fast=fast.Next
        slow=slow.Next
    }
    target:=slow.Next
    slow.Next=target.Next
    target.Next=nil
    return dummy.Next
}

17. 电话号码的字母组合

17. 电话号码的字母组合

回溯,这里使用了string不可变类型的特性,将[]byte转换为string会自动复制一份,这样就不需要你make后再copy了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func letterCombinations(digits string) []string {
    l:=len(digits)
    if l==0{return []string{}}
    ans:=[]string{}
    onpath:=make([]byte,l)
    var backtrack func(i int)
    backtrack=func(i int){
        if i==l{
            ans=append(ans,string(onpath))
            return
        }
        for _,v :=range m[digits[i]]{
            onpath[i]=v
            backtrack(i+1)
        }
    }
    backtrack(0)
    return ans
}

var m map[byte][]byte=map[byte][]byte{
    '2':[]byte{'a','b','c'},
    '3':[]byte{'d','e','f'},
    '4':[]byte{'g','h','i'},
    '5':[]byte{'j','k','l'},
    '6':[]byte{'m','n','o'},
    '7':[]byte{'p','q','r','s'},
    '8':[]byte{'t','u','v'},
    '9':[]byte{'w','x','y','z'},
}

20. 有效的括号

20. 有效的括号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func isValid(s string) bool {
    stack:=make([]rune,0,len(s))
    m:=map[rune]rune{
        ']':'[',
        ')':'(',
        '}':'{',
    }
    for _,v:=range s{
        if v=='['||v=='('||v=='{'{
            stack=append(stack,v)
        }else if v==']'||v==')'||v=='}'{
            l:=len(stack)
            if l==0{return false}
            if stack[l-1]!=m[v]{
                return false
            }
            stack=stack[:l-1]
        }
    }
    if len(stack)!=0{return false}
    return true
}

21. 合并两个有序链表

21. 合并两个有序链表

迭代,使用虚拟节点技巧

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
    dummy:=&ListNode{Next:nil}
    now:=dummy
    for list1!=nil&&list2!=nil{
        if list1.Val<list2.Val{
            now.Next=list1
            list1=list1.Next
        }else{
            now.Next=list2
            list2=list2.Next
        }
        now=now.Next
    }
    if list1!=nil{
        now.Next=list1
    }else{now.Next=list2}

    return dummy.Next
}

33. 搜索旋转排序数组

33. 搜索旋转排序数组

二分搜索,先比较nums[mid]和nums[0],再考虑num[mid]和target

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func search(nums []int, target int) int {
    if target==nums[0]{return 0}
    l,r:=0,len(nums)-1
    var mid int
    for l<=r{
        mid=l+(r-l)/2
        //fmt.Println(l,r,mid)
        if nums[mid]==target{return mid}
        if nums[mid]>nums[0]{
            if target>nums[0]&&target<nums[mid]{
                r=mid-1
            }else {
                l=mid+1
            }
        }else if nums[mid]<nums[0]{
            if target>nums[mid]&&target<nums[0]{
                l=mid+1
            }else{
                r=mid-1
            }
        }else{l=mid+1}
    }
    //fmt.Println(l,r)
    return -1
}

39. 组合总和

39. 组合总和

回溯,东哥说的回溯解决所有排列组合子集问题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func combinationSum(candidates []int, target int) [][]int {
    cur:=0
    l:=len(candidates)
    onpath:=[]int{}
    ans:=[][]int{}
    var backtrack func(start int)
    backtrack=func(start int){
        if cur==target{
            tmp:=make([]int,len(onpath))
            copy(tmp,onpath)
            ans=append(ans,tmp)
            return
        }
        if cur>target{return}
        for i:=start;i<l;i++{
            cur+=candidates[i]
            onpath=append(onpath,candidates[i])
            backtrack(i)
            onpath=onpath[:len(onpath)-1]
            cur-=candidates[i]
        }
    }
    backtrack(0)
    return ans
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func combinationSum(candidates []int, target int) [][]int {
    sort.Ints(candidates)
    ans:=[][]int{}
    onpath:=[]int{}
    var backtrack func(n,k int)
    backtrack=func(n,k int){
        if n<0{
            return
        }
        if n==0{
            tmp:=make([]int,len(onpath))
            copy(tmp,onpath)
            ans=append(ans,tmp)
            return
        }
        for k<len(candidates){
            onpath=append(onpath,candidates[k])
            backtrack(n-candidates[k],k)
            onpath=onpath[:len(onpath)-1]
            k++
        }
    }
    backtrack(target,0)
    return ans
}

46. 全排列

46. 全排列

回溯

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func permute(nums []int) [][]int {
    l:=len(nums)
    onpath:=make([]int,0,l)
    visited:=make([]bool,l)
    ans:=[][]int{}
    var backtrack func(i int)
    backtrack=func(i int){
        if visited[i]{return}
        visited[i]=true
        onpath=append(onpath,nums[i])

        if len(onpath)==l{
            tmp:=make([]int,l)
            copy(tmp,onpath)
            ans=append(ans,tmp)
        }else{
            for i,_:=range nums{
                backtrack(i)
            }
        }
        
        onpath=onpath[:len(onpath)-1]
        visited[i]=false
    }
    for i,_:=range nums{
        backtrack(i)
    }
    return ans
}

48. 旋转图像

48. 旋转图像

模拟,将左上1/4的元素循环修改即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func rotate(matrix [][]int)  {
    l:=len(matrix)
    m:=(l+1)/2 -1
    n:=m
    swap:=func (i,j int){
        tmp:=matrix[i][j]
        matrix[i][j]=matrix[l-1-j][i]
        matrix[l-1-j][i]=matrix[l-1-i][l-1-j]
        matrix[l-1-i][l-1-j]=matrix[j][l-1-i]
        matrix[j][l-1-i]=tmp
    }
    if l&1==1{
        m--
    }
    //fmt.Println(m,n)
    for i:=0;i<=m;i++{
        for j:=0;j<=n;j++{
            swap(i,j)
        }
    }
    return
}

49. 字母异位词分组

49. 字母异位词分组

数组每个单词计数字母数,哈希表记录分组结果,这里运用了使用数组作为键的技巧,数组可以快速判断计数是否一致

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func groupAnagrams(strs []string) [][]string {
    m:=map[[26]int][]string{}
    for _,s:=range strs{
        tmp:=[26]int{}
        for _,v:=range s{
            tmp[v-'a']++
        }
        m[tmp]=append(m[tmp],s)
    }
    ans:=make([][]string,0,len(m))
    for _,v:=range m{
        ans=append(ans,v)
    }
    return ans
}

53. 最大子数组和

53. 最大子数组和

动态规划

定义状态:dp[i]为以i结尾的最大子数组和

basecase:dp[0]=nums[0]

选择:当前位置要nums[i-1](接着dp[i-1]的子数组加上nums[i])和不要nums[i-1](nums[i]自己成为一个新的子数组)

状态转移方程:dp[i]=Max(dp[i-1]+nums[i],nums[i])

在转移过程中用ans更新最大值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
func maxSubArray(nums []int) int {
    l:=len(nums)
    dp:=make([]int,l)
    dp[0]=nums[0]
    ans:=dp[0]
    for i,v:=range nums[1:]{
        i+=1
        dp[i]=Max(dp[i-1]+v,v)
        if dp[i]>ans{ans=dp[i]}
    }
    return ans
}

func Max(a,b int)int{
    if a>b{return a}
    return b
}

55. 跳跃游戏

55. 跳跃游戏

贪心,记录最远能到的位置,遍历数组更新最远位置

1
2
3
4
5
6
7
8
9
func canJump(nums []int) bool {
    l:=len(nums)
    tmp:=0
    for i:=0;i<=tmp&&tmp<l;i++{
        r:=i+nums[i]
        if tmp<r{tmp=r}
    }
    return tmp>=l-1
}

56. 合并区间

56. 合并区间

按start排序,模拟合并,分两种情况,v[0]>start和其他情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func merge(intervals [][]int) [][]int {
    sort.Slice(intervals,func (i,j int)bool{
        return intervals[i][0]<intervals[j][0]
    })
    start,end:=intervals[0][0],intervals[0][1]
    ans:=[][]int{}
    for _,v:=range intervals{
        if v[0]>end{
            ans=append(ans,[]int{start,end})
            start=v[0]
            end=v[1]
        }else{
            end=Max(end,v[1])
        }
    }
    ans=append(ans,[]int{start,end})
    return ans
}

func Max(a,b int)int{
    if a<b{return b}
    return a
}

62. 不同路径

62. 不同路径

动态规划,添加虚拟行列技巧

dp[i][j]代表到(i,j)位置的路径数

basecase:dp[1][1]=1,但是为了统一遍历逻辑,将dp[0][1]设置为1

选择:只能往右或往下,可以选择从(i-1,j)到(i,j)或者(i,j-1)到(i,j)

状态转移:dp[i][j]=dp[i-1][j]+dp[i][j-1]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func uniquePaths(m int, n int) int {
    dp:=make([][]int,m+1)
    for i:=range dp{
        dp[i]=make([]int,n+1)
    }
    dp[0][1]=1
    for i:=1;i<m+1;i++{
        for j:=1;j<n+1;j++{
            dp[i][j]=dp[i-1][j]+dp[i][j-1]
        }
    }
    return dp[m][n]
}

64. 最小路径和

64. 最小路径和

动态规划,添加虚拟行列技巧

dp[i][j]代表到(i,j)位置的最小值

basecase:dp[1][1]=nums[0][0],为了统一遍历逻辑将虚拟行列都设置为math.MaxInt,然后将dp[0][1]设置为0

选择:只能往右或往下,可以选择从(i-1,j)到(i,j)或者(i,j-1)到(i,j)

状态转移:dp[i][j]=Min(dp[i-1][j],dp[i][j-1])+nums[i-1][j-1]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func minPathSum(grid [][]int) int {
    m,n:=len(grid),len(grid[0])
    dp:=make([][]int,m+1)
    for i:=range dp{
        dp[i]=make([]int,n+1)
        dp[i][0]=math.MaxInt
    }
    for j:=0;j<n+1;j++{
        dp[0][j]=math.MaxInt
    }
    dp[0][1]=0
    for i:=1;i<m+1;i++{
        for j:=1;j<n+1;j++{
            dp[i][j]=Min(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1]
        }
    }
    return dp[m][n]
}

func Min(a,b int)int{
    if a<b{return a}
    return b
}

70. 爬楼梯

70. 爬楼梯

动态规划

dp[i]为到第i阶的路径数

basecase:dp[0],dp[1]都为1

选择:只能跳上1,2阶,可以从i-1阶或i-2阶跳上来

dp[i]=dp[i-1]+dp[i-2]

1
2
3
4
5
6
7
8
9
func climbStairs(n int) int {
    dp:=make([]int,n+1)
    dp[0]=1
    dp[1]=1
    for i:=2;i<n+1;i++{
        dp[i]=dp[i-1]+dp[i-2]
    }
    return dp[n]
}

75. 颜色分类

75. 颜色分类

快排

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func sortColors(nums []int)  {
    var fs func(i,j int)
    fs=func(i,j int){
        //fmt.Println(i,j)
        if i>=j{return}
        tmp:=nums[i]
        l,r:=i,j
        for l<r{
            for l<r&&nums[r]>=tmp{
                r--
            }
            nums[l]=nums[r]
            for l<r&&nums[l]<=tmp{
                l++
            }
            nums[r]=nums[l]
        }
        //fmt.Println(l,nums)
        nums[l]=tmp
        fs(i,l-1)
        fs(l+1,j)
    }
    fs(0,len(nums)-1)
}

一次遍历,计数排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func sortColors(nums []int)  {
    n0,n1:=0,0
    for _,v:=range nums{
        switch v{
            case 0:n0++
            case 1:n1++
        }
    }
    tmp:=0
    for i:=0;i<n0;i++{
        nums[tmp]=0
        tmp++
    }
    for i:=0;i<n1;i++{
        nums[tmp]=1
        tmp++
    }
    for tmp<len(nums){
        nums[tmp]=2
        tmp++
    }
}

76. 最小覆盖子串

76. 最小覆盖子串

滑动窗口,官方题解使用check函数遍历比对是否覆盖,时间复杂度O(NK),K为子串不同字符数,我这里用还未覆盖字符数n优化,在滑动过程中维护该值,时间复杂度为O(N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func minWindow(s string, t string) string {
    mt:=map[byte]int{}
    for i:=range t{
        mt[t[i]]++
    }
    n:=len(mt)
    var ansl,ansr int
    ans:=math.MaxInt
    ms:=map[byte]int{}
    r,l:=0,0
    for r<len(s){
        ms[s[r]]++
        if mt[s[r]]!=0&&ms[s[r]]==mt[s[r]]{n--}
        r++
        for l<r&&n==0{
            if r-l<ans{
                ans=r-l
                ansl=l
                ansr=r
            }
            ms[s[l]]--
            if mt[s[l]]!=0&&ms[s[l]]==mt[s[l]]-1{n++}
            l++
        }
    }
    return s[ansl:ansr]
}

78. 子集

78. 子集

回溯

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func subsets(nums []int) [][]int {
    l:=len(nums)
    onpath:=make([]int,0,l)

    ans:=[][]int{}
    var backtrack func(i int)
    backtrack=func(i int){
        if i==l{
            tmp:=make([]int,len(onpath))
            copy(tmp,onpath)
            ans=append(ans,tmp)
            return
        }
        backtrack(i+1)
        onpath=append(onpath,nums[i])
        backtrack(i+1)
        onpath=onpath[:len(onpath)-1]
    }
    backtrack(0)
    return ans
}

79. 单词搜索

79. 单词搜索

回溯剪枝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
func exist(board [][]byte, word string) bool {
    m,n,l:=len(board),len(board[0]),len(word)
    visited:=make([][]bool,m)
    for i:=range visited{
        visited[i]=make([]bool,n)
    }

    ans:=false
    var backtrack func(i,j,k int)
    backtrack=func(i,j,k int){
        if ans{return}
        if k==l{
            ans=true
            return
        }
        if i<0||i>=m||j<0||j>=n{return}
        if visited[i][j]{return}
        if board[i][j]!=word[k]{
            return
        }else{
            visited[i][j]=true
            for _,v:=range dir{
                backtrack(i+v[0],j+v[1],k+1)
            }
            visited[i][j]=false
        }
    }
    for i:=0;i<m;i++{
        for j:=0;j<n;j++{
            backtrack(i,j,0)
        }
    }
    return ans
}

var dir=[][]int{
    {1,0},
    {0,1},
    {-1,0},
    {0,-1},
}

84. 柱状图中最大的矩形

84. 柱状图中最大的矩形

单调栈,维护单调递增,存索引,遇到索引i对应值v小于顶部元素索引对应值s[len(s)-1]时,弹出顶部元素,可以确定顶部元素索引值为高h的最大矩形,左端点l为该元素索引前一个元素索引+1,如果没有前一个元素索引则l为0,右端点为x-1。

所以面积为h*(r-l+1)

遍历完后将计算面积的逻辑再调用一遍,不过此时r为数组长度lh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func largestRectangleArea(heights []int) int {
    lh:=len(heights)
    s:=make([]int,0,lh)
    ans:=math.MinInt

    for i,v:=range heights{
        r:=i-1
        for len(s)>0&&heights[s[len(s)-1]]>=v{
            l:=0
            if len(s)>1{
                l=s[len(s)-2]+1
            }
            h:=heights[s[len(s)-1]]
            tmp:=h*(r-l+1)
            if ans<tmp{ans=tmp}
            s=s[:len(s)-1]
        }
        s=append(s,i)
    }
    r:=lh-1
    for len(s)>0{
        l:=0
        if len(s)>1{
            l=s[len(s)-2]+1
        }
        h:=heights[s[len(s)-1]]
        tmp:=h*(r-l+1)
        if ans<tmp{ans=tmp}
        s=s[:len(s)-1]
    }
    
    return ans
}

代码复用优化后

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func largestRectangleArea(heights []int) int {
    lh:=len(heights)
    s:=make([]int,0,lh)
    ans:=math.MinInt

    countmianji:=func(r int){
        l:=0
        if len(s)>1{
            l=s[len(s)-2]+1
        }
        h:=heights[s[len(s)-1]]
        tmp:=h*(r-l+1)
        if ans<tmp{ans=tmp}
    }
    for i,v:=range heights{
        r:=i-1
        for len(s)>0&&heights[s[len(s)-1]]>=v{
            countmianji(r)
            s=s[:len(s)-1]
        }
        s=append(s,i)
    }
    r:=lh-1
    for len(s)>0{
        countmianji(r)
        s=s[:len(s)-1]
    }
    
    return ans
}

85. 最大矩形

85. 最大矩形

记录每个元素往右最大能延申多少个1,然后暴力检查每个元素能往上下扩多少个宽度并计算最大矩形

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
func maximalRectangle(matrix [][]byte) int {
    m:=len(matrix)
    n:=len(matrix[0])
    vertical:=make([][]int,m)
    for i:=range vertical{
        vertical[i]=make([]int,n)
    }
    // for i,v:=range matrix{
    //     fmt.Println(i,v)
    // }
    for i:=range matrix{
        l,r:=0,0
        for r<n{
            if matrix[i][r]=='0'{
                for l<r{
                    vertical[i][l]=r-l
                    l++
                }
                r=r+1
                l=r
            }else{
                r++
            }
        }
        for l<r{
            vertical[i][l]=r-l
            l++
        }
    }
    // for i,v:=range vertical{
    //     fmt.Println(i,v)
    // }

    ans:=0
    for i:=range vertical{
        for j:=range vertical[i]{
            width:=1
            lenth:= vertical[i][j]
            for k:=i-1;k>=0;k--{
                if vertical[k][j]>=lenth{
                    width++
                }else{break}
            }
            for k:=i+1;k<m;k++{
                if vertical[k][j]>=lenth{
                    width++
                }else{break}
            }
            tmp:=width*lenth
            if ans<tmp{ans=tmp}
        }
    }
    return ans
}

另一种方法,单调栈,其实就是将84. 柱状图中最大的矩形的方法用于计算面积,比较复杂,详见官网题解

94. 二叉树的中序遍历

94. 二叉树的中序遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func inorderTraversal(root *TreeNode) []int {
    ans:=[]int{}
    var dfs func(root *TreeNode)
    dfs=func(root *TreeNode){
        if root==nil{return}
        dfs(root.Left)
        ans=append(ans,root.Val)
        dfs(root.Right)
    }
    dfs(root)
    return ans
}

101. 对称二叉树

101. 对称二叉树

a和b对称的意思是a和b的值相同,且a的左(右)子树和b的右(左)子树也对称

所以可以通过这个递归关系写出递归函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func isSymmetric(root *TreeNode) bool {
    var isok func(a,b *TreeNode)bool
    isok=func(a,b *TreeNode)bool{
        if a==nil||b==nil{
            if a==nil&&b==nil{return true}
            return false
        }
        if a.Val!=b.Val{
            return false
        }
        t1:=isok(a.Left,b.Right)
        t2:=isok(a.Right,b.Left)
        return t1&&t2
    }
    return isok(root,root)
}

104. 二叉树的最大深度

104. 二叉树的最大深度

树形DP,左子树和右子树的最大深度为t1和t2,二叉树的最大深度为max(t1,t2)+1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func maxDepth(root *TreeNode) int {
    if root==nil{return 0}
    t1:=maxDepth(root.Left)
    t2:=maxDepth(root.Right)
    if t1<t2{t1=t2}
    return t1+1
}

105. 从前序与中序遍历序列构造二叉树

105. 从前序与中序遍历序列构造二叉树

递归,先构造跟节点再构造左右子树

先序遍历:[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ] 中序遍历:[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func buildTree(preorder []int, inorder []int) *TreeNode {
    l:=len(preorder)
    if l==0{return nil}
    root:=&TreeNode{Val:preorder[0]}
    var mid int
    for i,v:=range inorder{
        if v==preorder[0]{
            mid=i
            break
        }
    }
    root.Left=buildTree(preorder[1:mid+1],inorder[:mid])
    root.Right=buildTree(preorder[mid+1:],inorder[mid+1:])
    return root
}

121. 买卖股票的最佳时机

121. 买卖股票的最佳时机

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func maxProfit(prices []int) int {
    li,lj,lk:=len(prices),2,2
    dp:=make([][][]int,li)
    for i:=range dp{
        dp[i]=make([][]int,lj)
        for j:=range dp[i]{
            dp[i][j]=make([]int,lk)
        }
    }

    for i:=0;i<li;i++{
        dp[i][1][lk-1]=-prices[i]
    }

    ans:=0
    for i:=1;i<li;i++{
        for k:=lk-1;k>=0;k--{
            if k+1<lk{
                dp[i][0][k]=Max(dp[i-1][1][k+1]+prices[i],dp[i-1][0][k])
            }
            dp[i][1][k]=Max(dp[i-1][0][k]-prices[i],dp[i-1][1][k])
            if ans<dp[i][0][k]{ans=dp[i][0][k]}
        }
    }

    // for i,v:=range dp{
    //     fmt.Println(i,v)
    // }

    return ans
}

func Max(a,b int)int{
    if a<b{return b}
    return a
}

124. 二叉树中的最大路径和

124. 二叉树中的最大路径和

树形DP,记录根节点向下走的最大路径,动态遍历的时候计算最大路径和

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func maxPathSum(root *TreeNode) int {
    ans:=math.MinInt
    var dfs func(root *TreeNode)int
    dfs=func(root *TreeNode)int{
        if root==nil{return -1001}
        l,r:=dfs(root.Left),dfs(root.Right)
        max:=Max(root.Val,root.Val+l,root.Val+r)
        maxl:=Max(max,l+r+root.Val)
        if ans<maxl{ans=maxl}
        return max
    }
    dfs(root)
    return ans
}

func Max(s ...int)int{
    tmp:=math.MinInt
    for _,v:=range s{
        if tmp<v{tmp=v}
    }
    return tmp
}

128. 最长连续序列

128. 最长连续序列

hash表实现O(1)查询数字

查询只从没有x-1的x元素开始,分摊查询成本为O(N)

时间复杂度为:hash表构建O(N),遍历元素O(N),分摊查询O(N),所以加起来O(3N)=O(N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func longestConsecutive(nums []int) int {
    set := map[int]bool{}
    for _, num := range nums {
        set[num] = true
    }
    max := 0
    for num := range set {
        if !set[num-1] {
            tmp := num
            curmax := 1
            for set[tmp+1] {
                tmp++
                curmax++
            }
            if max < curmax {
                max = curmax
            }
        }
    }
    return max
}

136. 只出现一次的数字

136. 只出现一次的数字

位运算

1
2
3
4
5
6
7
func singleNumber(nums []int) int {
    ans:=0
    for _,v:=range nums{
        ans^=v
    }
    return ans
}

139. 单词拆分

139. 单词拆分

哈希表m预处理存储字符串实现O(1)查找

dp[i]表示s[:i]是否能被分解

dp[i]=OR(m[s[ j:i ]&&dp[j-1]]),j<=i

basecase:dp[-1]=true,添加条件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func wordBreak(s string, wordDict []string) bool {
    l:=len(s)
    m:=make(map[string]bool,l)
    for _,ss:=range wordDict{
        m[ss]=true
    }

    //fmt.Println(m)

    dp:=make([]bool,l)
    for i:=range dp{
        for j:=range dp[:i+1]{
            if dp[i]{break}
            if m[s[j:i+1]]{
                if j==0||dp[j-1]{
                    dp[i]=true
                }
            }
        }
    }

    //fmt.Println(dp)

    return dp[l-1]
}

10. 正则表达式匹配

10. 正则表达式匹配

我这里用的回溯+备忘录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func isMatch(s string, p string) bool {
    ls,lp:=len(s),len(p)
    visited:=make([][]bool,ls)
    for i:=range visited{
        visited[i]=make([]bool,lp)
    }
    
    ans:=false
    var backtrack func(i,j int)
    backtrack=func(i,j int){
        if ans{return}
        //fmt.Println("s:",i,s[:i],"|",s[i:],"p:",j,p[:j],"|",p[j:])
        if i>=ls||j>=lp{
            if i==ls{
                if j==lp{
                    ans=true
                }else if j+1<lp&&p[j+1]=='*'{
                    backtrack(i,j+2)
                }
            }
            return
        }
        if visited[i][j]{return}
        visited[i][j]=true
        if j+1<lp&&p[j+1]=='*'{
            backtrack(i,j+2)
            if s[i]==p[j]||p[j]=='.'{
                backtrack(i+1,j)
            }
        }else{
            if s[i]==p[j]||p[j]=='.'{
                backtrack(i+1,j+1)
            }
        }
    }
    backtrack(0,0)
    return ans
}

另外官方题解用的动态规划,跟着写了一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func isMatch(s string, p string) bool {
    ls,lp:=len(s),len(p)
    // fmt.Println("ls:",ls,"lp:",lp)
    dp:=make([][]bool,ls+1)
    for i:=range dp{
        dp[i]=make([]bool,lp+1)
    }

    dp[0][0]=true
    for j:=1;j<=lp;j++{
        if p[j-1]=='*'{
            dp[0][j]=dp[0][j]||dp[0][j-2]
        }
    }

    for i:=1;i<=ls;i++{
        for j:=1;j<=lp;j++{
            if s[i-1]==p[j-1]||p[j-1]=='.'{
                dp[i][j] = dp[i - 1][j - 1]
            }else if p[j-1]=='*'{
                if s[i-1]==p[j-2]||p[j-2]=='.'{
                    dp[i][j]=dp[i][j-2]||dp[i-1][j-2]||dp[i-1][j]
                }else{
                    dp[i][j]=dp[i][j-2]
                }
            }
        }
    }

    for i,v:=range dp{
        fmt.Println(i,v)
    }

    return dp[ls][lp]
}

206. 反转链表

206. 反转链表

模拟,固定框架

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseList(head *ListNode) *ListNode {
    var prev,cur,next *ListNode=nil,head,nil
    for cur!=nil{
        next=cur.Next
        cur.Next=prev
        prev=cur
        cur=next
    }
    return prev
}

239. 滑动窗口最大值

239. 滑动窗口最大值

剑指 Offer 59 - I. 滑动窗口的最大值

297. 二叉树的序列化与反序列化

297. 二叉树的序列化与反序列化

存入带nil的先序遍历结果即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type Codec struct{
    s []int
}

func Constructor() Codec {
    return Codec{}
}

// Serializes a tree to a single string.
func (this *Codec) serialize(root *TreeNode) string {
    this.s=[]int{}
    var dfs func(root *TreeNode)
    dfs=func(root *TreeNode){
        if root==nil{
            this.s=append(this.s,-1001)
            return
        }
        this.s=append(this.s,root.Val)
        dfs(root.Left)
        dfs(root.Right)
    }
    dfs(root)
    ret,_:=json.Marshal(this.s)
    return string(ret)
}

// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {
    i:=0
    b:=[]byte(data)
    json.Unmarshal(b,&this.s)
    var dfs func()*TreeNode
    dfs=func()*TreeNode{
        if i>=len(this.s){
            return nil
        }
        var root *TreeNode
        if this.s[i]>-1001{
            root=&TreeNode{Val:this.s[i]}
            i++
            root.Left=dfs()
            root.Right=dfs()
        }else{
            root=nil
            i++
        }
        return root
    }
    return dfs()
}


/**
 * Your Codec object will be instantiated and called as such:
 * ser := Constructor();
 * deser := Constructor();
 * data := ser.serialize(root);
 * ans := deser.deserialize(data);
 */

另外由于encoding/json包会自动对指针解引用,所以可以直接用Marshal函数和Unmarshal函数直接莽

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type Codec struct{}

func Constructor() Codec {
    return Codec{}
}

// Serializes a tree to a single string.
func (this *Codec) serialize(root *TreeNode) string {
    ret,_:=json.Marshal(root)
    return string(ret)
}

// Deserializes your encoded data to tree.
func (this *Codec) deserialize(data string) *TreeNode {
    var root *TreeNode
    json.Unmarshal([]byte(data),&root)
    return root
}


/**
 * Your Codec object will be instantiated and called as such:
 * ser := Constructor();
 * deser := Constructor();
 * data := ser.serialize(root);
 * ans := deser.deserialize(data);
 */

301. 删除无效的括号

301. 删除无效的括号

暴力回溯,选择删和不删

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func removeInvalidParentheses(s string) []string {
    m:=map[string]bool{}

    count:=0
    maxlen:=0
    var backtrack func(i int,ss string)
    backtrack=func(i int,ss string){
        //fmt.Println(ss,i,count)
        if i==len(s){
            if count==0&&!m[ss]{
                if maxlen<len(ss){maxlen=len(ss)}
                m[ss]=true
            }
            return
        }
        si:=string(s[i])
        if s[i]!='('&&s[i]!=')'{
            backtrack(i+1,ss+si)
        }else{
            if s[i]=='('{
                backtrack(i+1,ss)
                count++
                backtrack(i+1,ss+si)
                count--
            }else if s[i]==')'{
                backtrack(i+1,ss)
                if count>0{
                    count--
                    backtrack(i+1,ss+si)
                    count++
                }
            }
        }
    }
    backtrack(0,"")
    ans:=[]string{}
    for k,_:=range m{
        if maxlen==len(k){
            ans=append(ans,k)
        }
    }
    if len(ans)==0{ans=append(ans,"")}
    return ans
}

func Min(a,b int)int{
    if a<b{return a}
    return b
}

312. 戳气球

312. 戳气球

动态规划

这里状态定义很巧妙,因为戳破气球两边的气球在后续状态仍然需要使用,所以无法直接将dp[i][j]设置为[i,j]区间上最大可以取得的硬币数量

dp[i][j]表示(i,j)区间上最大可以取得的硬币数量

1
2
dp[i][j]=Max(dp[i][k]+dp[k][j]+num[i] * num[k] * num[j]),k∈(i,j)&&i < j+1
         0,i>=j+1

然后这里还有更巧妙的是在数组两端分别添加了一个1,类似于添加虚拟节点,这样就把边界条件略去,使得解题逻辑统一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func maxCoins(nums []int) int {
    l:=len(nums)
    nums2:=make([]int,l+2)
    nums2[0]=1
    nums2[l+1]=1
    copy(nums2[1:l+1],nums)
    dp:=make([][]int,l+2)
    for i:=range dp{
        dp[i]=make([]int,l+2)
    }
    for i:=l+1;i>=0;i--{
        for j:=i+2;j<l+2;j++{
            for k:=i+1;k<j;k++{
                dp[i][j]=Max(dp[i][j],dp[i][k]+dp[k][j]+nums2[i]*nums2[k]*nums2[j])
            }
        }
    }
    return dp[0][l+1]
}

func Max(a,b int)int{
    if a<b{return b}
    return a
}

739. 每日温度

739. 每日温度

单调栈

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func dailyTemperatures(temperatures []int) []int {
    l:=len(temperatures)
    stack:=make([]int,0,l)
    ans:=make([]int,l)
    for r:=0;r<l;r++{
        for len(stack)>0{
            t:=stack[len(stack)-1]
            if temperatures[t]<temperatures[r]{
                stack=stack[:len(stack)-1]
                ans[t]=r-t
            }else{break}
        }
        stack=append(stack,r)
    }
    return ans
}

142. 环形链表 II

142. 环形链表 II

双指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func detectCycle(head *ListNode) *ListNode {
    if head==nil||head.Next==nil{return nil}
    dummy:=&ListNode{Next:head}
    slow,fast:=dummy.Next,dummy.Next.Next
    for slow!=fast{
        if slow==nil||fast==nil||fast.Next==nil{return nil}
        slow=slow.Next
        fast=fast.Next.Next
    }
    entry:=dummy
    for entry!=slow{
        entry=entry.Next
        slow=slow.Next
    }
    return entry
}

148. 排序链表

148. 排序链表

归并排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func sortList(head *ListNode) *ListNode {
    return merge(head)
}

func merge(head *ListNode)*ListNode{
    if head==nil||head.Next==nil{return head}
    dummy:=&ListNode{Next:head}
    slow,fast:=dummy,dummy
    for fast!=nil&&fast.Next!=nil{
        slow=slow.Next
        fast=fast.Next.Next
    }
    l1,l2:=head,slow.Next
    slow.Next=nil
    a:=merge(l1)
    b:=merge(l2)
    return mergesort(a,b)
}

func mergesort(a,b *ListNode) *ListNode{
    dummy:=&ListNode{}
    cur:=dummy
    for a!=nil&&b!=nil{
        if a.Val>b.Val{
            cur.Next=b
            b=b.Next
        }else{
            cur.Next=a
            a=a.Next
        }
        cur=cur.Next
    }
    if a!=nil{
        cur.Next=a
    }
    if b!=nil{
        cur.Next=b
    }
    return dummy.Next
}

155. 最小栈

155. 最小栈

数组作为栈,优先队列查询最大值,hash表记录栈内元素,优先队列弹出元素时将所有不在栈内的顶部元素都弹出来维护最小值O(1)查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
type PriorityQueue struct {
	sort.IntSlice
}

func (h *PriorityQueue) Push(x interface{}) {
	h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *PriorityQueue) Pop() interface{} {
	l := len(h.IntSlice)
	ret := h.IntSlice[l-1]
	h.IntSlice = h.IntSlice[:l-1]
	return ret
}

func (h *PriorityQueue) HPush(x int) { heap.Push(h, x) }

func (h *PriorityQueue) HPop() int { return heap.Pop(h).(int) }

type MinStack struct {
    k int
    m map[int]int
    stack []int
    min PriorityQueue
}


func Constructor() MinStack {
    return MinStack{0,map[int]int{},[]int{},PriorityQueue{}}
}


func (this *MinStack) Push(val int)  {
    this.k++
    this.m[val]++
    this.stack=append(this.stack,val)
    this.min.HPush(val)
}


func (this *MinStack) Pop()  {
    if this.k==0{return}
    tmp:=this.Top()
    this.m[tmp]--
    this.stack=this.stack[:this.k-1]
    this.k--
    for this.min.Len()!=0&&this.m[this.min.IntSlice[0]]==0{
        this.min.HPop()
    }
}


func (this *MinStack) Top() int {
    return this.stack[this.k-1]
}


func (this *MinStack) GetMin() int {
    return this.min.IntSlice[0]
}


/**
 * Your MinStack object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Push(val);
 * obj.Pop();
 * param_3 := obj.Top();
 * param_4 := obj.GetMin();
 */

想复杂了,栈已经能够满足push,pop和top操作了,要常数时间检索最小元素,那么我们只需要记录任何时候最小元素即可,由于栈是先进后出,所以我们只需要添加一个辅助栈,跟原来的栈一起压栈出栈但是对象是此时的最小元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
type MinStack struct {
    stack []int
    minStack []int
}

func Constructor() MinStack {
    return MinStack{
        stack: []int{},
        minStack: []int{math.MaxInt64},
    }
}

func (this *MinStack) Push(x int)  {
    this.stack = append(this.stack, x)
    top := this.minStack[len(this.minStack)-1]
    this.minStack = append(this.minStack, min(x, top))
}

func (this *MinStack) Pop()  {
    this.stack = this.stack[:len(this.stack)-1]
    this.minStack = this.minStack[:len(this.minStack)-1]
}

func (this *MinStack) Top() int {
    return this.stack[len(this.stack)-1]
}

func (this *MinStack) GetMin() int {
    return this.minStack[len(this.minStack)-1]
}

func min(x, y int) int {
    if x < y {
        return x
    }
    return y
}

208. 实现 Trie (前缀树)

208. 实现 Trie (前缀树)

直接用map实现,特殊值#标记单词结束

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
type Trie map[byte]*Trie


func Constructor() Trie {
    return Trie{}
}


func (this *Trie) Insert(word string)  {
    tmp:=this
    for i:=range word{
        if _,has:=(*tmp)[word[i]];!has{(*tmp)[word[i]]=&Trie{}}
        tmp=(*tmp)[word[i]]
    }
    (*tmp)['#']=&Trie{}
}


func (this *Trie) Search(word string) bool {
    tmp:=this
    for i:=range word{
        if _,has:=(*tmp)[word[i]];!has{return false}
        tmp=(*tmp)[word[i]]
    }
    if (*tmp)['#']==nil{return false}
    return true
}


func (this *Trie) StartsWith(prefix string) bool {
    tmp:=this
    for i:=range prefix{
        if _,has:=(*tmp)[prefix[i]];!has{return false}
        tmp=(*tmp)[prefix[i]]
    }
    return true
}


/**
 * Your Trie object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Insert(word);
 * param_2 := obj.Search(word);
 * param_3 := obj.StartsWith(prefix);
 */

215. 数组中的第K个最大元素

215. 数组中的第K个最大元素

小顶堆,O(Nlogk)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func findKthLargest(nums []int, k int) int {
    h:=&PriorityQueue{}
    for _,v:=range nums{
        h.HPush(v,k)
    }
    return h.IntSlice[0]
}

type PriorityQueue struct {
	sort.IntSlice
}

func (h *PriorityQueue) Push(x interface{}) {
	h.IntSlice = append(h.IntSlice, x.(int))
}

func (h *PriorityQueue) Pop() interface{} {
	l := len(h.IntSlice)
	ret := h.IntSlice[l-1]
	h.IntSlice = h.IntSlice[:l-1]
	return ret
}

func (h *PriorityQueue) HPush(x,k int) { 
    heap.Push(h, x)
    for h.Len()>k{
        heap.Pop(h)
    }
}

func (h *PriorityQueue) HPop() int { return heap.Pop(h).(int) }

官方题解给出了随机化+快排,期望时间复杂度为O(N)

快排

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func findKthLargest(nums []int, k int) int {
    partition:=func(l,r int)int{
        tmp:=nums[l]
        for l<r{
            for l<r&&nums[r]<=tmp{
                r--
            }
            nums[l]=nums[r]
            for l<r&&nums[l]>=tmp{
                l++
            }
            nums[r]=nums[l]
        }
        nums[l]=tmp
        return l
    }
    l,r:=0,len(nums)-1
    for l<r{
        //fmt.Println(l,r)
        t:=partition(l,r)
        if t<k-1{
            l=t+1
        }else if t>k-1{
            r=t-1
        }else{
            l=t
            break
        }
    }
    return nums[l]
}

538. 把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树

dfs,右子树中序遍历,全局变量记录从大到小的和

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func convertBST(root *TreeNode) *TreeNode {
    tmp:=0
    var dfs func(root *TreeNode)
    dfs=func(root *TreeNode){
        if root==nil{return}
        dfs(root.Right)
        tmp+=root.Val
        root.Val=tmp
        dfs(root.Left)
    }

    dfs(root)
    return root
}

560. 和为 K 的子数组

560. 和为 K 的子数组

子数组,求和,直接前缀和加哈希表,不加哈希表也能过

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func subarraySum(nums []int, k int) int {
    l:=len(nums)
    preSum:=make([]int,l+1)
    for i,v:=range nums{
        preSum[i+1]=v+preSum[i]
    }
    //fmt.Println(preSum)
    ans:=0
    m:=map[int]int{}
    for _,v:=range preSum{
        ans+=m[v-k]
        m[v]++
    }
    return ans
}

581. 最短无序连续子数组

581. 最短无序连续子数组

排序后比对

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func findUnsortedSubarray(nums []int) int {
    tmp:=make([]int,len(nums))
    copy(tmp,nums)
    sort.Ints(tmp)
    l:=0
    for l<len(nums)&&tmp[l]==nums[l]{
        l++
    }
    if l==len(nums){return 0}
    r:=len(nums)-1
    for r>0&&tmp[r]==nums[r]{
        r--
    }
    return r-l+1
}

官网还有个一次遍历法,从左往右遍历保存最大值r,从右往左遍历保存最小值l,最后的区间是[l,r]

221. 最大正方形

221. 最大正方形

动态规划,dp[i][j]表示以matrix[i-1][j-1]为右下角的最大正方形

1
2
dp[i][j]=Min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1,matrix[i][j]=='1'
         0,otherwise

这里用了虚拟行列解决边界问题

basecase:dp[…][0]=dp[0][…]=0

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func maximalSquare(matrix [][]byte) int {
    m,n:=len(matrix),len(matrix[0])
    dp:=make([][]int,m+1)
    for i:=range dp{
        dp[i]=make([]int,n+1)
    }
    ans:=0
    for i:=1;i<=m;i++{
        for j:=1;j<=n;j++{
            if matrix[i-1][j-1]=='1'{
                dp[i][j]=Min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1
                if ans<dp[i][j]{
                    ans=dp[i][j]
                }
            }
        }
    }
    //for i,v:=range dp{
    //    fmt.Println(i,v)
    //}
    return ans*ans
}

func Min(s ...int)int{
    tmp:=math.MaxInt
    for _,v:=range s{
        if tmp>v{tmp=v}
    }
    return tmp
}

240. 搜索二维矩阵 II

240. 搜索二维矩阵 II

从左下角或者右上角开始搜索,可以看成一颗搜索二叉树,贪心策略,对于matrix[i][j],大于target时i–,小于target时j++

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func searchMatrix(matrix [][]int, target int) bool {
    m,n:=len(matrix),len(matrix[0])
    ans:=false
    i,j:=m-1,0
    for i>=0&&j<n{
        if matrix[i][j]>target{
            i--
        }else if matrix[i][j]<target{
            j++
        }else{
            ans=true
            break
        }
    }
    return ans
}

287. 寻找重复数

287. 寻找重复数

填坑法,将下标和值对应

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func findDuplicate(nums []int) int {
    n:=len(nums)
    swap:=func(i,j int){
        nums[i],nums[j]=nums[j],nums[i]
    }
    for i:=0;i<n;i++{
        for nums[i]!=i+1{
            if nums[nums[i]-1]==nums[i]{return nums[i]}
            swap(i,nums[i]-1)
        }
    }
    return -1
}

还有一种解法是把数组看作是一个链表,直接用求链表环入口的方法就行

347. 前 K 个高频元素

347. 前 K 个高频元素

我这里直接哈希表统计加排序,时间复杂度为O(NlogN),可以用优先队列(二叉堆)优化到O(Nlogk)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func topKFrequent(nums []int, k int) []int {
    m:=map[int]int{}
    for _,v:=range nums{
        m[v]++
    }
    ans:=make([]int,0,len(m))
    for key,_:=range m{
        ans=append(ans,key)
    }
    sort.Slice(ans,func(i,j int)bool{
        return m[ans[i]]>m[ans[j]]
    })
    return ans[:k]
}

322. 零钱兑换

322. 零钱兑换

动态规划

dp[i]表示金额为i最少可以用多少种表示

dp[i]=Min(dp[i-v])+1,v为硬币取值

basecase:dp[0]=0

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func coinChange(coins []int, amount int) int {
    dp:=make([]int,amount+1)
    for i:=range dp{dp[i]=math.MaxInt}
    dp[0]=0
    for i:=1;i<=amount;i++{
        for _,v:=range coins{
            t:=i-v
            if t>=0&&dp[t]!=math.MaxInt{
                tt:=dp[t]+1
                if dp[i]>tt{dp[i]=tt}
            }
        }
    }
    if dp[amount]==math.MaxInt{return -1}
    return dp[amount]
}

还可以使用动态规划

先获取所有数的和sum,然后求出sum与目标和target的差diff,然后问题就转化为取和为diff的数,将他们取负值,即将数放到容量为diff的背包问题

dp[i][j]表示nums[:i+1]中取出和为j的数有多少种

1
dp[i][j]=dp[i-1][j-nums[i]]

basecase:dp[0][nums[0]]=1

647. 回文子串

647. 回文子串

枚举中心点索引i,分两种情况向两边扩展,以i为直接中心点和以i和i+1为中心点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func countSubstrings(s string) int {
    ans:=0
    n:=len(s)
    for i:=range s{
        i1,j1:=i,i
        for i1>=0&&j1<n{
            if s[i1]==s[j1]{
                ans++
                i1--
                j1++
            }else{break}
        }
        i2,j2:=i,i+1
        for i2>=0&&j2<n{
            if s[i2]==s[j2]{
                ans++
                i2--
                j2++
            }else{break}
        }
    }
    return ans
}

309. 最佳买卖股票时机含冷冻期

309. 最佳买卖股票时机含冷冻期

动态规划

dp[i][j]表示第i天,持有状态为j(0代表不持有,1代表持有,2代表冷冻期)的最大收益

basecase:dp[0][0]=0,dp[0][1]=-prices[0],dp[0][2]=0

选择有买,卖和保持原样

1
2
3
4
5
6
dp[i][0]=Max(dp[i-1][0],dp[i-1][2])
             保持        卖
dp[i][1]=Max(dp[i-1][1],dp[i-1][0]-prices[i])
             保持        买
dp[i][2]=dp[i-1][1]+prices[i]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func maxProfit(prices []int) int {
    n:=len(prices)
    dp:=make([][]int,n)
    for i:=range dp{
        dp[i]=make([]int,3)
    }
    dp[0][1]=-prices[0]
    for i:=1;i<n;i++{
        dp[i][0]=Max(dp[i-1][0],dp[i-1][2])
        dp[i][1]=Max(dp[i-1][0]-prices[i],dp[i-1][1])
        dp[i][2]=dp[i-1][1]+prices[i]
    }
    return Max(dp[n-1][0],dp[n-1][2])
}

func Max(a,b int)int{
    if a<b{return b}
    return a
}

394. 字符串解码

394. 字符串解码

每个[]对为一个层,每个层具有字符串和重复次数,最外面默认存在一个重复次数为1的层

两个栈分别存储字符串和重复次数,分别初始化为[]string{""}[]int{1}

使用tmp来存储下一个重复次数,l和r存储上一次的字符串左右边界

当遇到[时直接将s[l:r+1]添加到当前层的字符串后面,并将重复次数k和空字符串压栈,最后重置tmp,l和r

当遇到]时将s[l:r+1]添加到当前层字符串后面,并将其重复k次添加到上一层的后面,两个栈都弹出一个层,最后重置tmp,l和r

其他时候只需要将r设置为i即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func decodeString(s string) string {
    ss:=[]string{""}
    si:=[]int{1}
    l,r:=0,-1
    tmp:=0
    for i,v:=range s{
        if v=='['{
            ss[len(ss)-1]=ss[len(ss)-1]+s[l:r+1]
            si=append(si,tmp)
            ss=append(ss,"")
            l=i+1
            r=l-1
            tmp=0
            //fmt.Println("[   ss:",ss,"   len(ss):",len(ss),"   si:",si,"   len(si):",len(si))
        }else if v==']'{
            ss[len(ss)-1]=ss[len(ss)-1]+s[l:r+1]
            t:=strings.Repeat(ss[len(ss)-1],si[len(si)-1])
            ss=ss[:len(ss)-1]
            si=si[:len(si)-1]
            ss[len(ss)-1]=ss[len(ss)-1]+t
            l=i+1
            r=l-1
            //fmt.Println("]   ss:",ss,"   len(ss):",len(ss),"   si:",si,"   len(si):",len(si))
        }else if unicode.IsNumber(v){
            tmp*=10
            tmp+=int(v-'0')
        }else{r=i}
    }
    if l<=r{
        ss[len(ss)-1]=ss[len(ss)-1]+s[l:r+1]
    }
    //fmt.Println("last   ss:",ss,"   len(ss):",len(ss),"   si:",si,"   len(si):",len(si))
    return ss[0]
}

399. 除法求值

399. 除法求值

剑指 Offer II 111. 计算除法

416. 分割等和子集

416. 分割等和子集

所有数字之和为sum,转换为装满sum/2的子集背包问题

dp[i][j]表示取nums[:i+1]能否装满容量为j的背包

basecase:dp[…][0]=true

选择是装和不装

1
2
3
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]],j-nums[i]>=0
         不装         装
         dp[i-1][j],j-nums[i]<0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func canPartition(nums []int) bool {
    n:=len(nums)
    sum:=0
    for _,v:=range nums{
        sum+=v
    }
    if sum&1==1{return false}
    target:=sum/2
    dp:=make([][]bool,n+1)
    for i:=range dp{
        dp[i]=make([]bool,target+1)
    }
    for i:=range dp{
        dp[i][0]=true
    }
    for i:=1;i<=n;i++{
        for j:=1;j<=target;j++{
            dp[i][j]=dp[i-1][j]
            tmp:=j-nums[i-1]
            if tmp>=0{
                dp[i][j]=dp[i][j]||dp[i-1][tmp]
            }
        }
    }
    return dp[n][target]
}

优化i维

1
2
dp[j]=dp[j]||dp[j-nums[i]],j-nums[i]>=0
         不装         装

由于这里j需要上一轮左边的数据,所以不能得从右往左遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func canPartition(nums []int) bool {
    n:=len(nums)
    sum:=0
    for _,v:=range nums{
        sum+=v
    }
    if sum&1==1{return false}
    target:=sum/2
    dp:=make([]bool,target+1)
    dp[0]=true
    for i:=1;i<=n;i++{
        for j:=target;j>0;j--{
            tmp:=j-nums[i-1]
            if tmp>=0{
                dp[j]=dp[j]||dp[tmp]
            }
        }
    }
    //fmt.Println(dp)
    return dp[target]
}

438. 找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词

固定长度滑动窗口,使用数组性质优化字母异位词检测

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func findAnagrams(s string, p string) []int {
    if len(s)<len(p){return nil}
    ss:=[26]int{}
    sp:=[26]int{}
    for _,v:=range p{
        sp[v-'a']++
    }
    l,r:=0,0
    for r<len(p){
        ss[s[r]-'a']++
        r++
    }
    ans:=[]int{}
    for r<len(s){
        if ss==sp{ans=append(ans,l)}
        ss[s[r]-'a']++
        ss[s[l]-'a']--
        l++
        r++
    }
    if ss==sp{ans=append(ans,l)}
    return ans
}

437. 路径总和 III

437. 路径总和 III

树形dp,深度优先遍历,返回子树可达的路径和

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func pathSum(root *TreeNode, targetSum int) int {
    ans:=0
    var dfs func(root *TreeNode)[]int
    dfs=func(root *TreeNode)[]int{
        if root==nil{return nil}
        l:=dfs(root.Left)
        r:=dfs(root.Right)
        ret:=make([]int,0,len(l)+len(r)+1)
        for _,v:=range l{
            tmp:=v+root.Val
            if tmp==targetSum{ans++}
            ret=append(ret,tmp)
        }
        for _,v:=range r{
            tmp:=v+root.Val
            if tmp==targetSum{ans++}
            ret=append(ret,tmp)
        }
        if root.Val==targetSum{ans++}
        ret=append(ret,root.Val)
        return ret
    }
    dfs(root)
    return ans
}

406. 根据身高重建队列

406. 根据身高重建队列

先将数组按身高h升序前面更高k降序排序

然后从左往右填入ans数组,cur记录第一个空位,前面有多少个比当前的人更高的就留多少个空位,

这样做的原因就是后填入的不需要考虑先填入的人,因为后面的人一定比前面的人高

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
func reconstructQueue(people [][]int) [][]int {
    sort.Slice(people,func(i,j int)bool{
        if people[i][0]<people[j][0]{
            return true
        }else if people[i][0]==people[j][0]{
            return people[i][1]>people[j][1]
        }else{return false}
    })
    //fmt.Println(people)
    ans:=make([][]int,len(people))
    cur:=0
    for _,v:=range people{
        tmp:=cur
        for i:=0;i<v[1];i++{
            tmp++
            for tmp<len(people)&&ans[tmp]!=nil{tmp++}
        }
        if tmp<len(people){ans[tmp]=v}
        for cur<len(people)&&ans[cur]!=nil{cur++}
    }
    return ans
}

sql

178. 分数排名

178. 分数排名

使用变量,这里注意rank作为关键字不允许使用,需要用rank来标记字段名

1
2
3
4
5
6
7
SELECT score, `rank` FROM 
(SELECT score, 
        cast((case when @prevScore = score then @curRank := @curRank else @curRank := @curRank + 1 end) as SIGNED) `rank`,
        @prevScore := score
    FROM Scores sc, (SELECT @curRank := 0, @prevScore := null) ra
    ORDER BY score DESC
) newscore
 |