1. 编辑距离
// dp数组含义:
// 下标从1开始有意义. dp[0][i] 代表 word1 取前0个字符, word2取前i个字符, 需要的最⼩编辑步骤.
// 遍历顺序:
// 两层循环, word1 的每个字符, word2的每个字符.
// 转移⽅程:
// 1. 如果当前两个字符相等, 那么不需要额外编辑, 当前状态可以由dp[i-1][j-1]得到.
// 2. 如果当前两个字符不等, 那么肯定要编辑(在原状态上+1), 但是原状态我也要找⼀个最⼩的: 插
// ⼊(dp[i-1][j]), 删除(dp[i][j-1], 替换(dp[i-1][j-1])
func minDistance(word1 string, word2 string) int {
len1, len2 := len(word1), len(word2)
dp := make([][]int, len1 + 1)
for i := range dp {
dp[i] = make([]int, len2 + 1)
}
for i := 0; i <= len1; i++ {
dp[i][0] = i
}
for i := 0; i <= len2; i++ {
dp[0][i] = i
}
for i := 0; i < len1; i++ {
for j := 0; j < len2; j++ {
if word1[i] == word2[j] {
dp[i+1][j+1] = dp[i][j]
} else {
dp[i+1][j+1] = min(dp[i][j], min(dp[i][j+1], dp[i+1][j])) + 1
}
}
}
return dp[len1][len2]
}
2. 合并区间
func merge(intervals [][]int) (ans [][]int) {
// 1. 根据起点排序
slices.SortFunc(intervals, func(p, q []int) int {
return p[0] - q[0]
})
// 2. 遍历排序后区间
for _, interval := range intervals {
ansLen := len(ans)
// 3.如果当前区间与答案末尾区间有重叠,则更新答案末尾区间终点
if ansLen > 0 && interval[0] <= ans[ansLen-1][1] {
ans[ansLen-1][1] = max(interval[1], ans[ansLen-1][1])
} else {
// 否则向答案中添加一个新的区间,
// 后续区间一定不会对更早的区间产生影响了,因为已经按照起点排过序了
ans = append(ans, interval)
}
}
return ans
}
3. 滑动窗口最大值
// 一句话:单调队列
func maxSlidingWindow(nums []int, k int) []int {
// 0. 创建答案数组,长度为4的数组,k=3,那么大小为3的区间有2个。
ans := make([]int, len(nums) - k + 1)
// 1. 单调队列,队列中存下标。
q := []int{}
for i, num := range nums {
// 2. 从队尾到队头递增。如果当前元素大于队尾,队尾就弹出。
for len(q) > 0 && num > nums[q[len(q)-1]] {
q = q[:len(q)-1]
}
// 3. 当前元素入队。
q = append(q, i)
// 4. 窗口长度超长了,弹出队头
if i - q[0] > k - 1 {
q = q[1:]
}
// 5. 窗口右端点大于等于k-1,就要开始填写答案
if i >= k - 1 {
ans[i-k+1] = nums[q[0]]
}
}
return ans
}
4. 手撕快速排序
// 一句话:先右后左带等于,闭区间递归
func proc(a []int, l, r int) {
if l >= r {
return
}
pivot := a[l]
// 细节1, left=l 而不是l+1
left, right := l, r
for left < right {
for left < right && a[right] >= pivot {
right--
}
// 细节2: 因为left=l,所以为了让left指针能「起步」,比较的时候要带上等于
for left < right && a[left] <= pivot {
left++
}
a[left], a[right] = a[right], a[left]
// fmt.Println(a)
}
a[l], a[right] = a[right], a[l]
proc(a, l, left-1)
proc(a, left+1, r)
}
5. 岛屿数量
// 一句话:一岛一深搜,陆地变海洋
// 每个岛屿分别dfs,把陆地改成海洋。
func numIslands(grid [][]byte) int {
m, n := len(grid), len(grid[0])
var dfs func(i, j int)
dfs = func(i, j int) {
if i < 0 || i >= m {
return
}
if j < 0 || j >= n {
return
}
if grid[i][j] == '0' {
return
}
grid[i][j] = '0'
dir := [][]int{{0,1},{1,0},{-1,0},{0,-1}}
for idx := 0; idx < 4; idx++ {
dfs(i + dir[idx][0], j + dir[idx][1])
}
}
cnt := 0
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if grid[i][j] == '1' {
cnt++
dfs(i, j)
}
}
}
return cnt
}
6. 三数之和
// 一句话:二三分左右,去重比前者
func threeSum(nums []int) (ans [][]int) {
sort.Ints(nums)
n := len(nums)
for i := 0; i <= n - 3; i++ {
n1 := nums[i]
if i > 0 && nums[i] == nums[i-1] {
continue
}
l, r := i + 1, n - 1
for l < r {
n2, n3 := nums[l], nums[r]
if n1 + n2 + n3 > 0 {
r--
} else if n1 + n2 + n3 < 0 {
l++
} else {
ans = append(ans, []int{n1, n2, n3})
for l < r && nums[l] == n2 {
l++
}
for l < r && nums[r] == n3 {
r--
}
}
}
}
return
}
7. 组合总和2
// 一句话:组合排序标起点,去重比前者
func combinationSum2(candidates []int, target int) (ans [][]int) {
// 排序:应对元素有重复的情况
sort.Ints(candidates)
path := []int{}
// 组合:dfs的for循环i从start开始,而不是0开始(排列)。
var dfs func(a []int, start int, sum int)
dfs = func(a []int, start int, sum int) {
if sum > target {
return
}
if sum == target {
ans = append(ans, append([]int(nil), path...))
return
}
for i := start; i < len(a); i++ {
// 与前一个相同则continue:应对元素有重复的情况
if i > start && a[i] == a[i-1] {
continue
}
path = append(path, a[i])
dfs(a, i + 1, sum + a[i])
path = path[:len(path)-1]
}
}
dfs(candidates, 0, 0)
return
}
8. TOP-K
// 一句话:从底构建堆,弹出k-1次
func findKthLargest(nums []int, k int) int {
n := len(nums)
// 自底向上构建堆
for i := n - 1; i >= 0; i-- {
sink(nums, i, n)
}
// 弹出k-1次,因为第1大的其实就是堆顶,不需要弹出
for i := 0; i < k - 1; i++ {
nums[0], nums[n-1] = nums[n-1], nums[0]
n--
sink(nums, 0, n)
}
// 剩下的堆顶就是答案
return nums[0]
}
// 标准的sink过程
func sink(heap []int, i int, heapSize int) {
for {
l, r := i * 2 + 1, i * 2 + 2
if l >= heapSize {
break
}
larger := l
if r < heapSize && heap[r] > heap[l] {
larger = r
}
if heap[larger] > heap[i] {
heap[larger], heap[i] = heap[i], heap[larger]
i = larger
} else {
break
}
}
}
9. LRU
// 一句话:小写为包大写类,链表 map 和 容量,链表节点是 entry
type LRUCache struct {
l *list.List
cache map[int]*list.Element
cap int
}
type entry struct {
key, value int
}
func Constructor(capacity int) LRUCache {
return LRUCache{
l : list.New(),
cache: make(map[int]*list.Element),
cap: capacity,
}
}
func (this *LRUCache) Get(key int) int {
node, ok := this.cache[key]
if ok {
this.l.MoveToBack(node)
return node.Value.(*entry).value
}
return -1
}
func (this *LRUCache) Put(key int, value int) {
node, ok := this.cache[key]
if ok {
this.l.MoveToBack(node)
node.Value.(*entry).value = value
this.cache[key] = node
return
}
newNode := this.l.PushBack(&entry{key, value})
this.cache[key] = newNode
if this.l.Len() > this.cap {
tmp := this.l.Front()
this.l.Remove(tmp)
delete(this.cache, tmp.Value.(*entry).key)
}
}
10. k个一组反转链表
// 一句话:取得总长度,算出反转数,保存虚假头
func reverseKGroup(head *ListNode, k int) *ListNode {
dummy := &ListNode{
Next: head,
}
var t, pre, cur *ListNode
t, pre, cur = dummy, nil, head
p := head
length := 0
for p != nil {
length++
p = p.Next
}
for i := 0; i < length / k; i++ {
for i := 0; i < k; i++ {
nxt := cur.Next
cur.Next = pre
pre = cur
cur = nxt
}
tmp := t.Next
t.Next.Next = cur
t.Next = pre
t = tmp
}
return dummy.Next
}
11. 最长递增子序列
// 一句话:dp[i]表示以i结尾的最长递增序列长度,要向前遍历
// dp[i] 表示:以nums[i]结尾的最长递增子序列长度
// 递推公式:[0,i-1] 中,找结尾小于nums[i]的最长的子序列,dp[i]在此基础上+1
func lengthOfLIS(nums []int) int {
n := len(nums)
dp := make([]int, n)
ans := 0
for i := 0; i < n; i++ {
maxLen := 0
for j := i - 1; j >= 0; j-- {
if nums[i] > nums[j] {
maxLen = max(maxLen, dp[j])
}
}
dp[i] = maxLen + 1
ans = max(ans, dp[i])
}
return ans
}
12. 最长回文子串
// 一句话:初始化长度为1和2的自串,dp[i][j]表示[i,j]最长回文子串的长度
func longestPalindrome(s string) string {
n := len(s)
dp := make([][]int, n)
for i := range dp {
dp[i] = make([]int, n)
}
ans := s[:1]
for i := 0; i < n; i++ {
dp[i][i] = 1
if i+1 < n && s[i] == s[i+1] {
dp[i][i+1] = 2
ans = s[i:i+2]
}
}
for step := 2; step < n; step++ {
for i := 0; i+step < n; i++ {
j := i + step
// 子问题是否也是回文,靠长度来判断
if s[i] == s[j] && dp[i+1][j-1] == j - 1 - i {
dp[i][j] = dp[i+1][j-1] + 2
ans = s[i:j+1]
} else {
dp[i][j] = 0
}
}
}
return ans
}
13. 相交链表(找交点)
// 一句话:从我家到终点,再从你家出发
func getIntersectionNode(headA, headB *ListNode) *ListNode {
a, b := headA, headB
flagA, flagB := true, true
for {
// 首次为nil,换头
if a == nil && flagA {
a = headB
flagA = false
}
if b == nil && flagB {
b = headA
flagB = false
}
// 二次为nil,不相交
if a == nil || b == nil {
return nil
}
// 相遇,找到交点
if a == b {
break
}
a = a.Next
b = b.Next
}
return a
}
14. 环形链表(找交点)
// 一句话:快慢指针,相遇一次快的回起点,一起走
func detectCycle(head *ListNode) *ListNode {
f, s := head, head
if f == nil || f.Next == nil {
return nil
}
for f != nil && f.Next != nil {
f = f.Next.Next
s = s.Next
if s == f {
break
}
}
if f == nil || f.Next == nil {
return nil
}
f = head
for f != s {
f = f.Next
s = s.Next
}
return f
}
15. 链表倒数第k个元素
// 一句话:前后指针,从假脑袋开始,拉开k的差距,一起走
func removeNthFromEnd(head *ListNode, n int) *ListNode {
d := &ListNode{Next: head}
l, r := d, d
for i := 0; i < n; i++ {
r = r.Next
}
for r.Next != nil {
r = r.Next
l = l.Next
}
l.Next = l.Next.Next
return d.Next
}
16. 二叉树直径
// 一句话:nil是-1根是0,返回最长链,更新直径
func diameterOfBinaryTree(root *TreeNode) (ans int) {
// dfs更新的是「直径」
// dfs返回的是「最长链」
var dfs func(root *TreeNode) int
dfs = func(root *TreeNode) int {
if root == nil {
return -1
}
if root.Left == nil && root.Right == nil {
return 0
}
leftLen := dfs(root.Left)
rightLen := dfs(root.Right)
ans = max(ans, leftLen + rightLen + 2)
return max(leftLen, rightLen) + 1
}
_ = dfs(root)
return
}
17. 二叉树右视图
// 一句话:层序遍历,取右边,外层队列不空,内层队列长度快照
func rightSideView(root *TreeNode) []int {
if root == nil {
return []int{}
}
help := make([][]int, 0)
q := []*TreeNode{root}
for len(q) > 0 {
// 长度快照
lenQ := len(q)
tmp := make([]int, lenQ)
for i := 0; i < lenQ; i++ {
curNode := q[0]
tmp[i] = curNode.Val
q = q[1:]
if curNode.Left != nil {
q = append(q, curNode.Left)
}
if curNode.Right != nil {
q = append(q, curNode.Right)
}
}
help = append(help, tmp)
}
ans := make([]int, len(help))
for i := 0; i < len(help); i++ {
ans[i] = help[i][len(help[i])-1]
}
return ans
}
18. 最近公共祖先
// 一句话:分类讨论。当前为p、q返回当前,递归左右子树。
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
if root == p || root == q || root == nil {
return root
}
left := lowestCommonAncestor(root.Left, p, q)
right := lowestCommonAncestor(root.Right, p, q)
if left != nil && right != nil {
return root
}
if left != nil {
return left
}
return right
}
19. 买股票的最佳时机1
// 一句话:一次卖出机会,不用dp,记录左边最低价格。
func maxProfit(prices []int) int {
lmin := prices[0]
ans := 0
for _, v := range prices {
ans = max(ans, v - lmin)
lmin = min(lmin, v)
}
return ans
}
20. 买股票的最佳时机2
// 一句话:状态机dp,dp[i][0]表示第i天不持有股票,dp[i][1]表示持有
// dp[i][0] 表示 [0:i]天,不持有股票的最大利润
// dp[i][1] 表示 [0:i]天,持有股票的最大利润
// 不持有 = max(前一天不持有, 前一天持有 + 今天价格)
// 持有 = max(前一天持有, 前一天不持有 - 今天价格)
func maxProfit(prices []int) int {
n := len(prices)
dp := make([][2]int, n)
dp[0][0] = 0
dp[0][1] = -prices[0]
for i := 1; i < n; i++ {
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
}
return dp[n-1][0]
}
21. 全排列
// 一句话:元素无重,不可复选,used 记录选过的
func permute(nums []int) (res [][]int) {
n := len(nums)
used := make([]bool, n)
track := []int{}
dfs(nums, track, used, &res)
return
}
func dfs(nums []int, track []int, used []bool, res *[][]int) {
if len(track) == len(nums) {
tmp := make([]int, len(track))
copy(tmp, track)
*res = append(*res, tmp)
return
}
for i, _ := range nums {
if used[i] {
continue
}
track = append(track, nums[i])
used[i] = true
dfs(nums, track, used, res)
track = track[:len(track)-1]
used[i] = false
}
}
22. 无重复的最长子串
// 一句话:滑动窗口,map,枚举右端点
func lengthOfLongestSubstring(s string) int {
w := [128]bool{}
l := 0
ans := 0
for r := 0; r < len(s); r++ {
ch := s[r]
for w[ch] {
w[s[l]] = false
l++
}
w[ch] = true
ans = max(ans, r - l + 1)
}
return ans
}
23. 接雨水
// 一句话:栈顶向右,来大的,弹出栈顶,栈还不空,计算宽高,更新答案
func trap(height []int) int {
st := []int{}
n := len(height)
ans := 0
for i := 0; i < n; i++ {
cur := height[i]
// 新来的高,则弹出栈顶
for len(st) > 0 && cur > height[st[len(st)-1]] {
bottomH := height[st[len(st)-1]]
st = st[:len(st)-1]
// 栈顶下一个存在,则计算水池高,更新答案
if len(st) > 0 {
left := st[len(st)-1]
tall := min(cur, height[left]) - bottomH
width := i - left - 1
ans += tall * width
}
}
st = append(st, i)
}
return ans
}
24. 手撕归并排序
// 一句话:标准merge + 闭区间分治
func sortArray(nums []int) []int {
return proc(nums, 0, len(nums)-1)
}
func proc(nums []int, l, r int) []int {
if l == r {
return nums[l:l+1]
}
mid := (l + r) / 2
left := proc(nums, l, mid)
right := proc(nums, mid+1, r)
return merge(left, right)
}
func merge(a, b []int) []int {
lenA, lenB := len(a), len(b)
c := make([]int, lenA + lenB)
i, j, k := 0, 0, 0
for i < lenA && j < lenB {
if a[i] < b[j] {
c[k] = a[i]
i++
} else {
c[k] = b[j]
j++
}
k++
}
for i < lenA {
c[k] = a[i]
i++
k++
}
for j < lenB {
c[k] = b[j]
j++
k++
}
return c
}
25. 合并k个升序链表
// 一句话:归并 + 闭区间分治
func mergeKLists(lists []*ListNode) *ListNode {
if len(lists) == 0 {
return nil
}
return proc(lists, 0, len(lists)-1)
}
// 闭区间[l, r]
func proc(lists []*ListNode, l, r int) *ListNode {
if l == r {
return lists[l]
}
mid := (l + r) / 2
left, right := proc(lists, l, mid), proc(lists, mid+1, r)
return merge(left, right)
}
func merge(a, b *ListNode) *ListNode {
dummy := new(ListNode)
p := dummy
for a != nil && b != nil {
if a.Val < b.Val {
p.Next = a
a = a.Next
} else {
p.Next = b
b = b.Next
}
p = p.Next
}
for a != nil {
p.Next = a
a = a.Next
p = p.Next
}
for b != nil {
p.Next = b
b = b.Next
p = p.Next
}
return dummy.Next
}
26. 复原IP
27. x的平方根
// 一句话:二分,闭区间,小于等于更新答案
func mySqrt(x int) int {
l := 0
r := x
ans := -1
// 闭区间
for l <= r {
mid := (l + r) / 2
// 小于等于,更新答案和左端点
if mid * mid <= x {
ans = mid
l = mid + 1
} else {
r = mid - 1
}
}
return ans
}
28. 从前序中序构造二叉树
// 一句话:找到根节点,分治,前序长度=0,返回nil
func buildTree(preorder []int, inorder []int) *TreeNode {
if len(preorder) == 0 {
return nil
}
idx := slices.Index(inorder, preorder[0])
leftLen := idx
root := &TreeNode {Val: preorder[0]}
root.Left = buildTree(preorder[1:1+leftLen], inorder[:leftLen])
root.Right = buildTree(preorder[1+leftLen:], inorder[1+leftLen:])
return root
}
29. 栈实现队列
// 一句话v1:2个栈,出队时如果输出栈为空,则把输入栈清空压入输出栈
// 一句话v2:想象你有一串珠子,2个烧杯
type MyQueue struct {
in, out []int
}
func Constructor() MyQueue {
return MyQueue {
in: make([]int, 0),
out: make([]int, 0),
}
}
func (this *MyQueue) Push(x int) {
this.in = append(this.in, x)
}
func (this *MyQueue) Pop() int {
ans := this.Peek()
this.out = this.out[:len(this.out)-1]
return ans
}
func (this *MyQueue) Peek() int {
if len(this.out) == 0 {
// 细节:一定不要一边修改,一边用len作为循环边界
// 应该:清空栈之前,对栈的长度做一次快照
inStkLen := len(this.in)
for i := 0; i < inStkLen; i++ {
tmp := this.in[len(this.in)-1]
this.out = append(this.out, tmp)
this.in = this.in[:len(this.in)-1]
}
}
ans := this.out[len(this.out)-1]
return ans
}
func (this *MyQueue) Empty() bool {
return (len(this.in) == 0) && (len(this.out)==0)
}
30. 队列实现栈
// 一句话:保障后来的元素在队列头部
type MyStack struct {
q []int
}
func Constructor() MyStack {
return MyStack {
q : make([]int, 0),
}
}
// 每次把队列清空,临时保存到tmp中,把后来的元素插入队列,再把之前的队列插入到该元素后面
// 这样就实现了:后来的元素在队列的头部
func (this *MyStack) Push(x int) {
tmp := make([]int, len(this.q))
copy(tmp, this.q)
this.q = []int{x}
lenTmp := len(tmp)
for i := 0; i < lenTmp; i++ {
this.q = append(this.q, tmp[0])
tmp = tmp[1:]
}
}
func (this *MyStack) Pop() int {
ans := this.q[0]
this.q = this.q[1:]
return ans
}
func (this *MyStack) Top() int {
return this.q[0]
}
func (this *MyStack) Empty() bool {
return len(this.q) == 0
}
31. 带缓存的LRU
// 一句话记忆:entry加个ttl,惰性删除过期节点
import (
"container/list"
"fmt"
"time"
)
type entry struct {
key int
value int
expTime time.Time
}
type LRU struct {
cap int
cache map[int]*list.Element
l *list.List
ttl time.Duration
}
func CreateLRU(capacity int, ttl time.Duration) *LRU {
return &LRU{
cap: capacity,
cache: make(map[int]*list.Element),
l: list.New(),
ttl: ttl,
}
}
func (this *LRU) Put(key int, value int) {
node, ok := this.cache[key]
// 存在,更新时间和value,更新位置
if ok {
this.l.MoveToBack(node)
node.Value.(*entry).value = value
node.Value.(*entry).expTime = time.Now().Add(this.ttl)
this.cache[key] = node
return
}
// 新增,添加到队尾
newEntry := &entry{ key, value, time.Now().Add(this.ttl) }
newNode := this.l.PushBack(newEntry)
this.cache[key] = newNode
// 超过容量,删除队头
if this.l.Len() > this.cap {
frontNode := this.l.Front()
this.l.Remove(frontNode)
delete(this.cache, frontNode.Value.(*entry).key)
}
return
}
func (this *LRU) Get(key int) int {
node, ok := this.cache[key]
// 不存在,返回-1
if !ok {
return -1
}
// 存在但过期,返回-1
e := node.Value.(*entry)
if time.Now().After(e.expTime) {
this.l.Remove(node)
delete(this.cache, key)
return -1
}
// 更新位置
this.l.MoveToBack(node)
return e.value
}
32. 最长有效括号
func longestValidParentheses(s string) int {
maxLen := 0
// 栈中元素是下标
st := make([]int, 0)
// 加一个“底”
st = append(st, -1)
for i, ch := range s {
// 左括号入栈
if ch == '(' {
st = append(st, i)
} else {
// 遇到右括号,先出栈,当前最长有效括号就是“底”到当前右括号的距离
st = st[:len(st)-1]
if len(st) > 0 {
length := i - st[len(st)-1]
if length > maxLen {
maxLen = length
}
} else { // 如果栈空了,右括号作为新的“底”
st = append(st, i)
}
}
}
return maxLen
}
33. 反转链表2
反转部分[left, right]
func reverseBetween(head *ListNode, left int, right int) *ListNode {
dummy := &ListNode{Next:head}
p0 := dummy
for i := 0; i < left - 1; i++ {
p0 = p0.Next
}
var pre, cur *ListNode = nil, p0.Next // pre 为nil, 而不是p0
for i := 0; i <= right - left; i++ {
nxt := cur.Next
cur.Next = pre
pre = cur
cur = nxt
}
p0.Next.Next = cur
p0.Next = pre
return dummy.Next // 必须返回dummy.Next 而不是 head
}
34. 有效括号
只是判断括号是否匹配,用栈即可。
func isValid(s string) bool {
stack := make([]byte,0)
for i := 0; i < len(s); i++ {
if s[i] == '(' || s[i] == '{' || s[i] == '[' {
stack = append(stack, s[i])
} else {
switch s[i] {
case ')':
if len(stack) <= 0 || stack[len(stack)-1] != '(' {
return false
} else {
stack = stack[:len(stack)-1]
}
case '}':
if len(stack) <= 0 || stack[len(stack)-1] != '{' {
return false
} else {
stack = stack[:len(stack)-1]
}
case ']':
if len(stack) <= 0 || stack[len(stack)-1] != '[' {
return false
} else {
stack = stack[:len(stack)-1]
}
}
}
}
return len(stack) == 0
}
35. 小于等于N的最大数
// 一句话:
// 参数pass=true,表示最高位没有或者前一位选择了严格小于的数。
// 返回值=false,表示无法找到符合要求的最大数。
// 时间复杂度是O(N*M) N是targetNumber的位数,M是input的长度。
package main
import (
"fmt"
"sort"
"strconv"
)
func main() {
input := []int{5, 7, 8}
targetNumber := 6999
tarNumStr := strconv.Itoa(targetNumber)
ans := -1
sort.Ints(input)
// 递归函数的时间复杂度是O(N*M) N是targetNumber的位数,M是input的长度
var dfs func(idx int, tmp int, input []int, pass bool) bool
dfs = func(idx int, tmp int, input []int, pass bool) bool {
if idx == len(tarNumStr) {
ans = tmp
return true
}
// pass=true 对应最高位没有数或者上一位数严格小于给定值
// 这样就可以直接从当前位开始填入input中最大的
if pass {
return dfs(idx+1, tmp*10+input[len(input)-1], input, true)
} else {
// 上一位有数,而且是等于
// 这样就需要找input中小于或等于当前位的数
digit := int(tarNumStr[idx] - '0')
for j := len(input) - 1; j >= 0; j-- {
if input[j] == digit {
// 细节,如果dfs返回false,会继续寻找下一个数
if dfs(idx+1, tmp*10+input[j], input, false) {
return true
}
} else if input[j] < digit {
if dfs(idx+1, tmp*10+input[j], input, true) {
return true
}
}
}
}
// 没有找到小于或等于当前位的数
// 说明当前位不能填入任何数
// 但是如果当前位是最高位,那么可以填入0
if idx != 0 {
return false
}
return dfs(idx+1, tmp, input, true)
}
_ = dfs(0, 0, input, false)
fmt.Println(ans)
}
36. 搜索旋转排序数组
// 一句话:二分闭区间找最小,二分左闭右开搜索两边,右边的话返回记得加上初始偏移量。
func search(nums []int, target int) int {
minIdx := findMin(nums)
// fmt.Println(minIdx)
l := findTar(nums[:minIdx], target)
if l != -1 {
return l
}
r := findTar(nums[minIdx:], target)
if r != -1 {
return r + minIdx
}
return -1
}
// 返回目标值的下标,没有返回-1
func findTar(a []int, tar int) int {
l, r := 0, len(a)
for l < r {
mid := (l + r) / 2
if a[mid] > tar {
r = mid
} else if a[mid] < tar {
l = mid + 1
} else {
return mid
}
}
return -1
}
// return 最小值的下标,闭区间
func findMin(a []int) int {
l, r := 0, len(a) - 1
for l < r {
mid := (l + r) / 2
if a[mid] < a[r] {
r = mid
} else {
l = mid + 1
}
}
return l
}
37. 对称二叉树
// 一句话:一棵树分成2棵树,a左=b右,a右=b左,a根=b根
func isSymmetric(root *TreeNode) bool {
return dfs(root.Left, root.Right)
}
func dfs(a *TreeNode, b *TreeNode) bool {
if a == nil || b == nil {
return a == b
}
return a.Val == b.Val && dfs(a.Left, b.Right) && dfs(a.Right, b.Left)
}
38. 验证搜索二叉树
// 一句话:限定左右子树范围:左子树为(负无穷,根节点值) 右子树为 (根节点值,正无穷)
func isValidBST(root *TreeNode) bool {
// 此题思路:前序遍历,参数传入合法区间
return dfs(root, math.MinInt, math.MaxInt)
}
func dfs (root *TreeNode, lower, upper int) bool {
if root == nil {
return true
}
x := root.Val
return x > lower && x < upper && dfs(root.Left, lower, x) && dfs(root.Right, x, upper)
}
39. 翻转二叉树
// 一句话:先交换再递归处理,还是先递归处理,再交换,都行。
func invertTree(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
root.Left, root.Right = root.Right, root.Left
invertTree(root.Left)
invertTree(root.Right)
return root
}
40. 二叉树最大路径和
// 一句话:递归返回左右子树中正的最大链路(如果为负,通过返回0来表示不选),枚举每一个节点为拐弯点,更新答案。
func maxPathSum(root *TreeNode) int {
ans := math.MinInt
var dfs func(root *TreeNode) int
dfs = func(root *TreeNode) int {
if root == nil {
return 0
}
l := dfs(root.Left)
r := dfs(root.Right)
ans = max(ans, l + r + root.Val)
sum := max(l,r) + root.Val
if sum < 0 {
sum = 0
}
return sum
}
dfs(root)
return ans
}
41. 下一个排列
// 一句话:从右往左找上升,从右往左找第一个大于,交换,翻转右侧
func nextPermutation(nums []int) {
// 从后往前找第一个上升
n := len(nums)
p := -1
for i := n - 2; i >= 0; i-- {
if nums[i] < nums[i+1] {
p = i
break
}
}
// 如果全局降序,则直接反转
if p == -1 {
slices.Reverse(nums)
return
}
// 从右向左找第一个大于p的
q := -1
for i := n - 1; i > p; i-- {
if nums[i] > nums[p] {
q = i
break
}
}
// 交换
nums[p], nums[q] = nums[q], nums[p]
// 反转右侧
slices.Reverse(nums[p+1:])
}