• Leetcode刷题笔记--Hot81--90


    目录

    1--打家劫舍III(337)

    2--比特位计数(338)

    3--前K个高频元素(347)

    4--字符串解码(394)

    5--除法求值(399)

    6--根据身高重建队列(406)

    7--分割等和子集(416)

    8--路径总和III(437)

    9--找到字符串中所有字母异位词(438)

    10--找到所有数组中消失的数字(448)


    1--打家劫舍III(337)

    主要思路:

            基于从下到上的 dp 回溯法,每一个节点只有两种状态,dp[0]表示被打劫,dp[1]表示不被打劫;

            当前节点被打劫时,其孩子只能都不被打劫;dp[0] = left[1] + right[1] + cur->val;

            当前节点不被打劫时,其孩子可以都被打劫,也可以都不被打劫,或者一个被打劫另一个不被打劫。

            dp[1] = max(left[0] + right[0], left[1] + right[1], left[0] + right[1], left[1] + right[0]);

    1. #include
    2. #include
    3. struct TreeNode {
    4. int val;
    5. TreeNode *left;
    6. TreeNode *right;
    7. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    8. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    9. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    10. };
    11. class Solution {
    12. public:
    13. int rob(TreeNode* root) {
    14. // 对于每一间房屋,有被打劫和不被打劫两种状态
    15. std::vector<int> res = dfs(root);
    16. return std::max(res[0], res[1]);
    17. }
    18. std::vector<int> dfs(TreeNode* cur){
    19. if(cur == nullptr) return {0, 0};
    20. std::vector<int> left = dfs(cur->left);
    21. std::vector<int> right = dfs(cur->right);
    22. std::vector<int> dp(2, 0); // dp[0]表示被打劫 dp[1]表示不被打劫
    23. dp[0] = left[1] + right[1] + cur->val; // 当前房屋被打劫,其孩子只能不被打劫
    24. // 当前房屋不被打劫,其孩子可以同时被打劫,也可以同时不被打劫
    25. // 当前房屋不被打劫,其孩子可以一个被打劫,另一个不被打劫
    26. dp[1] = std::max(std::max(std::max(left[0] + right[0], left[1] + right[1]), left[0] + right[1]), left[1] + right[0]);
    27. return dp;
    28. }
    29. };
    30. int main(int argc, char* argv[]){
    31. // root = [3, 2, 3, null, 3, null, 1]
    32. TreeNode *Node1 = new TreeNode(3);
    33. TreeNode *Node2 = new TreeNode(2);
    34. TreeNode *Node3 = new TreeNode(3);
    35. TreeNode *Node4 = new TreeNode(3);
    36. TreeNode *Node5 = new TreeNode(1);
    37. Node1->left = Node2;
    38. Node1->right = Node3;
    39. Node2->right = Node4;
    40. Node3->right = Node5;
    41. Solution S1;
    42. int res = S1.rob(Node1);
    43. std::cout << res << std::endl;
    44. return 0;
    45. }

    2--比特位计数(338)

    主要思路: 

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. std::vector<int> countBits(int n) {
    6. std::vector<int> dp(n + 1, 0);
    7. int high_valid = 0;
    8. for(int i = 1; i <= n; i++){
    9. if((i & (i - 1)) == 0){ // i是2的指数幂,更新最高有效位
    10. high_valid = i;
    11. }
    12. dp[i] = dp[i - high_valid] + 1;
    13. }
    14. return dp;
    15. }
    16. };
    17. int main(int argc, char* argv[]){
    18. // n = 5
    19. int n = 5;
    20. Solution S1;
    21. std::vector<int> res = S1.countBits(n);
    22. for(auto num : res)
    23. std::cout << num << " ";
    24. std::cout << std::endl;
    25. return 0;
    26. }

    3--前K个高频元素(347)

    主要思路:

            维护一个小根堆,存储k个高频元素;

    1. #include
    2. #include
    3. #include
    4. #include
    5. class Solution{
    6. public:
    7. std::vector<int> topKFrequent(std::vector<int>& nums, int k){
    8. std::unordered_map<int, int> hash_map;
    9. for(int i = 0; i < nums.size(); i++){
    10. hash_map[nums[i]] += 1;
    11. }
    12. // 小顶堆
    13. std::priority_queueint, int>, std::vectorint, int>>, mycompare> pri_q;
    14. for(auto i = hash_map.begin(); i != hash_map.end(); i++){
    15. if(pri_q.size() >= k){
    16. if(i->second > pri_q.top().second){
    17. pri_q.pop();
    18. pri_q.emplace(i->first, i->second);
    19. }
    20. }
    21. else{
    22. pri_q.emplace(i->first, i->second);
    23. }
    24. }
    25. std::vector<int> res;
    26. while(!pri_q.empty()){
    27. res.push_back(pri_q.top().first);
    28. pri_q.pop();
    29. }
    30. return res;
    31. }
    32. class mycompare{
    33. public:
    34. bool operator()(std::pair<int, int>& item1, std::pair<int, int>& item2){
    35. return item1.second > item2.second;
    36. }
    37. };
    38. };
    39. int main(int argc, char argv[]){
    40. // nums = [1, 1, 1, 2, 2, 3], k = 2
    41. std::vector<int> test = {1, 1, 1, 2, 2, 3};
    42. int k = 2;
    43. Solution S1;
    44. std::vector<int> res = S1.topKFrequent(test, k);
    45. for(int num : res) std::cout << num << " ";
    46. std::cout << std::endl;
    47. return 0;
    48. }

    4--字符串解码(394)

    主要思路:

            从后开始遍历,用一个栈存储访问的字符,遇到左括号就处理字符串和重复次数;

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. std::string decodeString(std::string s) {
    7. std::stack stk;
    8. int i = s.length() - 1; // 从后开始遍历
    9. while(i >= 0){
    10. if(s[i] == ']' || ('a' <= s[i] && s[i] <= 'z')){
    11. stk.push(s.substr(i, 1));
    12. i--; // 移到下一个字符
    13. }
    14. else{ // 遇到 '['
    15. // 取出栈中的所有字符串(除']')
    16. std::string tmp;
    17. while(!stk.empty() && stk.top() != "]"){
    18. tmp += stk.top();
    19. stk.pop();
    20. }
    21. if(!stk.empty()) stk.pop(); // 弹出 "]"
    22. i--; // 移到数字位
    23. // 处理数字
    24. int val = 0, base = 1;
    25. while(i >= 0 && s[i] >= '0' && s[i] <= '9'){
    26. val += (s[i] - '0') * base;
    27. base = base * 10;
    28. i--;
    29. }
    30. // 存储字符串到栈中
    31. while(val > 0){
    32. stk.push(tmp);
    33. val--;
    34. }
    35. }
    36. }
    37. std::string res;
    38. // 将栈中的字符串取出
    39. while(!stk.empty()){
    40. res += stk.top();
    41. stk.pop();
    42. }
    43. return res;
    44. }
    45. };
    46. int main(int argc, char* argv[]){
    47. // s = "3[a]2[bc]"
    48. std::string test = "3[a]2[bc]";
    49. Solution S1;
    50. std::string res = S1.decodeString(test);
    51. std::cout << res << std::endl;
    52. return 0;
    53. }

    5--除法求值(399)

    主要思路:

            基于带权重的并查集。I think this is a hard question....

    1. #include
    2. #include
    3. #include
    4. #include
    5. class Solution {
    6. public:
    7. int findf(std::vector<int>& f, std::vector<double>& w, int x) { // 引用传递,方便递归更新
    8. if (f[x] != x) { // 递归查找父亲节点
    9. int father = findf(f, w, f[x]);
    10. w[x] = w[x] * w[f[x]]; // 更新权重
    11. f[x] = father; // 更新父亲节点
    12. }
    13. return f[x];
    14. }
    15. void merge(std::vector<int>& f, std::vector<double>& w, int x, int y, double val) {
    16. int fx = findf(f, w, x); // 查找父亲节点
    17. int fy = findf(f, w, y); // 查找父亲节点
    18. f[fx] = fy; // 对于x节点的父亲节点,其父亲节点设置为y的父亲节点
    19. w[fx] = val * w[y] / w[x]; // 同时更新权重, x / y = val, 而 y/f[y] = w[y], x / f[x] = w[x] 则 f[x] / f[y] = val * w[y] / w[x]
    20. } // x / f[y] = val*w[y], x = f[x]*w[x] -> f[x]*w[x] / f[y] = val * w[y] -> f[x] / f[y] = val * w[y] / w[x]
    21. std::vector<double> calcEquation(std::vector>& equations, std::vector<double>& values, std::vector>& queries) {
    22. // 为每个字符节点记录其对应的id
    23. int nvars = 0;
    24. std::unordered_mapint> variables;
    25. int n = equations.size();
    26. for (int i = 0; i < n; i++) {
    27. if (variables.find(equations[i][0]) == variables.end()) {
    28. variables[equations[i][0]] = nvars++;
    29. }
    30. if (variables.find(equations[i][1]) == variables.end()) {
    31. variables[equations[i][1]] = nvars++;
    32. }
    33. }
    34. std::vector<int> f(nvars);
    35. std::vector<double> w(nvars, 1.0);
    36. for (int i = 0; i < nvars; i++){ // 初始化字符节点的父亲节点
    37. f[i] = i;
    38. }
    39. for (int i = 0; i < n; i++) {
    40. int va = variables[equations[i][0]], vb = variables[equations[i][1]];
    41. merge(f, w, va, vb, values[i]); // 合并
    42. }
    43. std::vector<double> ret;
    44. for (const auto& q: queries) { // 遍历queries
    45. double result = -1.0;
    46. if (variables.find(q[0]) != variables.end() && variables.find(q[1]) != variables.end()) { // 同时存在
    47. int ia = variables[q[0]], ib = variables[q[1]]; // 取出对应的id
    48. int fa = findf(f, w, ia), fb = findf(f, w, ib); // 计算对应的父亲
    49. if (fa == fb) { // 具有相同的父亲,表明联通
    50. result = w[ia] / w[ib];
    51. }
    52. }
    53. ret.push_back(result);
    54. }
    55. return ret;
    56. }
    57. };
    58. int main(int argc, char* argv[]){
    59. // equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
    60. std::vector> equations = {{"a", "b"}, {"b", "c"}};
    61. std::vector<double> values = {2.0, 3.0};
    62. std::vector> queries = {{"a", "c"}, {"b", "a"}, {"a", "e"}, {"a", "a"}, {"x", "x"}};
    63. Solution S1;
    64. std::vector<double> res = S1.calcEquation(equations, values, queries);
    65. for(double num : res) std::cout << num << " ";
    66. std::cout << std::endl;
    67. return 0;
    68. }

    6--根据身高重建队列(406)

    主要思路:

            对数组按身高从大到小排序,身高相同按k从小到大排序;

            遍历数组,根据k来插入到结果数组中。

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. std::vectorint>> reconstructQueue(std::vectorint>>& people) {
    7. std::sort(people.begin(), people.end(), [](std::vector<int> a, std::vector<int> b){
    8. if(a[0] == b[0]) return a[1] < b[1];
    9. return a[0] > b[0];
    10. });
    11. std::vectorint>> res;
    12. for(int i = 0; i < people.size(); i++){
    13. int pos = people[i][1];
    14. res.insert(res.begin() + pos, people[i]);
    15. }
    16. return res;
    17. }
    18. };
    19. int main(int argc, char* argv[]){
    20. // people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
    21. std::vectorint>> test = {{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}};
    22. Solution S1;
    23. std::vectorint>> res = S1.reconstructQueue(test);
    24. for(auto vec : res){
    25. for(int num : vec) std::cout << num << " ";
    26. std::cout << std::endl;
    27. }
    28. return 0;
    29. }

    7--分割等和子集(416)

    主要思路:

            转化成0 - 1背包问题,一半的子集和vec作为背包容量,另一半的子集和作为物品,只需判断最后 dp[vec] 是否等于 vec 即可;

            这道题使用 0-1 背包问题的 1 维解法时,需要先正序遍历物品,再倒序遍历背包,防止物品被多次选择(回看背包问题的笔记)。

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. bool canPartition(std::vector<int>& nums) {
    7. int sum = 0;
    8. for(int i = 0; i < nums.size(); i++){
    9. sum += nums[i];
    10. }
    11. if(sum % 2 != 0) return false;
    12. // 转化为背包问题
    13. int vec = int(sum / 2); // 背包容量
    14. std::vector<int> dp(vec+1, 0); // dp[i] 背包容量为i时装的物品价值
    15. dp[0] = 0;
    16. for(int j = 0; j < nums.size(); j++){ // 遍历物品
    17. for(int i = vec; i >= 0; i--){ // 遍历背包
    18. if(i >= nums[j]){
    19. dp[i] = std::max(dp[i - nums[j]] + nums[j], dp[i]);
    20. }
    21. }
    22. }
    23. if(dp[vec] == vec) return true;
    24. else return false;
    25. }
    26. };
    27. int main(int argc, char* argv[]){
    28. // nums = [1, 5, 11, 5]
    29. std::vector<int> test = {1, 5, 11, 5};
    30. Solution S1;
    31. bool res = S1.canPartition(test);
    32. if(res) std::cout << "true" << std::endl;
    33. else std::cout << "false" << std::endl;
    34. return 0;
    35. }

    8--路径总和III(437)

    主要思路:

            经典二叉树深搜,本题需要向下传递每一个节点的加和值(因为每一个节点都可以作为起始节点)。

    1. #include
    2. #include
    3. struct TreeNode {
    4. int val;
    5. TreeNode *left;
    6. TreeNode *right;
    7. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    8. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    9. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    10. };
    11. class Solution {
    12. public:
    13. int pathSum(TreeNode* root, int targetSum) {
    14. int res = 0, num = 0; // 结果
    15. dfs(root, targetSum, {}, res);
    16. return res;
    17. }
    18. void dfs(TreeNode* root, int targetSum, std::vector<long long> sum, int& res){
    19. if(root == nullptr) return;
    20. for(long long& num : sum) {
    21. if(num + root->val == targetSum){ // 上面的节点作为起始节点,以当前节点作为终点,匹配成功
    22. res++; // 结果+1
    23. }
    24. num += root->val; // 更新路径和:加上当前节点
    25. }
    26. if(root->val == targetSum) res++; // 判断单个当前节点是否匹配
    27. sum.push_back(root->val); // 更新路径和(当前节点可以作为新的起始节点)
    28. dfs(root->left, targetSum, sum, res); // 深搜
    29. dfs(root->right, targetSum, sum, res);
    30. return;
    31. }
    32. };
    33. int main(int argc, char* argv[]){
    34. // root = [10, 5, -3, 3, 2, null, 11, 3, -2, null, 1], targetSum = 8
    35. TreeNode *Node1 = new TreeNode(10);
    36. TreeNode *Node2 = new TreeNode(5);
    37. TreeNode *Node3 = new TreeNode(-3);
    38. TreeNode *Node4 = new TreeNode(3);
    39. TreeNode *Node5 = new TreeNode(2);
    40. TreeNode *Node6 = new TreeNode(11);
    41. TreeNode *Node7 = new TreeNode(3);
    42. TreeNode *Node8 = new TreeNode(-2);
    43. TreeNode *Node9 = new TreeNode(1);
    44. Node1->left = Node2;
    45. Node1->right = Node3;
    46. Node2->left = Node4;
    47. Node2->right = Node5;
    48. Node3->right = Node6;
    49. Node4->left = Node7;
    50. Node4->right = Node8;
    51. Node5->right = Node9;
    52. int targetSum = 8;
    53. Solution S1;
    54. int res = S1.pathSum(Node1, targetSum);
    55. std::cout << res << std::endl;
    56. return 0;
    57. }

    9--找到字符串中所有字母异位词(438)

    主要思路:

            基于滑动窗口,判断s串滑动窗口的元素是否与p串匹配;

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. bool check(std::vector<int> cnts, std::vector<int> cntp){
    6. for(int i = 0; i < 26; i++){
    7. if(cnts[i] != cntp[i]) return false;
    8. }
    9. return true;
    10. }
    11. std::vector<int> findAnagrams(std::string s, std::string p) {
    12. // 基于滑动窗口
    13. // 先统计p串各字符出现的次数
    14. std::vector<int> cntp(26);
    15. for(int i = 0; i < p.length(); i++){
    16. cntp[p[i] - 'a']++;
    17. }
    18. // 初始化s串第一个滑动窗口各字符出现的次数
    19. std::vector<int> cnts(26);
    20. for(int i = 0; i < std::min(p.length(), s.length()); i++){ // s串可能会小于p串
    21. cnts[s[i] - 'a']++;
    22. }
    23. std::vector<int> res;
    24. if(check(cnts, cntp)) res.push_back(0); // 判断第一个窗口是否合法
    25. for(int r = p.length(); r < s.length(); r++){ // 枚举右边界
    26. int l = r - p.length() + 1; // 当前窗口左边界
    27. cnts[s[l-1] - 'a']--; // 移除上一个窗口左边界元素
    28. cnts[s[r] - 'a']++; // 移入右边界元素
    29. if(check(cnts, cntp)) res.push_back(l); // 匹配,记录当前窗口的左边界索引
    30. }
    31. return res;
    32. }
    33. };
    34. int main(int argc, char* argv[]){
    35. // s = "cbaebabacd", p = "abc"
    36. std::string s = "cbaebabacd";
    37. std::string p = "abc";
    38. Solution S1;
    39. std::vector<int> res = S1.findAnagrams(s, p);
    40. for(int num : res) std::cout << num << " ";
    41. std::cout << std::endl;
    42. return 0;
    43. }

    10--找到所有数组中消失的数字(448)

    主要思路:

            原地修改数组,遍历数组,根据数组的值修改对应原数组索引的值,具体对应关系是 1对应坐标0,2对应坐标1;修改数组时,将对应坐标的值加上 nums.size(),最后只需判断哪个坐标的值 <= nums.size(),就可以知道哪些值没有出现过。

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. std::vector<int> findDisappearedNumbers(std::vector<int>& nums) {
    6. std::vector<int> res;
    7. for(int i = 0; i < nums.size(); i++){
    8. int pos = (nums[i] - 1) % nums.size();
    9. nums[pos] += nums.size();
    10. }
    11. for(int i = 0; i < nums.size(); i++){
    12. if (nums[i] <= nums.size()) res.push_back(i+1);
    13. }
    14. return res;
    15. }
    16. };
    17. int main(int argc, char* argv[]){
    18. // nums = [4, 3, 2, 7, 8, 2, 3, 1]
    19. std::vector<int> test = {4, 3, 2, 7, 8, 2, 3, 1};
    20. Solution S1;
    21. std::vector<int> res = S1.findDisappearedNumbers(test);
    22. for(int num : res) std::cout << num << " ";
    23. std::cout << std::endl;
    24. return 0;
    25. }

  • 相关阅读:
    如何在 ELEMENTOR 中创建、编辑和设置列样式
    基于权限控制的Kubernetes容器远程连接方法
    [附源码]计算机毕业设计基于springboot在线影院系统
    2022第五空间WEB&MISC
    英语——分享篇——每日200词——2601-2800
    opencv-python 印刷质量缺陷的视觉检测
    17 OpenCv Canny算子
    java基于springboot+vue+elementui的会员制在线读书图书购物管理平台
    ELK日志分析系统
    翻译软件排行榜-免费翻译软件排行榜-翻译软件推荐排行榜
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/134075395