力扣刷题笔记:回溯、广搜、深搜、矩阵(18)

17. 电话号码的字母组合(回溯)

回溯法,用map记录数字和字母的映射关系,可以用大括号嵌套的方式对map进行列表初始化

class Solution {
public:
    //map放在类内,方便成员函数调用
    map<char, string> phoneNum = {
    {'2', "abc"},{'3', "def"},{'4', "ghi"},{'5', "jkl"},{'6', "mno"},{'7', "pqrs"},{'8', "tuv"},{'9', "wxyz"}
    };
    vector<string> ans;
    string temp;
    void DFS(int index, string digits) {
        if (index == digits.size()){
            ans.push_back(temp);
            return;
        }
        else{
            char now= digits[index]; //不能写成now= digits[index]-'0';!因为map需要输入字符,否则程序自动结束!
            for (int i = 0; i < phoneNum[now].size(); i++) {
                //string支持字符类型弹入弹出
                temp.push_back(phoneNum[now][i]);
                DFS(index+1, digits);
                temp.pop_back();
            }
        }
    }
	vector<string> letterCombinations(string digits) {
        if (digits.empty())
            return ans;
        DFS(0, digits);
        return ans;
	}
};

22. 括号生成(回溯)

回溯法枚举所有括号情况,如果左括号等于右括号,只能增加左括号,如果左括号多都可以加

class Solution {
public:
	vector<string> ans;
	void generate(string str, int left, int right)
	{
		if (left==0&&right==0){
			ans.push_back(str);
			return;
		}
		else if (left == right)
			generate(str + '(', left - 1, right);
		else if (left < right){
			if (left > 0)
				generate(str + '(', left - 1, right);
			generate(str + ')', left, right - 1);
		}
	}
	vector<string> generateParenthesis(int n) {
		string str;
		generate(str, n, n);
		return ans;
	}
};

39. 组合总和(回溯)

1、递归函数包括原数组、已组合数组、目标值和当前序号
2、每次递归包括两种选择,选择当前序号的数,不选择当前序号的数跳过
3、边界判断包括最后目标值正好等于0,序号越界

class Solution {
public:
    void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
        if (idx == candidates.size()) 
            return;
        if (target == 0) {
            ans.emplace_back(combine);
            return;
        }
        // 直接跳过
        dfs(candidates, target, ans, combine, idx + 1);
        // 选择当前数
        if (target - candidates[idx] >= 0) {
            combine.emplace_back(candidates[idx]);
            dfs(candidates, target - candidates[idx], ans, combine, idx);
            combine.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> ans;
        vector<int> combine;
        dfs(candidates, target, ans, combine, 0);
        return ans;
    }
};

40. 组合总和 II(回溯)

1、上一题是给的数不重复,但是数可以不限制选,这题是给的数重复,只能选一次,还是用回溯
2、回溯前先排序,把给的数重复的放在一块,然后扫一遍,统计每个数出现的次数
3、递归前要么跳过该数,要么递归使用该数的最多次,用统计过的二维数组判断还能用几次递归

class Solution {
private:
    vector<pair<int, int>> freq;
    vector<vector<int>> ans;
    vector<int> sequence;
public:
    void dfs(int pos, int rest) {
        if (rest == 0) {
            ans.push_back(sequence);
            return;
        }
        if (pos == freq.size() || rest < freq[pos].first) 
            return;
        dfs(pos + 1, rest);
        int most = min(rest / freq[pos].first, freq[pos].second);
        for (int i = 1; i <= most; ++i) {
            sequence.push_back(freq[pos].first);
            dfs(pos + 1, rest - i * freq[pos].first);
        }
        for (int i = 1; i <= most; ++i) 
            sequence.pop_back();
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        for (int num: candidates) {
            if (freq.empty() || num != freq.back().first) {
                freq.emplace_back(num, 1);
            } else 
                ++freq.back().second;
        }
        dfs(0, target);
        return ans;
    }
};

46. 全排列(回溯)

1、用二维数组代替输出数组。
2、用左右标记代替标记数组,但这样的操作输出不是有序,若有序应使用标记数组。

    void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
        // 所有数都填完了
        if (first == len) {
            res.emplace_back(output);
            return;
        }
        for (int i = first; i < len; ++i) {
            // 动态维护数组
            swap(output[i], output[first]);
            // 继续递归填下一个数
            backtrack(res, output, first + 1, len);
            // 撤销操作
            swap(output[i], output[first]);
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int> > res;
        backtrack(res, nums, 0, (int)nums.size());
        return res;
    }

48. 旋转图像(辅助数组)

找规律得到数组旋转后的通项公式,建立一个辅助数组保存

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        // C++ 这里的 = 拷贝是值拷贝,会得到一个新的数组
        auto matrix_new = matrix;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        // 这里也是值拷贝
        matrix = matrix_new;
    }
};

51. N 皇后(回溯)

1、N皇后使用回溯法,逐个判断当前点行列、正反对角线都没有重复的点就往下搜索
2、行用遍历保证不同,列和正反对角线用三个数组标记,正反对角线的规律是行列差、行列和相同,值相同说明重复了

class Solution {
public:
    void dfs(int row, int n, vector<vector<string>>& res, vector<string> a, vector<int> col, vector<int> dg, vector<int> adg){
        if(row == n){ //符合条件,直接存进数组
            res.push_back(a);
            return ;
        }
        for(int i = 0; i < n; i++){
            if(!col[i] && !dg[row - i + n] && !adg[row + i]){//说明该位置合法
            //这里注意一下,因为行与列的差值可能为负,所以dg下标要加一个n
                col[i] = dg[row - i + n] = adg[row + i] = 1; //将该位置的列,正对角线,反对角线的值都置1
                a[row][i] = 'Q'; //设置Q
                dfs(row + 1, n, res, a, col, dg, adg); //深度优先搜索
                col[i] = dg[row - i + n] = adg[row + i] = 0; //重置,以便下次查找该行合适位置
                a[row][i] = '.';
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> res; //存储最终结果
        vector<string> a(n, string(n, '.')); //临时结果
        vector<int> col(n, 0);  //存储已有的列值
        vector<int> dg(2 * n, 0); //存储已有的正对角线的值
        vector<int> adg(2 * n, 0); //存储已有的反对角线的值
        dfs(0, n, res, a, col, dg, adg); //深度优先搜索回溯
        return res;
    }
};

54. 螺旋矩阵(模拟)

用二维数组存四个方向,vector<vector>,用now记录当前方向,一直走到矩阵边界换方向

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
    //按照题目要求搜一遍即可
    //判空必须放在开头,并且不能用变量去表示矩阵的长宽
    if(matrix.size()==0||matrix[0].size()==0) return {};
    int m=matrix.size(),n=matrix[0].size();
    vector<vector<int>> flag(m,vector<int>(n,0));
    vector<vector<int>> dir={{0,1},{1,0},{0,-1},{-1,0}};
    int count=0,now=0,x=0,y=0;
    vector<int> ans;
    while(count<n*m){
        ans.push_back(matrix[x][y]);
        flag[x][y]=1;
        if(x+dir[now][0]<0||x+dir[now][0]>=m||y+dir[now][1]<0||y+dir[now][1]>=n||flag[x+dir[now][0]][y+dir[now][1]]==1) 
            now=(now+1)%4;
        x+=dir[now][0];
        y+=dir[now][1];
        count++;
    }
    return ans;
    }
};

59. 螺旋矩阵 II(模拟)

碰到边界或者已存在则换方向

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int maxNum = n * n;
        int curNum = 1;
        vector<vector<int>> matrix(n, vector<int>(n));
        int row = 0, column = 0;
        vector<vector<int>> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};  // 右下左上
        int directionIndex = 0;
        while (curNum <= maxNum) {
            matrix[row][column] = curNum;
            curNum++;
            int nextRow = row + directions[directionIndex][0], nextColumn = column + directions[directionIndex][1];
            if (nextRow < 0 || nextRow >= n || nextColumn < 0 || nextColumn >= n || matrix[nextRow][nextColumn] != 0) {
                directionIndex = (directionIndex + 1) % 4;  // 顺时针旋转至下一个方向
            }
            row = row + directions[directionIndex][0];
            column = column + directions[directionIndex][1];
        }
        return matrix;
    }
};

78. 子集(回溯)

由于给的不存在重复,也不存在是否重复添加问题,所以就选择加或不加两种情况

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;
    void dfs(int cur, vector<int>& nums) {
        if (cur == nums.size()) {
            ans.push_back(t);
            return;
        }
        t.push_back(nums[cur]);
        dfs(cur + 1, nums);
        t.pop_back();
        dfs(cur + 1, nums);
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(0, nums);
        return ans;
    }
};

79. 单词搜索(深搜)

遍历所有格子作为起点深搜,只要找到就返回

class Solution {
public:
 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(dfs(board,word,i,j,0))
                    return true;
        return false;
    }
    bool dfs(vector<vector<char>>& board, string word,int i,int j,int k)
    {
        if (i<0||i>=board.size()||j<0||j>=board[0].size()||board[i][j]!=word[k]) return false;
        //k等于word大小不是board大小
        if (k==word.size()-1) return true;
        int res;
        char temp=board[i][j];
        board[i][j]='0';
        vector<int> x={-1,1,0,0};
        vector<int> y={0,0,1,-1};
        //用数组记录变化,不用写四个dfs
        for(int l=0;l<4;l++) {
            //用m,n记录暂时变化,保证不改变原i,j
            int m=x[l]+i,n=y[l]+j;
            if (dfs(board,word,m,n,k+1)) return true;
        }
        board[i][j]=temp;
        return false;
    }
};

90. 子集 II(排序+回溯)

1、与上一个子集问题区别在于,可能给定元素中包含重复的元素
2、带重复元素问题必须先排序,把重复元素挨在一起方便判断
3、排序后回溯时进行剪枝,如果当前元素和之前相同,并且上一个元素没有选择,则不回溯了返回
4、回溯函数加上一个bool判断上一个值是否已经被选择

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;
    void dfs(bool choosePre, int cur, vector<int> &nums) {
        if (cur == nums.size()) {
            ans.push_back(t);
            return;
        }
        dfs(false, cur + 1, nums);
        if (!choosePre && cur > 0 && nums[cur - 1] == nums[cur]) {
            return;
        }
        t.push_back(nums[cur]);
        dfs(true, cur + 1, nums);
        t.pop_back();
    }
    vector<vector<int>> subsetsWithDup(vector<int> &nums) {
        sort(nums.begin(), nums.end());
        dfs(false, 0, nums);
        return ans;
    }
};

93. 复原 IP 地址(回溯)

1、用string存结果,用数组存临时的四个数,用回溯查找,如果找到第四段并且刚好遍历完字符串,就把数组转成字符串返回
2、如果没找到四串就到结尾则返回,碰到前导0立马下一轮
3、剩下的进行回溯,一个个数字加入并且回溯

class Solution {
private:
    static constexpr int SEG_COUNT = 4;
private:
    vector<string> ans;
    vector<int> segments;
public:
    void dfs(const string& s, int segId, int segStart) {
        // 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
        if (segId == SEG_COUNT) {
            if (segStart == s.size()) {
                string ipAddr;
                for (int i = 0; i < SEG_COUNT; ++i) {
                    ipAddr += to_string(segments[i]);
                    if (i != SEG_COUNT - 1) 
                        ipAddr += ".";
                }
                ans.push_back(move(ipAddr));
            }
            return;
        }
        // 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
        if (segStart == s.size()) 
            return;
        // 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
        if (s[segStart] == '0') {
            segments[segId] = 0;
            dfs(s, segId + 1, segStart + 1);
        }
        // 一般情况,枚举每一种可能性并递归
        int addr = 0;
        for (int segEnd = segStart; segEnd < s.size(); ++segEnd) {
            addr = addr * 10 + (s[segEnd] - '0');
            if (addr > 0 && addr <= 0xFF) {
                segments[segId] = addr;
                dfs(s, segId + 1, segEnd + 1);
            } else 
                break;
        }
    }
    vector<string> restoreIpAddresses(string s) {
        segments.resize(SEG_COUNT);
        dfs(s, 0, 0);
        return ans;
    }
};

200. 岛屿数量(深搜)

1、遍历碰到所有为1的岛屿设为起点,岛屿数量加1,传入该点行列开始深搜
2、将这点附近能走到的所有岛屿设为0,返回

class Solution {
private:
    void dfs(vector<vector<char>>& grid, int r, int c) {
        int nr = grid.size();
        int nc = grid[0].size();
        grid[r][c] = '0';
        if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c);
        if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c);
        if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1);
        if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1);
    }
public:
    int numIslands(vector<vector<char>>& grid) {
        int nr = grid.size();
        if (!nr) return 0;
        int nc = grid[0].size();
        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    dfs(grid, r, c);
                }
            }
        }
        return num_islands;
    }
};

240. 搜索二维矩阵 II(线性查找)

1、从左上角开始搜索,判断大小不知道走哪边,深搜广搜时间复杂度较高
2、从右上角开始搜索,可以根据大小进行线性搜索,取巧方式较深搜复杂度较低

    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        //前面必须先判断vector是否为空!!否则对vector取长度会造成内存崩溃
        if (matrix.empty()) return false;
        int n = matrix.size(),m = matrix[0].size(),x = 0, y = m - 1;
        while (x < n && y >= 0){
            if (matrix[x][y] == target) return true;
            if (matrix[x][y] > target) y--;
            //这里必须写else!!!!不能用多个if进行判断,否则造成xy已经修改,内存崩溃
            else x++;
        }
        return false;
    }

301. 删除无效的括号(深搜)

class Solution {
public:
    unordered_set<string> sets;
    /*
    * @param s: 源字符串
    * @param index: 当前源字符串索引
    * @param str: 记录结果
    * @param el: 当前可以删除的'('的数量
    * @param er: 当前可以删除的')'的数量
    * @param cntl: 记录当前str中错误的'('的数量
    * @param cntr: 记录当前str中错误的')'的数量
    */
    void dfs(string &s, int index, string &str, int el, int er, int cntl, int cntr){
        // 剪枝
        if(cntr > cntl || el < 0 || er < 0) return;
        // 结束条件
        if(index == s.length()){
            if(cntl == 0 && cntr == 0)
                sets.insert(str);
            return;
        }
        // 当前字符不是括号,直接跳过
        if(s[index] != '(' && s[index] != ')'){
            str += s[index];
            dfs(s, index+1, str, el, er, cntl, cntr);
            str.erase(str.length()-1, 1);
        }else{
            // 不删除当前括号,需要记录当前str中错误的左右括号的数量
            str += s[index];
            int cl = cntl, cr = cntr;
            if(s[index] == '(') cl++;
            else{
                if(cl == 0) cr++;
                else cl--;
            }
            dfs(s, index+1, str, el, er, cl, cr);
            str.erase(str.length()-1, 1);
            // 删除当前括号,修改可删除的左右括号数量
            if(s[index] == '(') --el;
            else --er;
            dfs(s, index+1, str, el, er, cntl, cntr);
        }
    }
    vector<string> removeInvalidParentheses(string s) {
        vector<string> res;
        // 统计源字符串中无效括号数目
        int el = 0, er = 0;
        for(int i = 0; i < s.length(); ++i){
            if(s[i] == '(') el++;
            else if(s[i] == ')'){
                if(el == 0) er++;
                else el--;
            }
        }
        string str = "";
        dfs(s, 0, str, el, er, 0, 0);
        for(auto it = sets.begin(); it != sets.end(); ++it)
            res.push_back(*it);
        return res;
    }
};

498. 对角线遍历(矩阵模拟)

1、向右上遍历时(判断顺序不可颠倒,因为在遍历到右上角元素时,先判断“第一行”的话下标会溢出)
元素在最后一列,向下走
元素在第一行,向右走
2、向左下遍历时
元素在最后一行,向右
元素在第一列,向下
3、其余情况就是正常的向右上或者左下遍历

class Solution 
{
public:
    vector<int> findDiagonalOrder(vector<vector<int>>& matrix) 
    {
        //判空
        if (matrix.size() == 0) return {};
        int m = matrix.size(),n = matrix[0].size(),x = 0,y = 0;
        int count = m * n;
        vector<int> ans;
        for (int i = 0; i < count; ++i){
            ans.push_back(matrix[x][y]);
            if ((x+y) % 2 == 0){
                if (y == n-1)   //最后一列,向下
                    x++;
                else if (x == 0)//第一行,向右
                    y++;
                else           //向右上{
                    x--;
                    y++;
                }
            }else{
                if (x == m-1)   //最后一行,向右
                    y++;
                else if (y == 0)//第一列,向下
                    x++;
                else           //向左下{
                    x++;
                    y--;
                }
            }
        }
        return ans;
    }
};

695. 岛屿的最大面积(深搜)

每次访问过的位置设置为0,记录访问过的最大面积

class Solution {
    int dfs(vector<vector<int>>& grid, int cur_i, int cur_j) {
        if (cur_i < 0 || cur_j < 0 || cur_i == grid.size() || cur_j == grid[0].size() || grid[cur_i][cur_j] != 1) 
            return 0;
        grid[cur_i][cur_j] = 0;
        int di[4] = {0, 0, 1, -1};
        int dj[4] = {1, -1, 0, 0};
        int ans = 1;
        for (int index = 0; index != 4; ++index) {
            int next_i = cur_i + di[index], next_j = cur_j + dj[index];
            ans += dfs(grid, next_i, next_j);
        }
        return ans;
    }
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int ans = 0;
        for (int i = 0; i != grid.size(); ++i) 
            for (int j = 0; j != grid[0].size(); ++j) 
                ans = max(ans, dfs(grid, i, j));
        return ans;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值