各类型算法题整理(python、c++版)hot100

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;
    }

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值