LeetCode刷题记31-40

LeetCode 31. 下一个排列

题目类型

  1. 数组
  2. 找规律题

题目链接

https://leetcode.cn/problems/next-permutation/

思路

建议画图用例子模拟

[2 3 5 4 1] => [2 4 1 3 5]

  1. 先用指针k从后往前找,找到第一个不满足nums[k - 1]>nums[k]的位置,即k即往后的都为降序
  2. 如果k在nums起始位置,则将整个数组反转,否则执行3
  3. 用指针t从指针k所在位置往右寻找,找到第一个大于nums[k-1]的数,此时整个数为nums[t - 1]
  4. 并且将nums[k-1]和nums[t - 1]互换
  5. 将k后面的序列反转

AC代码

class Solution {
public:
    void nextPermutation(vector<int>& nums) &#123;
        int k = nums.size() - 1;
        // 2 3 5 4 1
        // 此时处于5这个位置
        while (k > 0 && nums[k - 1] >= nums[k]) k --;
        // 整个序列都是降序
        if (k <= 0) 
        &#123;
            reverse(nums.begin(), nums.end());
        &#125;
        else
        &#123;
            int t = k;
            while (t <= nums.size() - 1 && nums[t] > nums[k - 1]) t ++;
            swap(nums[k - 1], nums[t - 1]);
            reverse(nums.begin() + k, nums.end());
        &#125;
    &#125;
&#125;;

LeetCode 32. 最长有效括号

题目类型

  1. 动态规划

题目链接

https://leetcode.cn/problems/longest-valid-parentheses/

思路

AC代码


LeetCode 33. 搜索旋转排序数组

题目类型

  1. 双指针
  2. 二分(最优解)

题目链接

https://leetcode.cn/problems/search-in-rotated-sorted-array

思路

朴素做法和双指针算法比较简单,但是都没有利用旋转这个前提

二分做法:

  1. 利用二分寻找分界点位置
  2. 两个有序序列的分界点画图很容易知道是nums[0]
  3. 如果target大于nums[0]说明在第一个区间 如果小于nums[0]说明在第二个区间
  4. 在target所在区间再进行一次二分

AC代码1

// 朴素做法
class Solution &#123;
public:
    int search(vector<int>& nums, int target) &#123;
        for (int i = 0; i < nums.size(); i ++)
        &#123;
            if (nums[i] == target) return i;
        &#125;
        return -1;
    &#125;
&#125;;

AC代码2

// 双指针做法
class Solution &#123;
public:
    int search(vector<int>& nums, int target) &#123;
        int n = nums.size() - 1;
        int i = 0, j = n;
        while (i <= j)
        &#123;
            if (nums[i] != target) i ++;
            else return i;
            if (nums[j] != target) j --;
            else return j;
        &#125;
        return -1;
    &#125;
&#125;;

AC代码3(最优解)

class Solution &#123;
public:
    int search(vector<int>& nums, int target) &#123;
        if (nums.empty()) return -1;
        // 用二分先找到两个有序序列的分段点位置
        int l = 0, r = nums.size() - 1;
        while (l < r)
        &#123;
            int mid = l + r + 1 >> 1;
            if (nums[mid] >= nums[0]) l = mid;
            else r = mid - 1;
        &#125;

        // 这里两个序列有个分界点 Nums[0]
        if (target >= nums[0]) l = 0; // 在第一个区间
        else l = r + 1, r = nums.size() - 1; // 在第二个区间

        while (l < r)
        &#123;
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        &#125;

        if (nums[r] == target) return r;
        else return -1;
    &#125;
&#125;;

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

题目类型

  1. 双指针
  2. 二分(最优解,代码简单)

题目链接

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array

思路

使用双指针前后扫描定位,注意边界问题即可

使用二分最容易,也是最优解,代码最简洁

AC代码1

// 双指针
class Solution &#123;
public:
    vector<int> searchRange(vector<int>& nums, int target) &#123;
        vector<int> res;
        if (nums.size() == 0)
        &#123;
            res.push_back(-1), res.push_back(-1);
            return res;
        &#125;
        int i = 0, j = nums.size() - 1;
        if (i < j)
        &#123;
            while (i < nums.size() - 1 && nums[i] < target) i ++;
            while (j > 0 &&nums[j] > target) j --;
        &#125;

        if (nums[i] != target && nums[j] != target)
        &#123;
            res.push_back(-1); res.push_back(-1);
            return res;
        &#125;
        else if (i <= j && nums[i] == target && nums[j] == target)
        &#123;
            res.push_back(i); res.push_back(j);
        &#125;
        return res;
    &#125;
&#125;;

AC代码2

// 二分
class Solution &#123;
public:
    vector<int> searchRange(vector<int>& nums, int target) &#123;
        if (nums.empty()) return &#123;-1, -1&#125;;
        int n = nums.size();
        // 寻找左边界
        int l = 0, r = n - 1;
        while (l < r)
        &#123;
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        &#125;
        // 存储好左边界l
        int L = l;
        if (nums[l] != target) return &#123;-1, -1&#125;;

        // 寻找右边界
        l = 0, r = n - 1;
        while (l < r)
        &#123;
            int mid = l + r + 1>> 1;
            if (nums[mid] <= target) l = mid;
            else r = mid - 1;
        &#125;
        return &#123;L, r&#125;;
    &#125;
&#125;;

LeetCode 35. 搜索插入位置

题目类型

二分

题目链接

https://leetcode.cn/problems/search-insert-position

思路

使用二分搜素比较简单 直接AC,这里只讲搜索左边界的思路(对应是AC代码1),右边界的思路(对应AC代码2)

  1. 使用二分搜索≤target数的位置l
  2. 如果target > nums[l],则插入位置为l+1,否则为l

AC代码1

// 二分写法一:
class Solution &#123;
public:
    int searchInsert(vector<int>& nums, int target) &#123;
        int n = nums.size();
        int l = 0, r = n - 1;
        while (l < r)
        &#123;
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        &#125;
        if (target > nums[l]) return l + 1;
        else return l;
    &#125;
&#125;;

AC代码2

// 二分写法二
class Solution &#123;
public:
    int searchInsert(vector<int>& nums, int target) &#123;
        int n = nums.size();
        int l = 0, r = n - 1;
        while (l < r)
        &#123;
            int mid = l + r + 1 >> 1;
            if (nums[mid] <= target) l = mid;
            else r = mid - 1;
        &#125;
        if (target <= nums[r]) return r;
        else return r + 1;
    &#125;
&#125;;

LeetCode 36. 有效的数独

题目类型

  1. 遍历

题目链接

https://leetcode.cn/problems/valid-sudoku/

思路

判断行列即可 小方块用偏移量来判断

AC代码

class Solution &#123;
public:
    bool isValidSudoku(vector<vector<char>>& board) &#123;
        bool st[9];
        // 判断行
        for (int i = 0; i < 9; i ++)
        &#123;
            // 每次判断下一行初始化一下st
            memset(st, false, sizeof st);
            for (int j = 0; j < 9; j ++)
            &#123;
                if (board[i][j] != '.')
                &#123;
                    int t = board[i][j] - '1';
                    if (st[t]) return false;
                    st[t] = true;
                &#125;
            &#125;
        &#125;

        // 判断列
        for (int i = 0; i < 9; i ++)
        &#123;
            // 每次判断下一行初始化一下st
            memset(st, false, sizeof st);
            for (int j = 0; j < 9; j ++)
            &#123;
                if (board[j][i] != '.')
                &#123;
                    int t = board[j][i] - '1';
                    if (st[t]) return false;
                    st[t] = true;
                &#125;
            &#125;
        &#125;


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

LeetCode 37. 解数独

题目类型

  1. 暴搜
  2. dfs

题目链接

https://leetcode.cn/problems/sudoku-solver/submissions/

思路

  1. 对每个没放数的格子,从1-9dfs枚举
  2. 选择某个数i的时候 判断rows[x][i] cols[y][i] cells[x / 3][y / 3][i]的状态
  3. 以上判断如果为false,回溯;为true,继续搜索下一个点(x, y + 1)

AC代码

class Solution &#123;
public:
    // 行 列 小方格的枚举每个数的状态
    bool rows[9][9], cols[9][9], cells[3][3][9]; 
    void solveSudoku(vector<vector<char>>& board) &#123;
        memset(rows, false, sizeof rows);
        memset(cols, false, sizeof cols);
        memset(cells, false, sizeof cells);

        // 初始化
        for (int i = 0; i < 9; i ++)
        &#123;
            for (int j = 0; j < 9; j ++)
            &#123;
                if (board[i][j] != '.')
                &#123;
                    int t = board[i][j] - '1';
                    rows[i][t] = cols[j][t] = cells[i / 3][j / 3][t] = true;

                &#125;
            &#125;
        &#125;
        // 从(0, 0)开始填
        dfs(board, 0, 0);
    &#125;

    bool dfs(vector<vector<char>> &board, int x, int y)
    &#123;
        if (y == 9) y = 0, x ++;
        if (x == 9) return true;

        // 如果该位置已经有数了 则换下一个位置
        if (board[x][y] != '.') return dfs(board, x, y + 1);
        // 否则这个位置可以插入,枚举一下
        for (int i = 0; i < 9; i ++)
        &#123;
            if (!rows[x][i] && !cols[y][i] && !cells[x / 3][y / 3][i])
            &#123;
                rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = true;
                board[x][y] = '1' + i;
                if (dfs(board, x, y + 1)) return true;
                // 回溯
                board[x][y] = '.';
                rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = false;
            &#125;
        &#125;
        return false;
    &#125;
&#125;;

LeetCode 38. 外观数列

题目类型

题目链接

https://leetcode.cn/problems/count-and-say/

思路

AC代码

class Solution &#123;
public:
    string countAndSay(int n) &#123;
        string s = "1";
        // 变换n次
        for (int i = 0; i < n - 1; i ++)
        &#123;
            string t;
            for (int j = 0; j < s.size();)
            &#123;
                int k = j + 1;
                while (k < s.size() && s[k] == s[j]) k ++;
                t += to_string(k - j) + s[j];
                j = k;
            &#125;
            s = t;
        &#125;
        return s;
    &#125;
&#125;;

LeetCode 39. 组合总和

题目类型

  1. dfs

题目链接

https://leetcode.cn/problems/combination-sum/

思路

  1. 先对c排序
  2. 使用dfs搜索,枚举每个位置,当target == 0返回
  3. 并且枚举每个位置的倍数,当c[i] > target返回
  4. 对当前结果curpop一次

方法二是y总的解题方法

AC代码1

class Solution &#123;
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum(vector<int>& c, int target) &#123;
        vector<int> cur;
        sort(c.begin(), c.end());
        dfs(c, target, 0, cur);
        return res;
    &#125;

    void dfs(vector<int>& c, int target, int u, vector<int>& cur)
    &#123;
        if (target == 0)
        &#123;
            res.push_back(cur);
            return;
        &#125;

        for (int i = u; i < c.size(); i ++)
        &#123;
            if (c[i] > target) break;
            cur.push_back(c[i]);
            dfs(c, target - c[i], i, cur);
            // 回溯
            cur.pop_back();
        &#125;
    &#125;
&#125;;

AC代码2

// 无需排序版本 但是运行时间会长
class Solution &#123;
public:
    vector<vector<int>> combinationSum(vector<int>& c, int target) &#123;
        vector<vector<int>> res;
        vector<int> cur;
        dfs(c, target, 0, cur, res);
        return res;
    &#125;

    void dfs(vector<int>& c, int target, int u, vector<int>& cur, vector<vector<int>>& res)
    &#123;
        if (target == 0)
        &#123;
            res.push_back(cur);
            return;
        &#125;

        if (u == c.size()) return;

        for (int i = 0; c[u] * i <= target; i ++)
        &#123;
            dfs(c, target - c[u] * i, u + 1, cur, res);
            cur.push_back(c[u]);
        &#125;

        // 恢复现场
        for (int i = 0; c[u] * i <= target; i ++)
        &#123;
            cur.pop_back();
        &#125;
    &#125;
&#125;;

LeetCode 40. 组合总和 II

题目类型

  1. dfs

题目链接

https://leetcode.cn/problems/combination-sum-ii/

思路

跟上一题 LeetCode 39. 组合总和 I类似,只需要dfs的时候把重复元素统计一下,记为cnt,表示该位最多可以有cnt次倍数

AC代码

class Solution &#123;
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum2(vector<int>& c, int target) &#123;
        sort(c.begin(), c.end());
        vector<int> cur;
        dfs(c, target, 0, cur);
        return res;
    &#125;

    void dfs(vector<int>& c, int target, int u, vector<int>& cur)
    &#123;
        if (target == 0)
        &#123;
            res.push_back(cur);
            return;
        &#125;

        if (u == c.size()) return;

        // 有重复元素
        int k = u + 1;
        while (k < c.size() && c[k] == c[u]) k ++;
        int cnt = k - u;

        for (int i = 0; i <= cnt && c[u] * i <= target; i ++)
        &#123;
            dfs(c, target - c[u] * i, k, cur);
            cur.push_back(c[u]);
        &#125;

        // 恢复现场
        for (int i = 0; i <= cnt && c[u] * i <= target; i ++)
        &#123;
            cur.pop_back();
        &#125;
    &#125;
&#125;;

  转载请注明: Justin博客 LeetCode刷题记31-40

 上一篇
408相关算法刷题【数组篇】 408相关算法刷题【数组篇】
LeetCode 1. 两数之和题目链接 https://leetcode.cn/problems/two-sum/ 思路1 暴力枚举 两重循环枚举下标 i,ji,j,然后判断 nums[i]+nums[j] 是否等于 target。
下一篇 
Leetcode刷题记21-30 Leetcode刷题记21-30
LeetCode 21. 合并两个有序链表题目类型 链表 升序序列的合并问题 题目链接 https://leetcode.cn/problems/merge-two-sorted-lists/ 思路 遍历两个链表,每次选择最小的数添加到
  目录