Leetcode(5)——遍历,并查集,回溯法和二分查找

格式:

题号+题名+简单思路+code



遍历

  • 深度优先遍历和广度优先遍历
  • 很多dfs可以用Union Find解决



T130: 被围绕的区域

  • DFS的写法
func solve(board [][]byte)  {
    lenX:=len(board)
    if lenX==0 {
        return 
    }
    lenY:=len(board[0])
    markerd:=make([][]bool, lenX)
    for i:=0;i<lenX;i++ {
        markerd[i]=make([]bool, lenY)
    }
    for i:=0;i<lenX;i++ {
        for j:=0;j<lenY;j++ {
            if i==0 || i==lenX-1 || j==0 || j==lenY-1 {
                dfs(board, markerd, i, j, lenX, lenY)
            }
        }
    }
    for i:=0;i<lenX;i++ {
        for j:=0;j<lenY;j++ {
            switch board[i][j] {
            case '.':
                board[i][j]='O'
            case 'O':
                board[i][j]='X'
            }
        }
    }
}

func dfs(board [][]byte, markerd [][]bool, i int, j int, lenX int, lenY int) {
    if i==-1 || i==lenX || j==-1 || j==lenY || markerd[i][j] || board[i][j]!='O' {
        return
    }
    markerd[i][j]=true
    board[i][j]='.'
    dfs(board, markerd, i-1, j, lenX, lenY)
    dfs(board, markerd, i+1, j, lenX, lenY)
    dfs(board, markerd, i, j-1, lenX, lenY)
    dfs(board, markerd, i, j+1, lenX, lenY)
}
  • 并查集的写法
  • 注意先把边界上的'O'都链接到god上
type UnionFind struct {
    n int
    parent []int
}

func buildUnionFind(n int) UnionFind {
    parent:=make([]int,n)
    for i:=0;i<n;i++ {
        parent[i]=i
    }
    return UnionFind{n, parent}
}

func (u UnionFind) Find(i int) int {
    tmp:=u.parent
    for tmp[i]!=i {
        tmp[i]=tmp[tmp[i]]
        i=tmp[i]
    }
    return i
}

func (u UnionFind) IsUnion(i int, j int) bool {
    fi:=u.Find(i)
    fj:=u.Find(j)
    return fi==fj
}

func (u *UnionFind) Union(i int, j int, god int) {
    if u.IsUnion(i, j) {
        return
    }
    fi:=u.Find(i)
    fj:=u.Find(j)
    if fi==god {
        u.parent[fj]=fi
    } else {
        u.parent[fi]=fj
    }
    u.n--
}

func solve(board [][]byte)  {
    lenX:=len(board)
    if lenX==0 {
        return
    }
    lenY:=len(board[0])
    unionfind:=buildUnionFind(lenX*lenY+1)
    direction:=[4][2]int{{0,1},{0,-1},{1,0},{-1,0}}
    god:=lenX*lenY
    for i:=0;i<lenX;i++ {
        for j:=0;j<lenY;j++ {
            if board[i][j]=='O' {
                if i==0 || i==lenX-1 || j==0 || j==lenY-1 {
                    unionfind.Union(god,i*lenY+j,god)
                }
            }
        }
    }
    for i:=1;i<lenX-1;i++ {
        for j:=1;j<lenY-1;j++ {
            if board[i][j]=='O' {
                for k:=0;k<len(direction);k++ {
                    ti:=i+direction[k][0]
                    tj:=j+direction[k][1]
                    if board[ti][tj]=='O' {
                        unionfind.Union(i*lenY+j,ti*lenY+tj,god)
                    }
                }
            }
        }
    }

    for i:=0;i<lenX;i++ {
        for j:=0;j<lenY;j++ {
            if board[i][j]=='O' && !unionfind.IsUnion(i*lenY+j,god) {
                board[i][j]='X'
            }
        }
    }
}
  • 并查集的另一道题T990,通过将"x==y"判断为连通,然后验证"x!=y"是否冲突;注意先把关系"=="先输入,再判断



※T463: 岛屿的周长

  • 根据上一行的状态计算下一行的状态
func islandPerimeter(grid [][]int) int {
    m, n:=len(grid), len(grid[0])
    ans:=0
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if grid[i][j]==1 {
                if i>0 && grid[i-1][j]==1 {
                    if j==0 || grid[i][j-1]==0 {
                        ans+=2
                    }
                } else {
                    if j==0 || grid[i][j-1]==0 {
                        ans+=4
                    } else {
                        ans+=2
                    }
                }
            }
        }
    }
    return ans
}



  • DFS求周长
  • 该种方法由于能减少遍历的次数(0的部分可以被剪枝),所以大矩阵上性能会更好一些
  • 参考题解
    在这里插入图片描述
func islandPerimeter(grid [][]int) int {
    m, n:=len(grid), len(grid[0])
    visited:=make([][]bool, m)
    for i:=0;i<m;i++ {
        visited[i]=make([]bool, n)
    }
    ans:=0
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if grid[i][j]==1 {
                ans+=dfs(grid, visited, i, j, m ,n)
            }
            
        }
    }
    return ans
}

func dfs(grid [][]int, visited [][]bool, i int, j int, m int, n int) int {
    if i==-1 || j==-1 || i==m || j==n {
        return 1
    }
    if grid[i][j]==0 {
        return 1
    }
    if visited[i][j] {
        return 0
    }
    visited[i][j]=true
    
    return dfs(grid, visited, i+1, j, m, n)+dfs(grid, visited, i-1, j, m, n)+dfs(grid, visited, i, j+1, m, n)+dfs(grid, visited, i, j-1, m, n)
}




T200: 岛屿数量

  • DFS
  • 计算连通分量
func numIslands(grid [][]byte) int {
    m:=len(grid)
    if m==0 {
        return 0
    }
    n:=len(grid[0])
    visited:=make([][]bool, m)
    for i:=0;i<m;i++ {
        visited[i]=make([]bool, n)
    }
    count:=0
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if grid[i][j]=='1' && !visited[i][j] {
                count++
                dfs(grid, visited, i, j, m, n)
            }
        }
    }
    return count
}

func dfs(grid [][]byte, visited [][]bool, i int, j int, m int, n int) {
    if i==-1 || j==-1 || i==m || j==n || visited[i][j] || grid[i][j]=='0' {
        return
    }
    visited[i][j]=true
    dfs(grid, visited, i+1, j, m, n)
    dfs(grid, visited, i-1, j, m, n)
    dfs(grid, visited, i, j+1, m, n)
    dfs(grid, visited, i, j-1, m, n)
}




T695: 岛屿的最大面积

func maxAreaOfIsland(grid [][]int) int {
    ans:=0
    for i:=0;i<len(grid);i++ {
        for j:=0;j<len(grid[0]);j++ {
            if grid[i][j]==1 {
                num:=dfs(grid, i, j)
                if num>ans {
                    ans=num
                }
            }
        }
    }
    return ans
}

func dfs(grid [][]int, i int, j int) int {
    if i==-1 || j==-1 || i==len(grid) || j==len(grid[0]) || grid[i][j]==0 {
        return 0
    }
    grid[i][j]=0    // 代替了visited数组
    return 1+dfs(grid, i+1, j)+dfs(grid, i-1, j)+dfs(grid, i, j+1)+dfs(grid, i, j-1)
}




剑指12: 矩阵中的路径

  • 对每个点进行DFS
func exist(board [][]byte, word string) bool {
    if len(word)==0 {
        return true
    }
    visited:=make([][]bool, len(board))
    for i:=0;i<len(board);i++ {
        visited[i]=make([]bool, len(board[0]))
    }
    directed:=[4][2]int{{0,1}, {0,-1}, {1,0}, {-1,0}}
    for i:=0;i<len(board);i++ {
        for j:=0;j<len(board[0]);j++ {
            if dfs(i, j, board, word, 0, visited, directed) {
                return true
            }
        }
    }
    return false
}

func dfs(i int, j int, board [][]byte, word string, idx int, visited [][]bool, directed [4][2]int) bool {
    if i==len(board) || i==-1 || j==len(board[0]) || j==-1 || board[i][j]!=word[idx] || visited[i][j] {
        return false
    }
    if idx==len(word)-1 && board[i][j]==word[idx] {
        return true
    }
    visited[i][j]=true
    ans:=false
    for k:=0;k<len(directed);k++ {
        ans=ans || dfs(i+directed[k][0], j+directed[k][1], board, word, idx+1, visited, directed)
        if ans==true {
            return true
        }
    }
    visited[i][j]=false
    return false
}




剑指13: 机器人的运动范围

  • 从(0,0)开始BFS
func movingCount(m int, n int, k int) int {
    visited:=make([][]bool, m)
    for i:=0;i<m;i++ {
        visited[i]=make([]bool, n)
    }
    queue:=[][2]int{{0,0}}
    directed:=[4][2]int{{0,1},{0,-1},{1,0},{-1,0}}
    ans:=0
    visited[0][0]=true
    for len(queue)>0 {
        tmp:=[][2]int{}
        for i:=0;i<len(queue);i++ {
            idx_i, idx_j:=queue[i][0], queue[i][1]
            ans++
            for kk:=0;kk<len(directed);kk++ {
                n_i:=idx_i+directed[kk][0]
                n_j:=idx_j+directed[kk][1]
                if n_i>=0 && n_i<m && n_j>=0 && n_j<n && !visited[n_i][n_j] && isValid(n_i, n_j, k) {
                    visited[n_i][n_j]=true
                    tmp=append(tmp, [2]int{n_i, n_j})
                }
            }
        }
        queue=tmp
    }
    return ans
}

func isValid(m int, n int, k int) bool {
    res:=0
    for m!=0 {
        res+=m%10
        m/=10
    }
    for n!=0 {
        res+=n%10
        n/=10
    }
    return res<=k
}




※T815: 公交路线

  • BFS
  • 节点的定义可以是①以公交路线route为节点,有公共站就相连→多起点BFS;②以公交站台station为节点,又涉及到图的构建及剪枝,以邻节点的传统方式构建图将会超时

  1. ②定义,传统方式,超时
func numBusesToDestination(routes [][]int, S int, T int) int {
    graph:=map[int]map[int]interface{}{}
    for i:=0;i<len(routes);i++ {
        for j:=0;j<len(routes[i]);j++ {
            for k:=j+1;k<len(routes[i]);k++ {
                if _,ok:=graph[routes[i][j]];!ok {
                    graph[routes[i][j]]=make(map[int]interface{})
                }
                if _,ok:=graph[routes[i][k]];!ok {
                    graph[routes[i][k]]=make(map[int]interface{})
                }
                graph[routes[i][j]][routes[i][k]]=nil
                graph[routes[i][k]][routes[i][j]]=nil
            }
        }
    }
    ans:=0
    queue:=[]int{S}
    seen:=map[int]interface{}{}
    seen[S]=nil
    for len(queue)>0 {
        tmp:=[]int{}
        for _,node := range queue {
            if node==T {
                return ans
            }
            for nextNode := range graph[node] {
                if _,ok:=seen[nextNode];!ok {
                    tmp=append(tmp, nextNode)
                    seen[nextNode]=nil
                }
            }
        }
        queue=tmp
        ans++
    }
    return -1
}
  1. ②定义,通过route剪枝

在这里插入图片描述

func numBusesToDestination(routes [][]int, S int, T int) int {
    graph:=map[int]map[int]struct{}{}
    for i:=0;i<len(routes);i++ {
        for j:=0;j<len(routes[i]);j++ {
            if _,ok:=graph[routes[i][j]];!ok {
                graph[routes[i][j]]=make(map[int]struct{})
            }
            graph[routes[i][j]][i]=struct{}{}
        }
    }
    ans:=0
    queue:=[]int{S}
    seen_route:=map[int]struct{}{}
    seen_station:=map[int]struct{}{}
    seen_station[S]=struct{}{}
    for len(queue)>0 {
        tmp:=[]int{}
        for _,node := range queue {
            if node==T {
                return ans
            }
            for nextRoute:=range graph[node] {
                if _,ok:=seen_route[nextRoute];!ok {
                    seen_route[nextRoute]=struct{}{}
                    for _,nextStation:=range routes[nextRoute] {
                        if _,ok:=seen_station[nextStation];!ok {
                            seen_station[nextStation]=struct{}{}
                            tmp=append(tmp, nextStation)
                        }
                    }
                }
            }
        }
        queue=tmp
        ans++
    }
    return -1
}
  1. ①定义,以route为节点
  • 两个无序数组判断相交→集合
  • 两个有序数组判断相交→双指针

在这里插入图片描述

func numBusesToDestination(routes [][]int, S int, T int) int {
    if S==T {
        return 0
    }
    graph:=map[int][]int{}
    for i:=0;i<len(routes);i++ {
        graph[i]=make([]int, 0)
    }
    seen:=map[int]struct{}{}
    target:=map[int]struct{}{}
    for i:=0;i<len(routes);i++ {
        if hasIntersect(routes[i], []int{S}) {
            seen[i]=struct{}{}
        }
        if hasIntersect(routes[i], []int{T}) {
            target[i]=struct{}{}
        }
        for j:=i+1;j<len(routes);j++ {
            if hasIntersect(routes[i], routes[j]) {
                graph[i]=append(graph[i], j)
                graph[j]=append(graph[j], i)
            }
        }
    }
    // 多起点BFS
    queue:=make([]int,len(seen))
    idx:=0
    for i := range seen {
        queue[idx]=i
        idx++
    }
    ans:=1
    for len(queue)>0 {
        tmp:=[]int{}
        for _,node := range queue {
            if _,ok := target[node];ok {
                return ans
            }
            for _,nextNode := range graph[node] {
                if _,ok := seen[nextNode];!ok {
                    seen[nextNode]=struct{}{}
                    tmp=append(tmp, nextNode)
                }
            }
        }
        queue=tmp
        ans++
    }
    return -1
}

func hasIntersect(list1 []int, list2 []int) bool {
    // 前提已排序
    i, j := 0, 0
    for i < len(list1) && j < len(list2) {
        if list1[i] == list2[j] { 
            return true 
        } else if list1[i] < list2[j] {
            i++
        } else { 
            j++ 
        }
    }    
    return false
}




T127: 单词接龙

  • 双向BFS,相当于双起点,当相遇时计算count
  • 注意比较单字符改动从 O ( N × W i d t h ) O(N×Width) O(N×Width) O ( N × 26 ) O(N×26) O(N×26)
class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        HashSet<String> wordSet = new HashSet<>(wordList);
        if (!wordSet.contains(endWord)) {return 0;}
        HashSet<String> memoUp = new HashSet<>();
        HashSet<String> memoDown = new HashSet<>();
        memoUp.add(beginWord);
        memoDown.add(endWord);
        int count = 0;
        ArrayList<String> queueUp = new ArrayList<>();
        ArrayList<String> tmpUp;
        ArrayList<String> queueDown = new ArrayList<>();
        ArrayList<String> tmpDown;
        queueUp.add(beginWord);
        queueDown.add(endWord);
        while (queueUp.size()>0 && queueDown.size()>0) {
            tmpUp = new ArrayList<>();
            tmpDown = new ArrayList<>();
            for (String s:queueUp) {
                if (memoDown.contains(s)) {
                    return count+1;
                }
                for (String word:DiffOne(s, wordSet)) {
                    if (!memoUp.contains(word)) {
                        memoUp.add(word);
                        tmpUp.add(word);
                    }
                }
            }
            count++;
            queueUp = tmpUp;
            for (String s:queueDown) {
                if (memoUp.contains(s)) {
                    return count+1;
                }
                for (String word:DiffOne(s, wordSet)) {
                    if (!memoDown.contains(word)) {
                        memoDown.add(word);
                        tmpDown.add(word);
                    }
                }
            }
            count++;
            queueDown = tmpDown;
        }
        return 0;
    }
    public ArrayList<String> DiffOne(String s, HashSet<String> wordSet) {
        ArrayList<String> ans = new ArrayList<>();
        for (int i=0;i<s.length();i++) {
            for (char c = 'a';c<='z';c++) {
                String newString = s.substring(0, i)+c+s.substring(i+1, s.length());
                if (wordSet.contains(newString)) {
                    ans.add(newString);
                }
            }

        }
        return ans;
    }
}




T542: 01矩阵

  • 多起点BFS,到0的距离最短
  • 原位修改
func updateMatrix(matrix [][]int) [][]int {
    m, n:=len(matrix), len(matrix[0])
    queue:=[][2]int{}
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if matrix[i][j]==0 {
                queue=append(queue, [2]int{i, j})
            } else {
                matrix[i][j]=-1
            }
        }
    }
    directed:=[][2]int{{0,1},{0,-1},{1,0},{-1,0}}
    distance:=0
    for len(queue)>0 {
        for idx := range queue {
            i, j:=queue[idx][0], queue[idx][1]
            matrix[i][j]=distance
        }
        tmp:=[][2]int{}
        for idx := range queue {
            i, j:=queue[idx][0], queue[idx][1]
            for k:=0;k<len(directed);k++ {
                idx_i:=i+directed[k][0]
                idx_j:=j+directed[k][1]
                if idx_i>=0 && idx_j>=0 && idx_i<m && idx_j<n && matrix[idx_i][idx_j]==-1 {
                    tmp=append(tmp, [2]int{idx_i, idx_j})
                }
            }
        }
        queue=tmp
        distance++
    }
    return matrix
}

思考:
类似多起点BFS, 多源最短路径问题→虚拟单源点

熟悉「最短路」的读者应该知道,我们所说的「超级零」实际上就是一个「超级源点」。在最短路问题中,如果我们要求多个源点出发的最短路时,一般我们都会建立一个「超级源点」连向所有的源点,用「超级源点」到终点的最短路等价多个源点到终点的最短路。(来自LeetCode-Solution)

类似的题还有T1162: 地图分析,该题思路一样,但是是到1的距离最短

func maxDistance(grid [][]int) int {
    m, n:=len(grid), len(grid[0])
    queue:=[][2]int{}
    for i:=0;i<m;i++ {
        for j:=0;j<n;j++ {
            if grid[i][j]==1 {
                queue=append(queue, [2]int{i,j})
            } else {
                grid[i][j]=-1
            }
        }
    }
    directed:=[][2]int{{0,1},{0,-1},{1,0},{-1,0}}
    distance:=0
    if len(queue)==m*n {
        return -1
    }
    for len(queue)>0 {
        tmp:=[][2]int{}
        for idx := range queue {
            i,j:=queue[idx][0],queue[idx][1]
            for k:=0;k<len(directed);k++ {
                idx_i, idx_j:=i+directed[k][0], j+directed[k][1]
                if idx_i>=0 && idx_j>=0 && idx_i<m && idx_j<n && grid[idx_i][idx_j]==-1 {
                    grid[idx_i][idx_j]=0
                    tmp=append(tmp, [2]int{idx_i, idx_j})
                }
            }
        }
        queue=tmp
        distance++
    }
    return distance-1
}




T847: 访问所有节点

  • 这个问题等价于最短的一笔画问题,与普通BFS区别在于这里节点可以重复经过
  • 最短路径无权重→BFS
  • 构造节点(state);bit表示已遍历节点
type state struct {    // BFS中的新节点,由于可以重复经过,因此包含
    visited_b int    // 这里采用二进制1的位置记录已经过的节点
    node int    // 当前所处节点位置
}

func shortestPathLength(graph [][]int) int {
    N:=len(graph)
    queue:=[]state{}
    visited:=make([][]bool, 1<<N)    // 二维数组代替哈希表记录已遍历状态
    for i:=0;i<(1<<N);i++ {
        visited[i]=make([]bool, N)
    }
    for i:=0;i<N;i++ {    // 因为任何一个节点都可以作为起点,所以多节点BFS
        queue=append(queue, state{1<<i, i})
        visited[1<<i][i]=true
    }
    distance:=0
    for len(queue)>0 {
        tmp:=[]state{}
        for idx := range queue {
            node:=queue[idx].node
            visited_b:=queue[idx].visited_b
            if visited_b==1<<N-1 {    // 当第一次经过全部节点,返回最小距离
                return distance
            }
            for _,nextNode:=range graph[node] {
                nextVisited_b:=visited_b | (1<<nextNode)    // 更新已遍历节点
                if visited[nextVisited_b][nextNode]==false {
                	visited[nextVisited_b][nextNode]=true
                    tmp=append(tmp, state{nextVisited_b, nextNode})
                }
            }
        }
        queue=tmp
        distance++
    }
    return -1
}




※T301: 删除无效括号

  • 由于要求删除最少数量的括号,所以用BFS
class Solution:
    def removeInvalidParentheses(self, s: str) -> List[str]:
        queue=set([s])    # 由于涉及去重,这里可用Set
        ans=[]
        while len(queue)>0:
            for ss in queue:
                if self.isValid(ss):
                    ans.append(ss)
            if len(ans)>0:
                return ans
            tmp=set()
            for ss in queue:
                for i in range(len(ss)):
                    if ss[i]=="(" or ss[i]==")":
                        nextSS=ss[:i]+ss[i+1:]
                        if nextSS not in tmp:
                            tmp.add(nextSS)
            queue=tmp
        return []

    def isValid(self, s: str) -> bool:
    	"""
    	计算括号匹配,由于只有一种括号,不需要使用Stack记录括号类型
    	"""
        count=0
        for c in s:
            if c=="(":
                count+=1
            elif c==")":
                count-=1
                if count<0:
                    return False
        if count==0:
            return True
        else:
            return False





回溯思想

  1. 终止条件
  2. 选择列表
  3. 更新和撤销



※T46: 全排列

  • 注意切片指针/深拷贝
  • 显示传递选择列表
func permute(nums []int) [][]int {
    curr:=[]int{}
    ans:=[][]int{}
    assist(nums, &curr, &ans)
    return ans
}

func assist(nums []int, curr *[]int, ans *[][]int) {
    if len(nums)==0 {
        tmp:=make([]int, len(*curr))
        copy(tmp, *curr)
        *ans=append(*ans, tmp)
        return
    }
    for i,v := range nums {
        *curr=append(*curr, v)
        next_nums:=make([]int, len(nums)-1)
        for j:=0;j<i;j++ {
            next_nums[j]=nums[j]
        }
        for j:=i+1;j<len(nums);j++ {
            next_nums[j-1]=nums[j]
        }
        assist(next_nums, curr, ans)
        *curr=(*curr)[:len(*curr)-1]
    }
} 
  • 隐式传递选择列表;通过当前路径间接获得
func permute(nums []int) [][]int {
    curr:=[]int{}
    ans:=[][]int{}
    memo:=make(map[int]interface{})
    assist(nums, &curr, &ans, memo)
    return ans
}

func assist(nums []int, curr *[]int, ans *[][]int, memo map[int]interface{}) {
    if len(*curr)==len(nums) {
        tmp:=make([]int, len(*curr))
        copy(tmp, *curr)
        *ans=append(*ans, tmp)
        return
    }
    for _,v := range nums {
        if _,ok:=memo[v];ok {
            continue
        }
        *curr=append(*curr, v)
        memo[v]=nil
        assist(nums, curr, ans, memo)
        *curr=(*curr)[:len(*curr)-1]
        delete(memo, v)
    }
}




T47: 全排列Ⅱ

  • 注意去重条件
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        int[] memo = new int[nums.length];
        List<List<Integer>> ans = new ArrayList<>();
        ArrayList<Integer> curr = new ArrayList<>();
        assist(nums, memo, ans, curr);
        return ans;
    }
    public void assist(int[] nums, int[] memo, List<List<Integer>> ans, ArrayList<Integer> curr) {
        if (curr.size()==nums.length) {
            ans.add(new ArrayList<>(curr));
            return;
        }
        for (int i=0;i<nums.length;i++) {
            if (memo[i]==1 || (i>0 && memo[i-1]==0 && nums[i]==nums[i-1])) {    // 必须加memo[i-1]==0
                continue;
            }
            memo[i]=1;
            curr.add(nums[i]);
            assist(nums, memo, ans, curr);
            memo[i]=0;
            curr.remove(curr.size()-1);
        }
    }
}




剑指38: 字符串的排列

  • 相对全排列需要多考虑去重
  • 利用集合,非最优
class Solution:
    def permutation(self, s: str) -> List[str]:
        ans=set()
        self.assist(s, set(), "", ans)
        return list(ans)
    def assist(self, s, memo, curr, ans):
        if len(curr)==len(s):
            ans.add(curr)
            return
        for i in range(len(s)):
            if i in memo:
                continue
            memo.add(i)
            self.assist(s, memo, curr+s[i], ans)
            memo.remove(i)




※剑指60: n个骰子的点数和

  • 基于N叉树不剪枝, O ( 6 n ) O(6^n) O(6n)时间复杂度
  • 基于背包问题的DP, O ( 6 2 n 2 ) O(6^2n^2) O(62n2)时间复杂度
class Solution:
    def twoSum(self, n: int) -> List[float]:
        res=self.assist(n)
        return [i/pow(6,n) for i in res if i!=0]
    def assist(self, n):
        if n==1:
            return [1 for i in range(6)]
        pre=self.assist(n-1)
        tmp=[0 for i in range(6*n)]
        for i in range(1,len(tmp)+1):
            for j in range(1,7):
                if i-j>0 and i-j-1<len(pre):
                    tmp[i-1]+=pre[i-j-1]
        return tmp




※T39: 组合总和

  • 注意选择列表的去重
import "sort"
func combinationSum(candidates []int, target int) [][]int {
    curr:=[]int{}
    ans:=[][]int{}
    sort.Ints(candidates)
    combinationSumAssist(candidates, 0, 0, target, &curr, &ans)
    return ans
}

func combinationSumAssist(candidates []int, index int, sum int, target int, curr *[]int, ans *[][]int) bool {
    if sum > target {
        return false
    }
    if sum == target {
        tmp:=make([]int, len(*curr))
        copy(tmp, *curr)
        *ans=append(*ans, tmp)
        return true
    }
    for i:=index;i<len(candidates);i++ {
        *curr=append(*curr, candidates[i])
        if !combinationSumAssist(candidates, i, sum+candidates[i], target, curr, ans) {
            *curr=(*curr)[:len(*curr)-1]
            break
        }
        *curr=(*curr)[:len(*curr)-1]
    }
    return true
}




T40: 组合总和Ⅱ

  • 对数组进行排序以避免重复计数
  • 排列问题用visited数组,组合用指针,见T47 全排列Ⅱ
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        List<List<Integer>> ans = new ArrayList<>();
        ArrayList<Integer> curr = new ArrayList<>();
        assist(candidates, curr, 0, 0, target, ans);
        return ans;
    }
    public boolean assist(int[] candidates, ArrayList<Integer> curr, int start, int currSum, int target, List<List<Integer>> ans) {
        if (currSum>target) {
            return true;
        }
        if (currSum==target) {
            ans.add(new ArrayList<>(curr));
            return true;
        }
        for (int i=start;i<candidates.length;i++) {
            if (i>start && candidates[i-1]==candidates[i]) {
                continue;
            }
            curr.add(candidates[i]);
            if (assist(candidates, curr, i+1, currSum+candidates[i], target, ans)) {
                curr.remove(curr.size()-1);
                return false;
            }
            curr.remove(curr.size()-1);
        }
        return false;
    }
}




T784: 字母大小写全排列

  • ^用于大小写转换
import "unicode"
func letterCasePermutation(S string) []string {
    curr:=[]byte{}
    ans:=[]string{}
    if len(S)==0 {
        return ans
    }
    letterCasePermutationAssist(S, 0, &curr, &ans)
    return ans
}

func letterCasePermutationAssist(S string, index int, curr *[]byte, ans *[]string) {
    if index==len(S) {
        *ans=append(*ans, string(*curr))
        return
    }
    if unicode.IsNumber(rune(S[index])) {
        *curr=append(*curr, S[index])
        letterCasePermutationAssist(S, index+1, curr, ans)
    } else {
        *curr=append(*curr, S[index])
        letterCasePermutationAssist(S, index+1, curr, ans)
        tmp :=S[index] ^ ' '
        (*curr)[len(*curr)-1]=tmp
        letterCasePermutationAssist(S, index+1, curr, ans)
    }
    *curr=(*curr)[:len(*curr)-1]
}




T22: 括号生成

func generateParenthesis(n int) []string {
    ans:=[]string{}
    generateParenthesisAssist(&ans, n, "")
    return ans
}

func generateParenthesisAssist(ans *[]string, n int, s string) {
    if !isValid(n, s) {
        return
    } else {
        if len(s)==2*n {
            *ans=append(*ans, s)
            return
        }
    }
    generateParenthesisAssist(ans, n, s+"(")    // 隐式回退
    generateParenthesisAssist(ans, n, s+")")
}

func isValid(n int, s string) bool {
	'''
	与判断括号的问题不同,这里可以在s的长度小于2n时进行剪枝
	'''
    count:=0
    for idx := range s {
        if s[idx]=='(' {
            count++
        } else {
            if count==0 {
                return false
            }
            count--
        }
    }
    if len(s)+count<=2*n {
        return true
    }
    return false
}




T17: 电话号码字母组合

  • 这里涉及路径列表curr的底层相同的问题,所以用切片,避免函数递归调用时互相影响,同时最后结果汇总注意不会互相影响(深拷贝)
func letterCombinations(digits string) []string {
    panel:=map[byte]string{'2':"abc",'3':"def",'4':"ghi",'5':"jkl",'6':"mno",'7':"pqrs",'8':"tuv",'9':"wxyz"}
    curr:=[]byte{}
    ans:=[]string{}
    if len(digits)==0 {
        return []string{}
    }
    letterCombinationsAssist(digits, &curr, &ans, panel)
    return ans
}

func letterCombinationsAssist(digits string, curr *[]byte, ans *[]string, panel map[byte]string) {
    if len(digits)==0 {
        *ans=append(*ans, string(*curr))
        return
    }
    choice:=panel[digits[0]]
    for idx := range choice {
        *curr=append(*curr, choice[idx])
        letterCombinationsAssist(digits[1:], curr, ans, panel)
        *curr=(*curr)[:len(*curr)-1]
    }
}




※T93: 复原IP地址

  • 基本框架是一样的
  • 注意IP地址要求 0 ∼ 255 0 \sim 255 0255,同时010这样的是不存在的
import (
    "strconv"
    "strings"
)

func restoreIpAddresses(s string) []string {
    curr:=[]string{}
    ans:=[]string{}
    restoreIpAddressesAssist(s, 0, &curr, &ans)
    return ans
}

func restoreIpAddressesAssist(s string, idx int, curr *[]string, ans *[]string) {
    if len(*curr)==4 || idx==len(s) {
        if idx==len(s) && len(*curr)==4 {
            *ans=append(*ans, strings.Join(*curr,"."))
            return
        }
        return
    }
    for i:=1;i<4;i++ {
        if idx+i>len(s) {
            continue
        }
        tmp:=s[idx:idx+i]
        if tmp_i,err:=strconv.Atoi(tmp);err==nil {
            if tmp_i>255 {
                continue
            }
            if len(tmp)>1 && tmp[0]=='0' {
                continue
            }
        }
        *curr=append(*curr, tmp)
        restoreIpAddressesAssist(s, idx+i, curr, ans)
        *curr=(*curr)[:len(*curr)-1]
    }
}




※T131: 分割回文串

func partition(s string) [][]string {
    curr:=[]string{}
    ans:=[][]string{}
    if len(s)==0 {
        return ans
    }
    memo:=map[[2]int]struct{}{}
    for i:=0;i<len(s);i++ {
        getPalindrome(s, i, i, memo)
        getPalindrome(s, i, i+1, memo)
    }
    partitionAssist(s, 0, &curr, &ans, memo)
    return ans
}

func partitionAssist(s string, index int, curr *[]string, ans *[][]string, memo map[[2]int]struct{}) {
    if index==len(s) {
        tmp:=make([]string, len(*curr))
        copy(tmp, *curr)
        *ans=append(*ans, tmp)
        return
    }
    for end:=index+1;end<=len(s);end++ {
        if _,ok:=memo[[2]int{index,end-1}];!ok {
            continue
        }
        *curr=append(*curr, s[index:end])
        partitionAssist(s, end, curr, ans, memo)
        *curr=(*curr)[:len(*curr)-1]
    }
}

func getPalindrome(s string, index1 int, index2 int, memo map[[2]int]struct{}) {
    for index1>=0 && index2<len(s) {
        if s[index1]==s[index2] {
            memo[[2]int{index1, index2}]=struct{}{}
            index1--
            index2++
        } else {
            break
        }
    }
}




T327: 区间和的个数

  • 归并法 或 线段树
  • 计算前缀和,如果前缀和分段有序,则可以根据指针移动快速计算对数
  • 最后使用归并排序保持前缀和数组有序
  • 注意需要证明分段排序不改变总对数证明思路
class Solution {
    public int countRangeSum(int[] nums, int lower, int upper) {
        long[] preSum = new long[nums.length+1];
        for (int i=1;i<preSum.length;i++) {
            preSum[i]=preSum[i-1]+nums[i-1];
        }
        long[] tmp = new long[preSum.length];
        return assist(lower, upper, preSum, tmp, 0, preSum.length-1);
     }

    public int assist(int lower, int upper, long[] preSum, long[] tmp, int lo, int hi) {
        if (lo==hi) {
            return 0;
        }
        int mid = (hi-lo)/2+lo;
        int count1 = assist(lower, upper, preSum, tmp, lo, mid);
        int count2 = assist(lower, upper, preSum, tmp, mid+1, hi);
        int point1=mid+1, point2=mid+1, subCount=0;
        for (int index=lo;index<=mid;index++) {
            while (point1<=hi && preSum[point1]-preSum[index]<lower) {
                point1++;
            }
            point2=point1;
            while (point2<=hi && preSum[point2]-preSum[index]<=upper) {
                point2++;
            }
            subCount+=point2-point1;
        }
        point1=lo; point2=mid+1;int index=lo;
        System.arraycopy(preSum, lo, tmp, lo, hi-lo+1);
        while (point1<=mid || point2<=hi) {
            if (point1==mid+1) {
                preSum[index++]=tmp[point2++];
            } else if (point2==hi+1) {
                preSum[index++]=tmp[point1++];
            } else {
                if (tmp[point1]<=tmp[point2]) {
                    preSum[index++]=tmp[point1++];
                } else {
                    preSum[index++]=tmp[point2++];
                }
            }
        }
        return count1+count2+subCount;
    }
}




T51: N皇后

  • 注意判断皇后是否冲突,有水平垂直和斜上方
  • 注意golang中多循环变量怎么写
func solveNQueens(n int) [][]string {
    curr:=make([][]byte, n)
    for i:=0;i<n;i++ {
        curr[i]=make([]byte,n)
        for j:=0;j<n;j++ {
            curr[i][j]='.'
        }
    }
    ans:=make([][]string, 0)
    assist(0, curr, &ans)
    return ans
}

func assist(q int, curr [][]byte, ans *[][]string) {
    if q==len(curr) {
        tmp:=make([]string, len(curr))
        for i:=0;i<len(tmp);i++ {
            tmp[i]=string(curr[i])
        }
        *ans=append(*ans, tmp)
        return
    }
    
    for i:=0;i<len(curr);i++ {
        if isConflict(q, i, curr) {
            continue
        }
        (curr)[q][i]='Q'
        assist(q+1, curr, ans)
        (curr)[q][i]='.'
    }
}

func isConflict(i int, j int, curr [][]byte) bool {
    for index:=0;index<len(curr);index++ {
        if index!=j && (curr)[i][index]=='Q' {
            return true
        }
        if index!=i && (curr)[index][j]=='Q' {
            return true
        }
    }
    for tmp_i,tmp_j:=i-1,j+1;tmp_i<len(curr) && tmp_i>=0 && tmp_j<len(curr) && tmp_j>=0;tmp_i,tmp_j=tmp_i-1,tmp_j+1 {
        if curr[tmp_i][tmp_j]=='Q' {
            return true
        }
    }
    for tmp_i,tmp_j:=i-1,j-1;tmp_i<len(curr) && tmp_i>=0 && tmp_j<len(curr) && tmp_j>=0;tmp_i,tmp_j=tmp_i-1,tmp_j-1 {
        if curr[tmp_i][tmp_j]=='Q' {
            return true
        }
    }
    return false
}




T52: N皇后Ⅱ

  • 回溯法可以让O(N^N)时间复杂度降低到O(N!)
  • 如何利用一维数组快速判断冲突
  • 对于所有的主对角线有 行号 + 列号 = 常数,对于所有的次对角线有 行号 - 列号 = 常数
    在这里插入图片描述
class Solution {
    public int totalNQueens(int n) {
        int[] cols = new int[n];
        int[] hills = new int[2*n-1];
        int[] dales = new int[2*n-1];
        return assist(n, 0, cols, hills, dales);
    }

    public int assist(int n, int row, int[] cols, int[] hills, int[] dales) {
        if (row==n) {
            return 1;
        }
        int tmp = 0;
        for (int j=0;j<n;j++) {
            if (cols[j]==0 && hills[row-j+n-1]==0 && dales[row+j]==0) {
                cols[j]=1;
                hills[row-j+n-1]=1;
                dales[row+j]=1;
                tmp+=assist(n, row+1, cols, hills, dales);
                cols[j]=0;
                hills[row-j+n-1]=0;
                dales[row+j]=0;
            }
        }
        return tmp;
    }
}




T37: 解数独

  • 只取出第一个解
func solveSudoku(board [][]byte)  {
    assist(board, 0)
}

func assist(board [][]byte, num int) bool {
    if num==81 {
        return true
    }
    i:=num/9
    j:=num%9
    if board[i][j]!='.' {
        return assist(board, num+1)
    }
    var c byte
    for c='1';c<='9';c++ {
        if isConflict(board, i, j, c) {
            continue
        }
        board[i][j]=c
        if assist(board, num+1) {
            return true
        }
        board[i][j]='.'
    }
    return false
}

func isConflict(board [][]byte, i int, j int, c byte) bool {
    for index:=0;index<9;index++ {
        if index!=i && board[index][j]==c {
            return true
        }
        if index!=j && board[i][index]==c {
            return true
        }
    }
    for idx_i:=i/3*3;idx_i<(i/3+1)*3;idx_i++ {
        for idx_j:=j/3*3;idx_j<(j/3+1)*3;idx_j++ {
            if board[idx_i][idx_j]==c {
                return true
            }
        }
    }
    return false
}
  • python3
class Solution:
    def solveSudoku(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.assist(board, 0)
    def assist(self, board: List[List[str]], num: int) -> bool:
        if num==81:
            return True
        i=num//9
        j=num%9
        if board[i][j]!=".":
            return self.assist(board, num+1)
        for c in range(1,10):
            c=str(c)
            if self.isConflict(board, i, j, c):
                continue
            board[i][j]=c
            if self.assist(board, num+1):
                return True
            board[i][j]='.'
        return False
    def isConflict(self, board: List[List[str]], i: int, j: int, c: str) -> bool:
        for index in range(0,9):
            if index!=i and board[index][j]==c:
                return True
            if index!=j and board[i][index]==c:
                return True
        for idx_i in range(i//3*3,(i//3+1)*3):
            for idx_j in range(j//3*3,(j//3+1)*3):
                if board[idx_i][idx_j]==c:
                    return True
        return False




T679: 24点

  • 思路:先选出有顺序的两张牌,然后两者有加减乘除四种可能,将新数加入剩下的两张牌,对三张牌进行同样的操作。( A 4 2 × 4 × A 3 2 × 4 × A 2 2 × 4 A_4^{2} \times 4 \times A_3^2 \times 4 \times A_2^{2} \times 4 A42×4×A32×4×A22×4)
from operator import add, sub, mul, truediv 
class Solution:
    ops=[add, sub, mul, truediv]
    def judgePoint24(self, nums: List[int]) -> bool:
        if len(nums)==1:
            return abs(nums[0]-24)<1e-6
        for i in range(len(nums)):
            for j in range(len(nums)):
                if i!=j:
                    left=[nums[k] for k in range(len(nums)) if k!=i and k!=j]
                    for op in self.ops:
                        if i>j and (op==add or op==mul):
                            continue
                        if op==truediv and nums[j]==0:
                            continue
                        left.append(op(nums[i], nums[j]))
                        if self.judgePoint24(left):
                            return True
                        left.pop()
        return False







二分查找




无重复元素二分查找

  • 两侧均为闭区间
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)-1
        while lo<=hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid-1
            elif tmp<value:
                lo=mid+1
            else:
                return mid
        # lo为应该插入的位置
        # return lo
        return -1
  • 左闭右开
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)
        while lo<hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid
            elif tmp<value:
                lo=mid+1
            else:
                return mid
        # lo为应该插入的位置
        # return lo
        return -1




寻找左侧边界的二分搜索

  • 左闭右开
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)
        while lo<hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid
            elif tmp<value:
                lo=mid+1
            else:
                hi=mid
        # lo为应该插入的位置
        # return lo
        if lo==len(lst):
            return -1
        return lo if lst[lo]==value else -1
  • 左闭右闭
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)-1
        while lo<=hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid-1
            elif tmp<value:
                lo=mid+1
            else:
                hi=mid-1
        # lo为应该插入的位置
        # return lo
        if lo==len(lst):
            return -1
        return lo if lst[lo]==value else -1

寻找右侧边界的二分搜索

  • 左闭右开
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)
        while lo<hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid
            elif tmp<value:
                lo=mid+1
            else:
                lo=mid+1
        # lo为不存在时应该返回的位置,lo-1为存在时返回的位置
        # return lo-1
        if lo==0:
            return -1
        return lo-1 if lst[lo-1]==value else -1
  • 左闭右闭
class Solution:
    def search(self, lst: List[int], value: int) -> int:
        lo=0
        hi=len(lst)-1
        while lo<=hi:
            mid=(hi-lo)//2+lo
            tmp=lst[mid]
            if tmp>value:
                hi=mid-1
            elif tmp<value:
                lo=mid+1
            else:
                lo=mid+1
        # lo为不存在时应该返回的位置,lo-1为存在时返回的位置
        # return lo-1
        if lo==0:
            return -1
        return lo-1 if lst[lo-1]==value else -1




T875: 爱吃香蕉的keke

  • 有序序列→二分搜索
  • 值域的左侧二分搜索
func minEatingSpeed(piles []int, H int) int {
    maxK:=0
    for i:=0;i<len(piles);i++ {
        if piles[i]>maxK {
            maxK=piles[i]
        }
    }
    return binarySearchLeft(piles, 1, maxK-1, H)
}

func binarySearchLeft(piles []int, lo int, hi int, H int) int {
    for lo<=hi {
        mid:=(hi-lo)/2+lo
        if canEat(piles, mid, H) {
            hi=mid-1
        } else {
            lo=mid+1
        }
    }
    return lo
}

func canEat(piles []int, k int, H int) bool {
    c:=0
    for i:=0;i<len(piles);i++ {
        c+=piles[i]/k
        if piles[i]%k!=0 {
            c++
        }
    }
    return c<=H
}




T1011: 在D天内送达包裹的能力

  • 确定值域范围
  • 变O(N)遍历为有序序列二分搜索O(logN)
  • 最低能力、最小速度→左侧
func shipWithinDays(weights []int, D int) int {
    minK:=0
    maxK:=0
    for i:=0;i<len(weights);i++ {
        if weights[i]>minK {
            minK=weights[i]
        }
        maxK+=weights[i]
    }
    return binarySearchLeft(weights, D, minK, maxK)
}

func binarySearchLeft(weights []int, D int, lo int, hi int) int {
    for lo<hi {
        mid:=(hi-lo)/2+lo
        if canShip(weights, D, mid) {
            hi=mid
        } else {
            lo=mid+1
        }
    }
    return lo
}

func canShip(weights []int, D int, K int) bool {
    c:=0
    curr:=K
    for i:=0;i<len(weights);i++ {
        if weights[i]<=curr {
            curr-=weights[i]
        } else {
            c++
            curr=K-weights[i]
        }
    }
    c++
    return c<=D
}




T392: 判断子序列

后续挑战 :

如果有大量输入的 S,称作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/is-subsequence
  • 预存字母坐标
  • 二分搜索代替线性搜索,这里基础版和左侧边界都可以
  • O(MN)→O(MlogN)
func isSubsequence(s string, t string) bool {
    memo:=map[byte][]int{}
    for i := range t {
        if _,ok:=memo[t[i]];!ok {
            memo[t[i]]=[]int{i}
        } else {
            memo[t[i]]=append(memo[t[i]], i)
        }
    }
    index:=0
    for i:=0;i<len(s);i++ {
        if tmp,ok:=memo[s[i]];ok {
            tmp_i:=binarySearch(tmp, index)
            if tmp_i==len(tmp) {
                return false
            }
            index=tmp[tmp_i]+1
        } else {
            return false
        }
    }
    return true
}

func binarySearch(tmp []int, val int) int {
    lo:=0
    hi:=len(tmp)-1
    for lo<=hi {
        mid:=(hi-lo)/2+lo
        if tmp[mid]<val {
            lo=mid+1
        } else if tmp[mid]>val {
            hi=mid-1
        } else {
            return mid
        }
    }
    return lo
}




T4: 寻找两个正序数组的中位数

  • O(M+N)的解法,类似双指针合并两个正序数组
  • O ( log ⁡ ( M + N 2 ) ) O(\log(\frac{M+N}{2})) O(log(2M+N))
  • 思路
  • 即分别在两个正序数组中找前 K / 2 K/2 K/2个,第 K K K个元素只可能在①第一个数组的第 K / 2 K/2 K/2个和②第二个数组的第 K / 2 K/2 K/2个和③④两个数组分别除前 K / 2 K/2 K/2个之后的数组中出现;而若①<②,则必不可能在①出现,所以可以不考虑这部分元素,从而将 K → K − K / 2 K→K-K/2 KKK/2;边界条件:当 K = 1 K=1 K=1时,只需比较头元素即可
import "math"

func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {
    m:=(len(nums1)+len(nums2))/2+1
    n:=(len(nums1)+len(nums2)+1)/2
    return findKthElementFrom2SortedArrays(nums1, 0, nums2, 0, m)/2+findKthElementFrom2SortedArrays(nums1, 0, nums2, 0, n)/2
}

func findKthElementFrom2SortedArrays(nums1 []int, i int, nums2 []int, j int, K int) float64 {
    if i>=len(nums1) {
        return float64(nums2[j+K-1])
    }
    if j>=len(nums2) {
        return float64(nums1[i+K-1])
    }
    if K==1 {
        return math.Min(float64(nums1[i]),float64(nums2[j]))
    }
    mid:=K/2
    minVal1:=math.Inf(1)
    minVal2:=math.Inf(1)
    if i+mid-1<len(nums1) {
        minVal1=float64(nums1[i+mid-1])
    }
    if j+mid-1<len(nums2) {
        minVal2=float64(nums2[j+mid-1])
    }
    if minVal1<minVal2 {
        return findKthElementFrom2SortedArrays(nums1, i+mid,nums2, j, K-mid)
    } else {
        return findKthElementFrom2SortedArrays(nums1,i,nums2,j+mid,K-mid)
    }
}




T50: Pow(x,n)

  • 对n进行二分查找
  • 递归版本
func myPow(x float64, n int) float64 {
    if n==0 {
        return 1
    }
    a:=n%2
    b:=n/2
    tmp:=myPow(x, b)
    if a==1 {
        return tmp*tmp*x
    } else if a==0{
        return tmp*tmp
    } else {
        return tmp*tmp/x
    }
}
  • 迭代版本

若一个整数被写成二进制,即拆分成
n = 2 i 0 + 2 i 1 + 2 i 2 + . . . + 2 i m n=2^{i_0}+2^{i_1}+2^{i_2}+...+2^{i_m} n=2i0+2i1+2i2+...+2im
那么
x n = x 2 i 0 ∗ x 2 i 1 ∗ x 2 i 2 ∗ . . . ∗ x 2 i m x^n=x^{2^{i_0}}*x^{2^{i_1}}*x^{2^{i_2}}*...*x^{2^{i_m}} xn=x2i0x2i1x2i2...x2im
所以可以利用除二取余法,若余数为1则计入结果

func myPow(x float64, n int) float64 {
    if n>=0 {
        return myPowPositive(x, n)
    } else {
        return 1/myPowPositive(x, -n)
    }
}
func myPowPositive(x float64, n int) float64 {
    tmp:=x
    ans:=1.0
    i:=n
    for i!=0 {
        if i%2==1 {
            ans*=tmp
        }
        tmp*=tmp
        i>>=1
    }
    return ans
}




T29: 两数相除

  • 利用除数的2次幂来逼近被除数
  • 异号的处理以及正边界溢出
func divide(dividend int, divisor int) int {
    m,n:=dividend,divisor
    if m<0 {
        m=-m
    }
    if n<0 {
        n=-n
    }
    if m<n {
        return 0
    }
    divisor_p:=n
    i:=0
    for m>=n {
        n<<=1
        i++
    }
    ans:=(1 << (i-1))+divide(m-n>>1,divisor_p)
    if dividend ^ divisor < 0 {
        ans=-ans
    }
    if ans==1<<31 {
        ans=1<<31-1
    }
    return ans
}




T34: 在排序数组中查找第一个位置和最后一个位置

  • 左侧边界
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        return self.binarySearch(nums, 0, len(nums)-1, target)
    def binarySearch(self, nums, lo, hi, target):
        start=lo
        end=hi
        while start<=end:
            mid=(end-start)//2+start
            if nums[mid]<target:
                start=mid+1
            else:
                end=mid-1
        if start==len(nums) or nums[start]!=target:
            return [-1, -1]
        index=start
        while index<len(nums)-1 and nums[index+1]==nums[index]:
            index+=1
        return [start, index]




剑指53: 0~n-1中缺失的数字

  • 求和相减; O ( N ) O(N) O(N)时间复杂度
  • m i d < n u m s [ m i d ] mid<nums[mid] mid<nums[mid]条件下二分搜索求左侧边界; O ( l o g N ) O(logN) O(logN)时间复杂度
class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        return self.binarySearch(nums, 0, len(nums)-1)
    def binarySearch(self, nums, lo, hi):
        start=lo
        end=hi
        while start<=end:
            mid=(end-start)//2+start
            if mid==nums[mid]:
                start=mid+1
            else:
                end=mid-1
        return start




T33: 搜索旋转排序数组

  • 不管有无重复,基础版binarySearch关注的是在不在数组中,左侧边界能给出第一个出现的位置/符合条件的最小值
  • 根据右端点大小先确定mid在左半部分还是右半部分,如在左半部分,再比较target,左端点和mid的值进一步缩小范围
func search(nums []int, target int) int {
    lo:=0
    hi:=len(nums)
    for lo<hi {
        mid:=(hi-lo)>>1+lo
        if nums[mid]==target {
            return mid
        }
        if nums[mid]<=nums[hi-1] {
        	// 注意target<nums[lo]不对,因为可能这个数组旋转两次保持不变
            if target>nums[mid] && target<=nums[hi-1] {
                lo=mid+1
            } else {
                hi=mid
            }
        } else {
            if target<nums[mid] && target>=nums[lo] {
                hi=mid
            } else {
                lo=mid+1
            }
        }
    }
    return -1
}




T81: 搜索旋转排序数组Ⅱ

class Solution {
    public boolean search(int[] nums, int target) {
        int lo = 0, hi = nums.length-1;
        while (lo <= hi) {
            int mid = (hi-lo)/2+lo;
            if (nums[mid]==target) {
                return true;
            }
            if (nums[mid]<=nums[hi]) {
                if (nums[hi]==nums[mid] && nums[lo]==nums[hi]) {
                    lo++;hi--;
                } else {
                    if (nums[mid]<target && target<=nums[hi]) {
                        lo = mid+1;
                    } else {
                        hi = mid-1;
                    }
                }   
            } else {
                if (nums[mid]>target && target>=nums[lo]) {
                    hi = mid-1;
                } else {
                    lo = mid+1;
                }
            }
        }
        return false;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值