leetcode 31~40

lc 31~40

acwing二分模板

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

31. 下一个排列

扫描,交换,翻转,实测用reverse比sort快很多

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        //逆序查找左边找到第一个下降的位置,然后再向右找到比该位置值大的最小值
        //两者交换,再将后面部分升序排列就行了;如果没找到下降得,直接升序排列
        int k = nums.size() - 1;
        while(k > 0 && nums[k - 1] >= nums[k]) k--;
        //此时,如果k<=0,说明左边没有小的数,那就直接升序
        if(k <= 0){
            reverse(nums.begin(), nums.end());//都可以sort,但浪费时间
        }else{
            //这时k左边(k-1)比k小,再去右边找到比该位置值大的最小值
            int t = k;
            while(t < nums.size() && nums[t] > nums[k - 1]) ++t;
            //此时t位置小于等于k-1位置,即t-1位置为要找的
            swap(nums[k - 1], nums[t - 1]);
            //再将后面部分升序排列
            reverse(nums.begin() + k, nums.end());
        }
    }
};

32. 最长有效括号

class Solution {
public:
    int longestValidParentheses(string s) {
        //首先明确,有效括号即任意前缀左括号不小于右括号,且整个串中两者数目相等
        //因此可以以每个刚开始不满足任意前缀左括号不小于右括号得位置为分界点,将整个串分组
        //那么有效括号串必然不可能跨越任意两组,因此在每组中更新最大长度就行
        int len = s.length(), res = 0;
        stack<int> stk;//栈中存放左括号下标
        //start代表分界点,起始位置分界点当然在0得左边即-1了
        for(int i = 0, start = -1; i < len; i++){
            //每次s[i]两种情况 ( 或 )
            if(s[i] == '(') stk.push(i);
            else{
                //为右括号时,此时要配对,栈两种情况,空与非空
                if(stk.empty()) start = i; //空得话说明,开始不满足上面条件,即分界点
                else{
                    stk.pop();//先弹栈,弹栈后两种情况,空与非空
                    //空了要想更新长度,只能减去分界点,非空就减去栈顶(为左边第一个还没匹配上得)就行
                    //为什么减去栈顶呢,因为此时栈顶还没匹配上,因此栈顶为计算长度得分界点
                    if(stk.empty()) res = max(res, i - start);
                    else res = max(res, i - stk.top());
                }
            }
        }
        return res;
    }
};

33. 搜索旋转排序数组

二分

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty()) return -1;
        //先二分找到左边有序区间的右端点,依据:左边一定>=nums[0],右边<nums[0]
        int l = 0, r = nums.size() - 1;
        while(l < r){
            //为什么加1,个人理解,最后l,r一定会缩到相邻r=l+1,但是最终跳出循环的结果一定是l=r
            //所以这就要看要移动哪个,因为我们是将区间分为了[l,mid-1],[mid,r],而我们最终要找的
            //归在左边,所以要将r-1,那么就要把mid归在右边,即nums[mid]<nums[0],所以要加1
            //如[1,2],右端点左边显然1;l=0,r=1;如果不加1,mid永远为0,此时走的是l=mid,即永远l与r不相遇
            //那就不对了,所以+1,左边向右靠
            int mid = l + r + 1 >> 1;
            //此时mid一定在左边有序区间,则向右包含该点查找端点
            if(nums[mid] >= nums[0]) l = mid;
            else r = mid - 1;
        }
        //此时l=r等于左边有序区间右端点;两种情况,ta在左边,或右边
        if(target >= nums[0]) l = 0;
        else l += 1, r = nums.size() - 1;
        while(l < r){
            //此时为什么不加1,同理分析,因为区间分为[l,mid],[mid+1,r],mid归在左边
            //最后情况r=l+1时,所以r向左靠才可能找到ta
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        //注意这里不能用nums[l],如当[1] 0时,即只有一个,r=1-1=0,l+=1越界;
        //只要时k=0的都会这样,如[1,2,3,4]最终lr都在3,如果ta=5,l加一越界,所以只有r永远不可能越界,用r
        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, a = -1;
        //找分界点,左边的第一个,与右边最后一个
        //1.找左端点,[l,mid] [mid+1,r]
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        if(nums[r] == target)
            a = r;
        else return {-1, -1};//如果没有找到,就返回-1,-1
        //找右端点(能走到这里必然有,不会-1,-1)
        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 {a, r};
    }
};

35. 搜索插入位置

二分,值得好好思考

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        //[l,mid], [mid+1,r]从左向右找到第一个>=target得值
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        //如果上面得r处值选size,直接return l就行,因为最终查的值范围[0,size],满足插入位置得所有可能情况
        //如果最终l所在值<ta,说明插入位置在右边
        if(nums[l] < target) return l + 1;
        //如果=ta,l;>ta,插在l处
        return l;
    }
};

36. 有效的数独

状态压缩

class Solution {
public:
    int r[10], c[10], box[10];//分别存储每一行,列,方块得状态,
    //(1~9)每一位分别代表1~9是否出现
    bool isValidSudoku(vector<vector<char>>& board) {
        int len1 = board.size(), len2 = board[0].size();
        for(int i = 0; i < len1; i++){
            for(int j = 0; j < len2; j++){
                if(board[i][j] == '.') continue;
                else{
                    int digit = board[i][j] - '0';
                    if(r[i] & (1 << digit)) return false;
                    else r[i] |= (1 << digit);
                    if(c[j] & (1 << digit)) return false;
                    else c[j] |= (1 << digit);
                    //box所在行(i/3),前面行和(i / 3) * 3,所在列j/3(都是从0开始)
                    int index = (i / 3) * 3 + j / 3 + 1;
                    if(box[index] & (1 << digit)) return false;
                    else box[index] |= (1 << digit);
                }
            }
        }
        return true;
    }
};

简单模拟

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 a = board[i][j] - '1';
                    if(st[a]) return false;
                    st[a] = 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 a = board[j][i] - '1';
                    if(st[a]) return false;
                    st[a] = true;
                }
            }
        }     
         //检查每一个方块,(i,j)代表每个小方块得左上点
        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 a = board[i + x][j + y] - '1';
                            if(st[a]) return false;
                            st[a] = true;
                        }
                    }
                }
            }
        }          
        return true;
    }
};

37. 解数独

二维DFS + 状态压缩(不过力扣上时间比不状态压缩慢挺多)

class Solution {
public:
    int r[9], c[9], box[9];
    void solveSudoku(vector<vector<char>>& board) {
        for(int i = 0; i < 9; i++)
            for(int j = 0; j < 9; j++){
                if(board[i][j] != '.'){
                    int digit = board[i][j] - '0';
                    r[i] |= (1 << digit), c[j] |=  (1 << digit);
                    box[i / 3 * 3 + j / 3] |= (1 << digit);
                }
            }
        dfs(board, 0, 0);
    }

    bool dfs(vector<vector<char>>& board, int x, int y){
        if(y == 9) x++, y = 0;
        if(x == 9) return true;
        if(board[x][y] != '.') return dfs(board, x, y + 1);
        for(int i = 1; i <= 9; i++){    //人傻了,box y忘了除以3,找了非常久
            if(!(r[x] & (1 << i)) && !(c[y] & (1 << i)) && !(box[x / 3 * 3 + y / 3] & (1 << i)))           {
                cout << r[x] << " " << c[y] << endl;
                r[x] |= (1 << i), c[y] |= (1 << i);
                box[x / 3 * 3 + y / 3] |= (1 << i);
                board[x][y] = i + '0';
                if(dfs(board, x, y + 1)) return true;
                r[x] ^= (1 << i), c[y] ^= (1 << i);
                box[x / 3 * 3 + y / 3] ^= (1 << i);
                board[x][y] = '.';
            }
        }
        return false;
    }
};

y总的

class Solution {
public:

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

        dfs(board, 0, 0);
    }

    bool dfs(vector<vector<char>>& board, int x, int y) {
        if (y == 9) x ++, y = 0;
        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;
                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";
        while(--n){
            string t;
            for(int i = 0; i < s.size();){
                int k = i + 1;
                while(k < s.size() && s[k] == s[i]) k++;
                t += to_string(k - i) + s[i];
                i = k;
            }
            s = t;
        }
        return s;
    }
};

39. 组合总和

DFS,看着像完全背包,写法值得学习

class Solution {
public:
    vector<vector<int>> ans;//存放答案
    vector<int> path;//存放路径

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

    //u代表选取candidates的索引
    void dfs(vector<int>& cs, int u, int target){
        //说明路径已经达到target
        if(!target){
            ans.push_back(path);
            return;
        }
        if(u == cs.size()) return;//所有所有索引都走过了
        //开始枚举该层要选几个
        for(int i = 0; i * cs[u] <= target; i++){
            dfs(cs, u + 1, target - i * cs[u]);//搜下层
            path.push_back(cs[u]);//存放路径
        }
        for(int i = 0; i * cs[u] <= target; i++){
            path.pop_back();//撤回路径
        }
    }
};

40. 组合总和 II

DFS,有重复,先排序,多重背包

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> path;

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        //与39不同的是此题有重复元素,为多重背包衍生,所以要计算每个元素的数目
        sort(candidates.begin(), candidates.end());
        dfs(candidates, 0, target);
        return ans;
    }

    void dfs(vector<int>& cs, int u, int target){
        if(target == 0){
            ans.push_back(path);
            return;
        }
        if(u == cs.size()) return;
        //计算cs[u]的数目
        int k = u + 1, cnt = 0;
        while(k < cs.size() && cs[k] == cs[u]) k++;
        cnt = k - u;
        for(int i = 0; i <= cnt && i * cs[u] <= target; i++){
            dfs(cs, k, target - i * cs[u]);
            path.push_back(cs[u]);
        }
        for(int i = 0; i <= cnt && i * cs[u] <= target; i++)
            path.pop_back();
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值