leetcode解题思路分析(六)37-42题

╰+哭是因爲堅強的太久メ 2023-06-29 07:25 114阅读 0赞
  1. 解数独
    编写一个程序,通过已填充的空格来解决数独问题。

本题主要是采取回溯法解决,选择最少空位的行、列、块,然后进行填入,如果出现问题则回溯

  1. class Solution {
  2. public:
  3. // line, column, block 分别存储每行、每列、每宫中可用的数字
  4. vector<set<int>> line, column, block;
  5. //哈希更新每行/列/宫中可以使用的数字
  6. void update( vector<vector<char>>& board){
  7. set<int> compare = { 1,2,3,4,5,6,7,8,9};
  8. //a 行;b 列;c 宫
  9. for( int i = 0; i < 9; i++)
  10. line.push_back( compare), column.push_back( compare), block.push_back( compare);
  11. for( int i = 0; i < 9; i++)
  12. for( int j = 0; j < 9; j++)
  13. if( board[i][j] != '.'){
  14. int t = board[i][j] - '0';
  15. line[i].erase( t), column[j].erase(t), block[i / 3 + j / 3 * 3].erase(t);
  16. }
  17. return ;
  18. }
  19. //检查该位置处字符是否可以放到该处
  20. bool check( vector<vector<char>>& board, const int& i, const int& j, int num){
  21. if( line[i].find( num) != line[i].end()
  22. && column[j].find( num) != column[j].end()
  23. && block[i/3 + j/3*3].find( num) != block[i/3 + j/3*3].end())
  24. return true;
  25. return false;
  26. }
  27. //标记
  28. int flag = 0;
  29. //dfs + 回溯
  30. void dfs( vector<vector<char>>& board, int count){
  31. if( count == 81){
  32. flag = 1;
  33. return ;
  34. }
  35. int i = count / 9, j = count % 9;
  36. if( board[i][j] == '.'){
  37. //检查 1 ~ 9 中数字哪一个可以放入该位置
  38. for( int k = 1; k < 10; k++)
  39. if( check( board, i, j, k)){
  40. line[i].erase( k), column[j].erase( k), block[ i /3 + j/3*3].erase( k);
  41. board[i][j] = k + '0';
  42. dfs( board, count + 1);
  43. if( !flag){
  44. line[i].insert( k), column[j].insert( k), block[ i /3 + j/3*3].insert( k);
  45. board[i][j] = '.';
  46. }
  47. else
  48. return ;
  49. }
  50. }
  51. else
  52. dfs( board, count + 1);
  53. return ;
  54. }
  55. void solveSudoku(vector<vector<char>>& board) {
  56. update( board);
  57. //show( line, column, block);
  58. dfs(board, 0);
  59. }
  60. };
  1. 外观数列
    「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。

本题采用递归可解:每次的答案均是上次的cnt + val

  1. class Solution {
  2. public:
  3. string countAndSay(int n) {
  4. if (n == 1)
  5. return "1";
  6. string s = countAndSay(n - 1);
  7. string ret;
  8. int size = s.size();
  9. for (int i = 0; i < size;)
  10. {
  11. int cnt = 1;
  12. char val = s[i];
  13. i++;
  14. while (i < size && s[i] == val)
  15. {
  16. i++;
  17. cnt++;
  18. }
  19. char count = cnt + '0';
  20. ret = ret + count + val;
  21. }
  22. return ret;
  23. }
  24. };
  1. 组合总和
    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
    candidates 中的数字可以无限制重复被选取。

本题可采用动态规划的方法解决:对target可以分解为数组中的某些元素,从第一个元素开始遍历,即dp[target] = dp[candidates[0]] + dp[target - candidates[0]],由此可解

  1. class Solution {
  2. public:
  3. vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
  4. unordered_map<int, set<vector<int>>>dict;
  5. for (int i = 1; i <= target; i++)
  6. {
  7. for (int it : candidates)
  8. {
  9. if (i == it) dict[i].insert(vector<int>{ it});
  10. else if (i > it)
  11. {
  12. for (auto ivec : dict[i - it])
  13. {
  14. ivec.push_back(it);
  15. sort(ivec.begin(), ivec.end());
  16. if(dict[i].count(ivec)==0)
  17. dict[i].insert(ivec);
  18. }
  19. }
  20. }
  21. }
  22. vector<vector<int>>ans;
  23. for (auto it : dict[target]) ans.push_back(it);
  24. return ans;
  25. }
  26. };

增加剪枝操作之后的代码如下

  1. class Solution {
  2. private:
  3. vector<int> candidates;
  4. vector<vector<int>> res;
  5. vector<int> path;
  6. public:
  7. void DFS(int start, int target) {
  8. if (target == 0) {
  9. res.push_back(path);
  10. return;
  11. }
  12. for (int i = start;
  13. i < candidates.size() && target - candidates[i] >= 0; i++) {
  14. path.push_back(candidates[i]);
  15. DFS(i, target - candidates[i]);
  16. path.pop_back();
  17. }
  18. }
  19. vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
  20. std::sort(candidates.begin(), candidates.end());
  21. this->candidates = candidates;
  22. DFS(0, target);
  23. return res;
  24. }
  25. };
  1. 组合总和2
    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
    candidates 中的每个数字在每个组合中只能使用一次。

此题本质上和上题是一样的东西,只不过是稍微修改一点条件而已。一样的回溯+剪纸可解

  1. class Solution {
  2. public:
  3. vector<int> input;
  4. int target;
  5. vector<vector<int>> result;
  6. vector<int> vc;
  7. void dfs(int index, int sum) {
  8. // index >= input.size() ,写成 index == input.size() 即可
  9. // 因为每次都 + 1,在 index == input.size() 剪枝就可以了
  10. if (sum >= target || index == input.size()) {
  11. if (sum == target) {
  12. result.push_back(vc);
  13. }
  14. return;
  15. }
  16. for (int i = index; i < input.size(); i++) {
  17. if (input[i] > target) {
  18. continue;
  19. }
  20. // 【我添加的代码在这里】:
  21. // 1、i > index 表明剪枝的分支一定不是当前层的第 1 个分支
  22. // 2、input[i - 1] == input[i] 表明当前选出来的数等于当前层前一个分支选出来的数
  23. // 因为前一个分支的候选集合一定大于后一个分支的候选集合
  24. // 故后面出现的分支中一定包含了前面分支出现的结果,因此剪枝
  25. // “剪枝”的前提是排序,升序或者降序均可
  26. if (i > index && input[i - 1] == input[i]) {
  27. continue;
  28. }
  29. vc.push_back(input[i]);
  30. sum += input[i];
  31. dfs(i + 1, sum);
  32. vc.pop_back();
  33. sum -= input[i];
  34. }
  35. }
  36. vector<vector<int>> combinationSum2(vector<int> &candidates, int target) {
  37. // “剪枝”的前提是排序,升序或者降序均可
  38. sort(candidates.begin(), candidates.end());
  39. this->input = candidates;
  40. this->target = target;
  41. dfs(0, 0);
  42. return result;
  43. }
  44. };
  1. 缺失的第一个正数
    给定一个未排序的整数数组,找出其中没有出现的最小的正整数。

本题有一个隐藏的结论:数组长度为N,则最小正整数一定小于等于N+1,介于此,我们可以用数组当哈希表用来存储状态,然后据此遍历一遍即可

  1. class Solution {
  2. public:
  3. int firstMissingPositive(vector<int>& nums) {
  4. int ret = 1;
  5. int size = nums.size();
  6. int mapNums[size + 2] = { 0};
  7. for (int i = 0; i < nums.size(); i++)
  8. {
  9. int tmp = nums[i];
  10. if (tmp < size + 2 && tmp > 0)
  11. mapNums[tmp] = 1;
  12. }
  13. while (1)
  14. {
  15. if (mapNums[ret] == 1)
  16. ret++;
  17. else
  18. return ret;
  19. }
  20. }
  21. };
  1. 接雨水
    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

遇到这种一般最优解都是双指针首尾滑动,主要难点在于设计怎么计算。这题的核心思想在于:左边比右边高则从右边开始算格子,右边比左边高则从左边开始算格子

  1. class Solution {
  2. public:
  3. int trap(vector<int>& height) {
  4. int begin = 0, end = height.size() - 1, ret = 0;
  5. int leftMax = 0, rightMax = 0;
  6. while (begin < end)
  7. {
  8. if (height[begin] < height[end])
  9. {
  10. if (height[begin] < leftMax)
  11. ret += leftMax - height[begin];
  12. else
  13. leftMax = height[begin];
  14. begin++;
  15. }
  16. else
  17. {
  18. if (height[end] < rightMax)
  19. ret += rightMax - height[end];
  20. else
  21. rightMax = height[end];
  22. end--;
  23. }
  24. }
  25. return ret;
  26. }
  27. };

发表评论

表情:
评论列表 (有 0 条评论,114人围观)

还没有评论,来说两句吧...

相关阅读