30-40(leetcode)

31.下一个排列

原题链接:
思路:
在这里插入图片描述

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
          int k = nums.size() - 1;
          //从后往前,第一次遍历,找到第一个非降序的位置k-1
          while(k > 0 && nums[k-1] >= nums[k]) k--;
          //全部逆序的情况,直接反转序列
          if(k <= 0){
              reverse(nums.begin(), nums.end());
          }else{
              int  t = k;
              //从降序序列中,找到最接近nums[k-1]的值,并交换
              while(t < nums.size() && nums[t] > nums[k-1]) t++;
              swap(nums[t-1], nums[k - 1]);
              //在反转降序序列
              reverse(nums.begin() + k, nums.end());
          }
    }
};

32.最长有效括号

原题链接:
思路:
在这里插入图片描述

class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> stk;
        int res = 0;
        //枚举每一个字符,start 用于记录非法的右括号位置
        for(int i = 0, start = -1; i < s.size(); i++){
            //将左括号的下标压入栈
            if(s[i] == '(') stk.push(i);
            else{
                //当前找右括号对应合法括号的最大长度
                if(stk.size()){
                    //将与当前右括号匹配的左括号下标出栈
                    stk.pop();
                    if(stk.size()) res = max(res, i - stk.top());
                    else res = max(res, i - start);
                }else{
                    start = i;
                }
            }
        }
        return res;
    }
};

33.搜索旋转排序数组

原题链接
思路:
在这里插入图片描述

class Solution {
    public int search(int[] nums, int target) {
        if(nums == null || nums.length < 1) return -1;

        // 二分找pivot, 这里用性质>=nums[0], 所以当找到边界时, 它将是最后一个比nums[0]大的数
        int l = 0, r = nums.length-1;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(nums[mid] >= nums[0]) l = mid;
            else r = mid - 1;
        }

        // 确定target在哪一段中
        if(target >= nums[0]) {l = 0;}
        else {l = r+1; r = nums.length-1;}

        // 二分找目标值
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(target >= nums[mid]) l = mid;
            else r = mid - 1;
        }

        // 这里写成nums[r], 当数组只有一个元素时, 两个二分查找代码都没有走, 而l在上面被+1, 这时会越界, 而r是length-1还是0, 不会产生越界
        if(nums[r] == target) return l;
        else return -1;
    }
}

34.在排序数组中查找元素的第一个和最后一个位置

原题链接:
思路:
在这里插入图片描述

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1, -1};

        int l = 0, r = nums.size() - 1;
        while(l < r){
            int mid = l + r  >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        if(nums[r] != target) return {-1,-1};

        int L = r;
        l = 0, r = nums.size() - 1;
        while(l < r){
            int mid = l + r +1 >> 1;
            if(nums[mid] <= target) l = mid;
            else r = mid - 1;
        }

        return {L, r};
    }
};

35.搜索插入位置

原题链接:
思路:
使用二分,根据两段性,找到要插入位置的前一个节点

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int l = 0, r = nums.size();

        while(l < r){
            int mid = l + r >>1;
            if(nums[mid] >= target) r = mid;
            else l  = mid + 1;
        }
        return l;
    }
};

36.有效的数独

原题链接:
思路:
1.使用bool数组记录每行、每列、每个九宫格的数字情况

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        bool st[9];

        // 判断行
        for (int i = 0; i < 9; i ++ ) {
            memset(st, 0, sizeof st);
            for (int j = 0; j < 9; j ++ ) {
                if (board[i][j] != '.') {
                    int t = board[i][j] - '1';
                    if (st[t]) return false;
                    st[t] = true;
                }
            }
        }

        // 判断列
        for (int i = 0; i < 9; i ++ ) {
            memset(st, 0, sizeof st);
            for (int j = 0; j < 9; j ++ ) {
                if (board[j][i] != '.') {
                    int t = board[j][i] - '1';
                    if (st[t]) return false;
                    st[t] = true;
                }
            }
        }

        // 判断小方格
        for (int i = 0; i < 9; i += 3)
            for (int j = 0; j < 9; j += 3) {
                memset(st, 0, sizeof st);
                for (int x = 0; x < 3; x ++ )
                    for (int y = 0; y < 3; y ++ ) {
                        if (board[i + x][j + y] != '.') {
                            int t = board[i + x][j + y] - '1';
                            if (st[t]) return false;
                            st[t] = true;
                        }
                    }
            }

        return true;
    }
};

37.解数独

原题链接:
思路:
(递归回溯)
预处理出 col、row 和 cell数组。
从 (0,0) 位置开始尝试并递归。遇到 . 时,枚举可以填充的数字
如果成功到达结尾,则返回 true,告知递归可以终止。

class Solution {
public:
    //定义行、列、小方阵,用于判断0~9的数字有没有
    bool row[9][9], col[9][9], cell[3][3][9];
    void solveSudoku(vector<vector<char>>& board) {
    //将行、列、小方阵清空
    memset(row, 0, sizeof row);
    memset(col, 0, sizeof col);
    memset(cell, 0, sizeof cell);
    // 将已经填入的数字保存到行列小方阵中
    for(int i = 0; i < 9; i++)
        for(int j = 0; j < 9; j++){
            if(board[i][j] != '.'){
                int t = board[i][j] - '1';
                row[i][t] = col[j][t] = cell[i/3][j/3][t] = true; 
            }
        }
    //从board左上角开始深搜
    dfs(board, 0, 0);
        
    }

    bool dfs(vector<vector<char>>&  board, int x, int y){
        //如果列搜到最右边,则从下一行开始
        if(y == 9) x++, y = 0;
        //如果x=9,则搜索完成
        if(x == 9) return true;

        //有数字,搜下一个位置
        if(board[x][y] != '.') return dfs(board, x,y+1);
        //没有数字,则判断该填什么数字
        for(int i = 0; i < 9; i++){
            //如果该数字当前行/列/小方阵都没有,则填入
            if(!row[x][i] && !col[y][i] && !cell[x/3][y/3][i]){
                board[x][y]='1' + i;
                row[x][i] = col[y][i] = cell[x/3][y/3][i] = true;
                // 如果搜到了返回true
                if(dfs(board, x ,y+1)) return true;
                //否则当前搜索是无解的,则进行回溯,返回该位置数字的默认状态
                board[x][y] = '.';
                row[x][i] = col[y][i] = cell[x/3][y/3][i] = false;
            }
        }
        return false;
    }
};

38.外观数列

原题链接:
思路:
读懂题意就比较简单了,利用双指针,统计相同数字的长度,在连起来

class Solution {
public:
    string countAndSay(int n) {
        //初始字符串
        string s = "1";
        
        //计算到第n项
        for(int i = 0; i < n -1; i++){
            string t;
            for(int j = 0; j <s.size();){
                int k = j + 1;
                //统计相同数字字符的个数
                while(k < s.size() && s[j] == s[k]) k++;
                //将个数与数字连接到一起
                t += to_string(k - j) + s[j];
                //将j更新到k的位置
                j = k;
            }
            s = t;
        }
        return s;
    }
};

39.组合总和

原题链接:
思路:
递归枚举

class Solution {
public: 

    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> combinationSum(vector<int>& c, int target) {
        dfs(c, 0, target);
        return ans;
    }

    void dfs(vector<int>& c, int u,  int target){
        // 如果搜到了,将path加入答案中
        if(target == 0){
            ans.push_back(path);
            return;
        }
        //搜不到,返回
        if(u == c.size()) return;
        //暴力搜索每个数字的数量
        for(int i = 0; c[u] * i <= target; i++){
            dfs(c, u+1, target-c[u] * i);
            path.push_back(c[u]);
        }
        // 恢复现场
        for(int i = 0; c[u] * i <= target; i++){
            path.pop_back();
        }
    }
};

40.组合总和II

原题链接:
思路:
和上一题类似,在暴搜每个数字时加一个个数限制

class Solution {
public:

    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> combinationSum2(vector<int>& c, int target) {
        //先排序,为了统计每个数字出现的个数
        sort(c.begin(), c.end());
        dfs(c, 0, target);

        return ans;
    }

    void dfs(vector<int>&  c,  int u, int target){
        if(target == 0){
            ans.push_back(path);
            return;
        }
        if(u == c.size()) return;
        // 统计当前数字在该数组中有几个
        int k = u + 1;
        while(k <c.size() && c[u] == c[k]) k++;
        int cnt = k - u;

        //加一个限制,就是当前数字枚举的个数
        for(int i = 0; c[u] * i <= target && i <= cnt; i++){
            dfs(c, k, target - c[u] * i);
            path.push_back(c[u]);
        }

        for(int i = 0; c[u] * i <= target && i <= cnt; i++){
            path.pop_back();
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是电话号码的字母组合问题的 C++ 代码实现: ``` class Solution { public: vector<string> letterCombinations(string digits) { unordered_map<char, string> mapping = { {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"}, {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"} }; vector<string> res; if (digits.empty()) { return res; } string combination; backtrack(res, combination, digits, 0, mapping); return res; } void backtrack(vector<string>& res, string& combination, string& digits, int index, unordered_map<char, string>& mapping) { if (index == digits.size()) { res.push_back(combination); return; } for (char c : mapping[digits[index]]) { combination.push_back(c); backtrack(res, combination, digits, index + 1, mapping); combination.pop_back(); } } }; ``` 其中 `letterCombinations` 函数用来生成所有的字母组合,`backtrack` 函数用来进行回溯操作。在 `letterCombinations` 函数中,首先根据数字字符和字母的映射关系创建了一个 `unordered_map` 对象 `mapping`。然后定义了一个空字符串 `combination` 和一个空 vector `res` 来保存最终结果。最后调用了 `backtrack` 函数来生成所有的字母组合。在 `backtrack` 函数中,首先判断是否达到了数字字符串的末尾,如果是,则将当前的 `combination` 字符串保存到 `res` 中。否则,遍历当前数字字符所能表示的所有字母,依次加入到 `combination` 字符串中,然后递归调用 `backtrack` 函数,添加下一个数字字符所能表示的字母。递归完成后,需要将 `combination` 字符串还原到上一个状态,以便进行下一次回溯。最终返回 `res` 数组即可。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值