1. 组合数:n个数找k个数的组合
这题的核心是每次遍历从begin到n之间的所有数,并放到一个path里。当path=k的时候返回。要注意两点:
(1)不要在path长度为k的时候清空path!回溯不需要清空,因为回溯的时候就已经pop了
(2)python不能直接results.append(path),因为path在回溯过程中一直在变化!append里只是加了它的引用,应该用copy()
n = 10
k = 2
results = []
path = []
def findk(begin):
if len(path) == k:
results.append(path.copy())
return
for i in range(begin, n+1):
path.append(i)
findk(i+1)
path.pop()
findk(1)
print(results)
剪枝优化方式:因为path小于k的根本就不会当成结果,所以n-begin<k的时候,begin就不用再递归了。改成:
for(int i = begin; i<=n-(k-path.size())+1;i++){
2. 无重复字符的最长子串
双指针问题,我想了好久,这题边界好容易出错。双指针要注意的是,左右指针相等的时候应该意味着当前子数组的长度为1.所以初始化最好置左指针为0,右指针为-1,每次先尝试右指针+1.
def lengthOfLongestSubstring(self, s: str) -> int:
i = 0
j = -1
res = 0
tmpset = set()
for i in range(len(s)):
while j+1 < len(s) and s[j+1] not in tmpset:
tmpset.add(s[j+1])
j = j+1
res = max(res, len(tmpset))
print(tmpset)
print(i," ",j)
tmpset.remove(s[i])
return res
3. 全排列问题(hot100)
要注意的点:(1)def findk需要定义成局部函数,不然在permute里无法调用。(2)全排列的区别是,不要设置begin,因为21和12是不一样的,只需要判断是否重复。
def permute(self, nums: List[int]) -> List[List[int]]:
path = []
results = []
def findk(nums):
if len(path) == len(nums):
results.append(path.copy())
return
for i in range(len(nums)):
if nums[i] in path:
continue
path.append(nums[i])
findk(nums)
path.remove(nums[i])
findk(nums)
return results
4. 删除有序数组的重复项
看到子串这种一定要想到双指针啊!!!想到了就会很简单。只用一个数组,一次循环就能解决,slow记录,fast遍历
slow = 0
for fast in range(len(nums)):
if nums[fast] != nums[slow]:
nums[slow+1] = nums[fast]
slow=slow+1
return slow+1
5. 子集(hot100)
回溯问题,还是三部曲,主要就是要搞清楚什么时候res append到里面,然后限制条件是path不重复
def subsets(self, nums: List[int]) -> List[List[int]]:
path = []
res = []
def findk(begin):
res.append(path.copy())
if begin > len(nums)-1:
return
for i in range(begin,len(nums)):
if nums[i] not in path:
path.append(nums[i])
findk(i)
path.remove(nums[i])
findk(0)
return res
6. 环形链表:找到入环后的第一个节点
主要解决方法有两种,1是哈希表,直接把node插入到哈希表里就行了
2 是快慢指针,难点是如何判断入环的地方?
ListNode * slow = head, *fast = head, * newl = head;
if (head == nullptr) return nullptr;
while(fast->next!=nullptr && fast->next->next != nullptr){
slow = slow->next;
fast = fast->next->next;
if (slow == fast){
while(newl != slow){
newl = newl->next;
slow = slow->next;
}
return newl;
}
}
return nullptr;
}
7. 电话号码的字符串
回溯的常规思路,就是要注意他这里path不能作为全局变量传到局部函数里面。要当成参数传进去。(应该是因为path是一个str,如果是list就可以传进去)
def letterCombinations(self, digits: str) -> List[str]:
mp = {}
mp['2'] = "abc"
mp['3'] = "def"
mp['4'] = "ghi"
mp['5'] = "jkl"
mp['6'] = "mno"
mp['7'] = "pqrs"
mp['8'] = "tuv"
mp['9'] = "wxyz"
results = []
def findk(idx,path):
if idx == len(digits):
results.append(path)
return
num = digits[idx]
for i in mp[num]:
path = path+i
findk(idx+1,path)
path = path[:-1]
if len(digits) ==0:
return results
findk(0,"")
return results
8. 岛屿数量
就说python和c++一起写!!!一直有个bug最后才发现是因为c++for后面忘了加大括号!!一定不能犯这种低级错误!
注意:每次写算法都要先判断一下输入数据的长度为0时输出应该怎么办
还有:vector二维矩阵的初始化要记住:
vector<vector<bool>> visited= vector<vector<bool>>(n, vector<bool>(m,false));
class Solution {
public:
int gotox[4] = {1,-1,0,0};
int gotoy[4] = {0,0,1,-1};
void dfs(vector<vector<char>>& grid,vector<vector<bool>>& visited,int x,int y){
int n = grid.size(), m = grid[0].size();
for(int t =0;t<4;t++){
int nextx = x + gotox[t];
int nexty = y + gotoy[t];
if(nextx >= 0 && nextx < n && nexty >=0 && nexty <m){
if(visited[nextx][nexty]==false && grid[nextx][nexty] == '1'){
visited[nextx][nexty] = true;
dfs(grid,visited, nextx,nexty);
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
int res = 0;
int n = grid.size();
int m = grid[0].size();
vector<vector<bool>> visited= vector<vector<bool>>(n, vector<bool>(m,false));
for(int i =0;i<n;i++){
for(int j =0;j<m;j++){
if(visited[i][j] == false && grid[i][j] == '1'){
res++;
dfs(grid,visited,i,j);
}
}
}
return res;
}
};
9. 括号生成(回溯)
主要难点在于判断括号是否有效,就是要注意记录左括号和右括号的数量,保证左括号数量大于右括号数量时才往里面加入右括号。
int lenleft = 0, lenright = 0;
string path = "";
vector<string> res;
void findk(int n){
if (lenleft+lenright>=2*n){
res.push_back(path);
return;
}
if(lenleft < n){
lenleft++;
path.push_back('(');
findk(n);
path.pop_back();
lenleft--;
}
if(lenright < n && lenright<lenleft){
lenright++;
path.push_back(')');
findk(n);
path.pop_back();
lenright--;
}
}
vector<string> generateParenthesis(int n) {
findk(n);
return res;
}
10. 单词搜索(麻烦,需要二刷)
先用了dfs+回溯的方法,但是一直超时:
public:
int indx[4] = {0,0,1,-1}, indy[4] = {1,-1,0,0};
bool visited[10][10] = {false};
bool findk(int x,int y,int index,string word,vector<vector<char>>& board){
if (index == word.size()){
return true;
}
bool res = false;
for(int i=0;i<4;i++){
int nextx = x + indx[i];
int nexty = y + indy[i];
if (nextx >= board.size() || nextx < 0 || nexty >= board[0].size()||nexty <0 ) continue;
// cout<<nextx<<nexty<<endl;
if (board[nextx][nexty] == word[index] && !visited[nextx][nexty]){
visited[nextx][nexty] = true;
cout<<nextx<<nexty<<endl;
res = res || findk(nextx,nexty,index+1,word,board);
visited[nextx][nexty] = false;
}
}
return res;
}
bool exist(vector<vector<char>>& board, string word) {
for(int i = 0;i<board.size();i++){
for(int j =0;j<board[0].size();j++){
if(board[i][j] == word[0] && !visited[i][j]){
visited[i][j] = true;
if (findk(i,j,1,word,board)) return true;
visited[i][j] = false;
}
}
}
return false;
}
改成思路:不要把判断放在for循环里面,而是放在一开始进入dfs的地方,就判断xy是否符合规则
int indx[4] = {0,0,1,-1}, indy[4] = {1,-1,0,0};
bool visited[10][10] = {false};
bool findk(int x,int y,int index,string word,vector<vector<char>>& board){
if (index == word.size()){
return true;
}
if (x >= board.size() || x < 0 || y >= board[0].size()||y <0 ) return false;
if (board[x][y] != word[index] || visited[x][y] == true ) return false;
visited[x][y] = true;
bool res = false;
for(int i=0;i<4;i++){
int nextx = x + indx[i];
int nexty = y + indy[i];
res = res || findk(nextx,nexty,index+1,word,board);
}
visited[x][y] = false;
return res;
}
bool exist(vector<vector<char>>& board, string word) {
for(int i = 0;i<board.size();i++){
for(int j =0;j<board[0].size();j++){
if(board[i][j] == word[0] && findk(i,j,0,word,board)){
return true;
}
}
}
return false;
}