格式:
题号+题名+简单思路+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为节点,又涉及到图的构建及剪枝,以邻节点的传统方式构建图将会超时
- ②定义,传统方式,超时
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
}
- ②定义,通过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:=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
}
- ①定义,以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
回溯思想
- 终止条件
- 选择列表
- 更新和撤销
※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: 括号生成
- 复杂度参考
- 回溯法 = 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:=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
0∼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/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 K→K−K/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=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:=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;
}
}