格式:

题号+题名+简单思路+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]=trueboard[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 intparent []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.parentfor 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*lenYfor 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:=0for 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:=0for 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]=truereturn 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:=0for 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]=truedfs(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: 岛屿的最大面积

  • Leetcode上同样的代码golangjava要慢几倍?
func maxAreaOfIsland(grid [][]int) int {ans:=0for 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]=trueans:=falsefor 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]=falsereturn 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:=0visited[0][0]=truefor 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]=truetmp=append(tmp, [2]int{n_i, n_j})}}}queue=tmp}return ans
}func isValid(m int, n int, k int) bool {res:=0for m!=0 {res+=m%10m/=10}for n!=0 {res+=n%10n/=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]]=nilgraph[routes[i][k]][routes[i][j]]=nil}}}ans:=0queue:=[]int{S}seen:=map[int]interface{}{}seen[S]=nilfor 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=tmpans++}return -1
}
  1. ②定义,通过route剪枝
  • struct{}{}占用内存更少
  • 应用在方法类,chan中

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:=0queue:=[]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=tmpans++}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)}}}// 多起点BFSqueue:=make([]int,len(seen))idx:=0for i := range seen {queue[idx]=iidx++}ans:=1for 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=tmpans++}return -1
}func hasIntersect(list1 []int, list2 []int) bool {// 前提已排序i, j := 0, 0for 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×Width)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:=0for 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=tmpdistance++}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:=0if 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]=0tmp=append(tmp, [2]int{idx_i, idx_j})}}}queue=tmpdistance++}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++ {    // 因为任何一个节点都可以作为起点,所以多节点BFSqueue=append(queue, state{1<<i, i})visited[1<<i][i]=true}distance:=0for len(queue)>0 {tmp:=[]state{}for idx := range queue {node:=queue[idx].nodevisited_b:=queue[idx].visited_bif 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]=truetmp=append(tmp, state{nextVisited_b, nextNode})}}}queue=tmpdistance++}return -1
}

※T301: 删除无效括号

  • 由于要求删除最少数量的括号,所以用BFS
class Solution:def removeInvalidParentheses(self, s: str) -> List[str]:queue=set([s])    # 由于涉及去重,这里可用Setans=[]while len(queue)>0:for ss in queue:if self.isValid(ss):ans.append(ss)if len(ans)>0:return anstmp=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=tmpreturn []def isValid(self, s: str) -> bool:"""计算括号匹配,由于只有一种括号,不需要使用Stack记录括号类型"""count=0for c in s:if c=="(":count+=1elif c==")":count-=1if count<0:return Falseif count==0:return Trueelse: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]=nilassist(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]==0continue;}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)returnfor i in range(len(s)):if i in memo:continuememo.add(i)self.assist(s, memo, curr+s[i], ans)memo.remove(i)

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

  • 基于N叉树不剪枝,O(6n)O(6^n)O(6n)时间复杂度
  • 基于背包问题的DP,O(62n2)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]=tmpletterCasePermutationAssist(S, index+1, curr, ans)}*curr=(*curr)[:len(*curr)-1]
}

T22: 括号生成

  • 复杂度参考
  • 回溯法 = N叉树遍历,所以涉及剪枝操作
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:=0for 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∼2550 \sim 2550∼255,同时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/9j:=num%9if board[i][j]!='.' {return assist(board, num+1)}var c bytefor c='1';c<='9';c++ {if isConflict(board, i, j, c) {continue}board[i][j]=cif 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 Truei=num//9j=num%9if 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):continueboard[i][j]=cif self.assist(board, num+1):return Trueboard[i][j]='.'return Falsedef 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 Trueif index!=j and board[i][index]==c:return Truefor 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 Truereturn False

T679: 24点

  • 思路:先选出有顺序的两张牌,然后两者有加减乘除四种可能,将新数加入剩下的两张牌,对三张牌进行同样的操作。(A42×4×A32×4×A22×4A_4^{2} \times 4 \times A_3^2 \times 4 \times A_2^{2} \times 4A42​×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-6for 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):continueif op==truediv and nums[j]==0:continueleft.append(op(nums[i], nums[j]))if self.judgePoint24(left):return Trueleft.pop()return False

二分查找

无重复元素二分查找

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

寻找左侧边界的二分搜索

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

寻找右侧边界的二分搜索

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

T875: 爱吃香蕉的keke

  • 有序序列→二分搜索
  • 值域的左侧二分搜索
func minEatingSpeed(piles []int, H int) int {maxK:=0for 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+loif canEat(piles, mid, H) {hi=mid-1} else {lo=mid+1}}return lo
}func canEat(piles []int, k int, H int) bool {c:=0for i:=0;i<len(piles);i++ {c+=piles[i]/kif piles[i]%k!=0 {c++}}return c<=H
}

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

  • 确定值域范围
  • 变O(N)遍历为有序序列二分搜索O(logN)
  • 最低能力、最小速度→左侧
func shipWithinDays(weights []int, D int) int {minK:=0maxK:=0for 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+loif canShip(weights, D, mid) {hi=mid} else {lo=mid+1}}return lo
}func canShip(weights []int, D int, K int) bool {c:=0curr:=Kfor 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:=0for 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:=0hi:=len(tmp)-1for lo<=hi {mid:=(hi-lo)/2+loif 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+N2))O(\log(\frac{M+N}{2}))O(log(2M+N​))
  • 思路
  • 即分别在两个正序数组中找前K/2K/2K/2个,第KKK个元素只可能在①第一个数组的第K/2K/2K/2个和②第二个数组的第K/2K/2K/2个和③④两个数组分别除前K/2K/2K/2个之后的数组中出现;而若①<②,则必不可能在①出现,所以可以不考虑这部分元素,从而将K→K−K/2K→K-K/2K→K−K/2;边界条件:当K=1K=1K=1时,只需比较头元素即可
import "math"func findMedianSortedArrays(nums1 []int, nums2 []int) float64 {m:=(len(nums1)+len(nums2))/2+1n:=(len(nums1)+len(nums2)+1)/2return 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/2minVal1:=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%2b:=n/2tmp:=myPow(x, b)if a==1 {return tmp*tmp*x} else if a==0{return tmp*tmp} else {return tmp*tmp/x}
}
  • 迭代版本

若一个整数被写成二进制,即拆分成
n=2i0+2i1+2i2+...+2imn=2^{i_0}+2^{i_1}+2^{i_2}+...+2^{i_m}n=2i0​+2i1​+2i2​+...+2im​
那么
xn=x2i0∗x2i1∗x2i2∗...∗x2imx^n=x^{2^{i_0}}*x^{2^{i_1}}*x^{2^{i_2}}*...*x^{2^{i_m}}xn=x2i0​∗x2i1​∗x2i2​∗...∗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:=xans:=1.0i:=nfor i!=0 {if i%2==1 {ans*=tmp}tmp*=tmpi>>=1}return ans
}

T29: 两数相除

  • 利用除数的2次幂来逼近被除数
  • 异号的处理以及正边界溢出
func divide(dividend int, divisor int) int {m,n:=dividend,divisorif m<0 {m=-m}if n<0 {n=-n}if m<n {return 0}divisor_p:=ni:=0for m>=n {n<<=1i++}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=loend=hiwhile start<=end:mid=(end-start)//2+startif nums[mid]<target:start=mid+1else:end=mid-1if start==len(nums) or nums[start]!=target:return [-1, -1]index=startwhile index<len(nums)-1 and nums[index+1]==nums[index]:index+=1return [start, index]

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

  • 求和相减;O(N)O(N)O(N)时间复杂度
  • 在mid<nums[mid]mid<nums[mid]mid<nums[mid]条件下二分搜索求左侧边界;O(logN)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=loend=hiwhile start<=end:mid=(end-start)//2+startif mid==nums[mid]:start=mid+1else:end=mid-1return start

T33: 搜索旋转排序数组

  • 不管有无重复,基础版binarySearch关注的是在不在数组中,左侧边界能给出第一个出现的位置/符合条件的最小值
  • 根据右端点大小先确定mid在左半部分还是右半部分,如在左半部分,再比较target,左端点和mid的值进一步缩小范围
func search(nums []int, target int) int {lo:=0hi:=len(nums)for lo<hi {mid:=(hi-lo)>>1+loif 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;}
}

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

  1. LeetCode 69: Sqrt(x) 求根号x(牛顿迭代法和二分查找法)

    题目: Implement int sqrt(int x). Compute and return the square root of x. 分析:我们平常可能好少会自己去求解某个数的平方根,一般都 ...

  2. LeetCode 729. 我的日程安排表 I(set 二分查找)

    文章目录 1. 题目 2. 解题 2.1 set 二分查找 2.2 差分思想 1. 题目 实现一个 MyCalendar 类来存放你的日程安排.如果要添加的时间内没有其他安排,则可以存储这个新的日程安 ...

  3. [Golang]力扣Leetcode - 852. 山脉数组的峰顶索引(二分查找)

    [Golang]力扣Leetcode - 852. 山脉数组的峰顶索引(二分查找) 题目:符合下列属性的数组 arr 称为 山脉数组 : arr.length >= 3 存在 i(0 < ...

  4. C#LeetCode刷题-并查集

    并查集篇 # 题名 刷题 通过率 难度 128 最长连续序列 39.3% 困难 130 被围绕的区域 30.5% 中等 200 岛屿的个数 38.4% 中等 547 朋友圈 45.1% 中等 684 ...

  5. Leetcode——唯唯诺诺对并查集的初次相见

    基本概念 并查集是一种数据结构 并查集这三个字,一个字代表一个意思. 并(Union),代表合并 查(Find),代表查找 集(Set),代表这是一个以字典为基础的数据结构,它的基本功能是合并集合中的 ...

  6. 【Python数据结构】——并查集的实现(查找、合并、集合、实例)

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2021/7/30 23:12 # @Author : @linlianqin # @S ...

  7. 并查集(合并与查找)

    什么是并查集?   并查集是一种用来管理元素分组情况的数据结构.并查集可以高效地查询两个元素是否在同一个集合.合并两个不同的集合.   不过需要注意并查集虽然可以进行合并操作,但是却无法进行分割操作. ...

  8. LeetCode 1838. 最高频元素的频数(二分查找)

    文章目录 1. 题目 2. 解题 1. 题目 元素的 频数 是该元素在一个数组中出现的次数. 给你一个整数数组 nums 和一个整数 k . 在一步操作中,你可以选择 nums 的一个下标,并将该下标 ...

  9. LeetCode 327. 区间和的个数(multiset二分查找/归并排序)

    文章目录 1. 题目 2. 解题 2.1 动态规划超时 2.2 二分查找 2.3 归并排序 1. 题目 给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 low ...

最新文章

  1. 智能合约重构社会契约 (1)李嘉图合约
  2. 中国智慧城市前瞻及开发运营可行性分析报告2022-2027年版
  3. 怎么找出电脑里隐藏的流氓软件_9成人都不知道的秘密!那些隐藏在你电脑里的“大象”!...
  4. 基础练习 十六进制转八进制 c语言
  5. 激光炸弹(BZOJ1218)
  6. 电脑运行卡顿怎么处理_【众点学】电脑运行PS卡顿?可能是你的虚拟内存没设置好!...
  7. 【Java】Java与GoF-23种设计模式
  8. JAVA编程相关:eclipse如何导入已有工程
  9. 华为鸿蒙ipc时延,虚搜
  10. 3.7亿条保单数据怎么分析?这个大数据平台有绝招
  11. Luogu 3423 [POI 2005]BAN-银行票据 (多重背包单调队列优化 + 方案打印)
  12. php for求合,怎么用PHP for循环求1到100的和
  13. 【3阶范德蒙行列式计算】
  14. CSDN信息无障碍知识库:为障碍者撑起一片蓝天
  15. ospf(MGRE的星型结构和全连结构)
  16. [游戏] 星际争霸2:一个新的传奇?
  17. python修改图片,Python之修改图片像素值的方法
  18. Linux使用本地光盘制作yum源
  19. 怎样修改MindMapper中的线条
  20. 营业执照识别/营业执照OCR识别API

热门文章

  1. GitHub上排名前100的Android开源库介绍
  2. GitHub前100的开源库,非常实用
  3. 身边现实:帮你揭开几个程序员的真相
  4. Unity用Shader实现UGU i图片边缘选中高亮
  5. RPATH $ORIGIN LD_LIBRARY_PATH和可移植 linux 二进制文件的描述
  6. 弘辽科技:食品美妆化与美妆食品化,收割了谁的流量?
  7. CAD设置点的样式(网页版)
  8. Educational Codeforces Round 70 (Rated for Div. 2) 题解
  9. utf8 与 utf8mb4
  10. 阿里软件联合10亿风险投资欲造30家上市公司