• Leetcode刷题笔记--Hot41-50


    目录

    1--二叉树的层序遍历(102)

    2--二叉树的最大深度(104)

    3--从前序与中序遍历序列构造二叉树(105)

    4--二叉树展开为链表(114)

    5--买卖股票的最佳时机(121)

    6--二叉树中的最大路径和(124)

    7--最长连续序列(128)

    8--只出现一次的数字(136)

    9--单词拆分(139)

    10--环形链表(141)


    1--二叉树的层序遍历(102)

    主要思路:

            经典广度优先搜索,基于队列;

            对于本题需要将同一层的节点放在一个数组中,因此遍历的时候需要用一个变量 nums 来记录当前层的节点数,即 nums 等于队列元素的数目;

    1. #include
    2. #include
    3. #include
    4. struct TreeNode {
    5. int val;
    6. TreeNode *left;
    7. TreeNode *right;
    8. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    9. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    10. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    11. };
    12. class Solution {
    13. public:
    14. std::vectorint>> levelOrder(TreeNode* root) {
    15. std::vectorint>> res;
    16. if(root == nullptr) return res;
    17. std::queue q;
    18. q.push(root);
    19. while(!q.empty()){
    20. int nums = q.size(); // 当前层的节点数
    21. std::vector<int> tmp;
    22. while(nums > 0){ // 遍历处理同一层
    23. TreeNode *cur = q.front();
    24. q.pop();
    25. tmp.push_back(cur->val);
    26. if(cur->left != nullptr) q.push(cur->left);
    27. if(cur->right != nullptr) q.push(cur->right);
    28. nums--;
    29. }
    30. res.push_back(tmp); // 记录当前层的元素
    31. }
    32. return res;
    33. }
    34. };
    35. int main(int argc, char* argv[]){
    36. // root = [1, null, 2, 3]
    37. TreeNode *Node1 = new TreeNode(3);
    38. TreeNode *Node2 = new TreeNode(9);
    39. TreeNode *Node3 = new TreeNode(20);
    40. TreeNode *Node4 = new TreeNode(15);
    41. TreeNode *Node5 = new TreeNode(7);
    42. Node1->left = Node2;
    43. Node1->right = Node3;
    44. Node3->left = Node4;
    45. Node3->right = Node5;
    46. Solution S1;
    47. std::vectorint>> res = S1.levelOrder(Node1);
    48. for(auto item : res) {
    49. for (int v : item) std::cout << v << " ";
    50. std::cout << std::endl;
    51. }
    52. return 0;
    53. }

    2--二叉树的最大深度(104)

    主要思路:

            递归计算左右子树的深度,选取两者最大值 +1 返回;

    1. #include
    2. #include
    3. #include
    4. struct TreeNode {
    5. int val;
    6. TreeNode *left;
    7. TreeNode *right;
    8. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    9. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    10. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    11. };
    12. class Solution {
    13. public:
    14. int maxDepth(TreeNode* root) {
    15. if(root == nullptr) return 0;
    16. int res = dfs(root);
    17. return res;
    18. }
    19. int dfs(TreeNode* root){
    20. if(root == nullptr) return 0;
    21. int left_height = dfs(root->left);
    22. int right_height = dfs(root->right);
    23. int cur_height = std::max(left_height, right_height) + 1;
    24. return cur_height;
    25. }
    26. };
    27. int main(int argc, char* argv[]){
    28. // root = [3,9,20,null,null,15,7]
    29. TreeNode *Node1 = new TreeNode(3);
    30. TreeNode *Node2 = new TreeNode(9);
    31. TreeNode *Node3 = new TreeNode(20);
    32. TreeNode *Node4 = new TreeNode(15);
    33. TreeNode *Node5 = new TreeNode(7);
    34. Node1->left = Node2;
    35. Node1->right = Node3;
    36. Node3->left = Node4;
    37. Node3->right = Node5;
    38. Solution S1;
    39. int res = S1.maxDepth(Node1);
    40. std::cout << res << std::endl;
    41. return 0;
    42. }

    3--从前序与中序遍历序列构造二叉树(105)

    主要思路:

            思路类似于根据中序和后序遍历来构建二叉树;

            对于本题,前序遍历的顺序是:根→左→右,则前序遍历的第一个节点是根节点;

            中序遍历的顺序是:左→根→右,遍历中序数组找到根节点的位置,根据根节点的位置划分左右子树;

            根据左右子树的大小划分前序遍历得到前序遍历的左右子树,递归构建左右子树即可;

    1. #include
    2. #include
    3. #include
    4. struct TreeNode {
    5. int val;
    6. TreeNode *left;
    7. TreeNode *right;
    8. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    9. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    10. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    11. };
    12. class Solution {
    13. public:
    14. TreeNode* buildTree(std::vector<int>& preorder, std::vector<int>& inorder) {
    15. TreeNode* res = dfs(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1);
    16. return res;
    17. }
    18. TreeNode* dfs(std::vector<int>& preorder, int p_st, int p_en, std::vector<int>& inorder, int i_st, int i_en){
    19. if(p_st > p_en || i_st > i_en){
    20. return nullptr;
    21. }
    22. // 头结点
    23. TreeNode *root = new TreeNode(preorder[p_st]);
    24. // 划分中序
    25. int pos = -1; // 中序遍历中根节点的位置索引
    26. for(int i = i_st; i <= i_en; i++){
    27. if(inorder[i] == root->val){
    28. pos = i;
    29. break;
    30. }
    31. }
    32. // 左子树的大小
    33. int left_size = pos - i_st;
    34. // 右子树的大小
    35. int right_size = i_en - pos;
    36. root->left = dfs(preorder, p_st+1, p_st+left_size, inorder, i_st, pos-1);
    37. root->right = dfs(preorder, p_st+left_size+1, p_en, inorder, pos+1, i_en);
    38. return root;
    39. }
    40. };
    41. int main(int argc, char* argv[]){
    42. // preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
    43. std::vector<int> preorder = {3, 9, 20, 15, 7};
    44. std::vector<int> inorder = {9, 3, 15, 20, 7};
    45. Solution S1;
    46. TreeNode* res = S1.buildTree(preorder, inorder);
    47. // 层次遍历打印
    48. std::queue q;
    49. q.push(res);
    50. while(!q.empty()){
    51. TreeNode* tmp = q.front();
    52. q.pop();
    53. std::cout << tmp->val << " ";
    54. if(tmp->left != nullptr) q.push(tmp->left);
    55. if(tmp->right != nullptr) q.push(tmp->right);
    56. }
    57. std::cout << std::endl;
    58. return 0;
    59. }

    4--二叉树展开为链表(114)

    主要思路:

            根据前序遍历的顺序,遍历二叉树,使用一个指针表示当前遍历到的节点的前驱节点,例如图中 2 的前驱节点是 1,4 的前驱节点是 3(根据前序遍历);

            将前驱节点的左孩子为 nullptr,右孩子为当前遍历到的节点;根据前序遍历的顺序,递归更新前驱节点、左孩子和右孩子即可;

    1. #include
    2. #include
    3. #include
    4. struct TreeNode {
    5. int val;
    6. TreeNode *left;
    7. TreeNode *right;
    8. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    9. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    10. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    11. };
    12. class Solution {
    13. public:
    14. void flatten(TreeNode* root) {
    15. dfs(root);
    16. }
    17. void dfs(TreeNode* cur){
    18. if(cur == nullptr) return;
    19. pre->left = nullptr; // 前驱节点的左孩子指向 nullptr
    20. // 根
    21. pre->right = cur; // 前驱节点的右孩子指向当前遍历到的节点
    22. pre = cur; // 更新前驱节点
    23. TreeNode* left = cur->left;
    24. TreeNode* right = cur->right;
    25. dfs(left); // 递归处理左子树
    26. dfs(right); // 递归处理右子树
    27. }
    28. private:
    29. TreeNode* pre = new TreeNode(-1); // 前序遍历的前驱节点
    30. };
    31. int main(int argc, char* argv[]){
    32. // root = [1,2,5,3,4,null,6]
    33. TreeNode* Node1 = new TreeNode(1);
    34. TreeNode* Node2 = new TreeNode(2);
    35. TreeNode* Node3 = new TreeNode(5);
    36. TreeNode* Node4 = new TreeNode(3);
    37. TreeNode* Node5 = new TreeNode(4);
    38. TreeNode* Node6 = new TreeNode(6);
    39. Node1->left = Node2;
    40. Node1->right = Node3;
    41. Node2->left = Node4;
    42. Node2->right = Node5;
    43. Node3->right = Node6;
    44. Solution S1;
    45. S1.flatten(Node1);
    46. TreeNode* tmp = Node1;
    47. while(tmp -> right != nullptr){
    48. std::cout << tmp->val << " ";
    49. tmp = tmp->right;
    50. }
    51. std::cout << std::endl;
    52. return 0;
    53. }

    5--买卖股票的最佳时机(121)

    主要思路:
            计算截止到第 i 天的最低买入价格和最高的利润,不断更新即可;

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int maxProfit(std::vector<int>& prices) {
    6. int min_cost = prices[0];
    7. int max_profit = 0;
    8. for(int i = 0; i < prices.size(); i++){
    9. min_cost = std::min(min_cost, prices[i]);
    10. max_profit = std::max(max_profit, prices[i] - min_cost);
    11. }
    12. return max_profit;
    13. }
    14. };
    15. int main(int argc, char *argv[]) {
    16. // [7, 1, 5, 3, 6, 4]
    17. std::vector<int> test = {7, 1, 5, 3, 6, 4};
    18. Solution S1;
    19. int res = S1.maxProfit(test);
    20. std::cout << res << std::endl;
    21. return 0;
    22. }

    6--二叉树中的最大路径和(124)

    主要思路:

            采用二叉树深度递归搜索,对于每一个结点,其最大路径和可能为以下四种情况:cur + cur->left, cur + cur->right, cur, cur + cur->left + cur->right;

            对于每一个结点,其返回给上层的最大路径,可能为以下三种情况:cur + cur->left, cur + cur->right, cur;

    1. #include
    2. #include
    3. #include
    4. struct TreeNode {
    5. int val;
    6. TreeNode *left;
    7. TreeNode *right;
    8. TreeNode() : val(0), left(nullptr), right(nullptr) {}
    9. TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    10. TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    11. };
    12. class Solution {
    13. public:
    14. int maxPathSum(TreeNode* root) {
    15. dfs(root);
    16. return max;
    17. }
    18. int dfs(TreeNode* root){
    19. if(root == nullptr) return 0;
    20. int left = dfs(root->left);
    21. int right = dfs(root->right);
    22. int cur_sum = std::max(
    23. std::max(
    24. std::max(left + root->val, right + root->val),
    25. left + right + root->val),
    26. root->val);
    27. max = std::max(cur_sum, max);
    28. int ret_sum = std::max(std::max(left + root->val, right + root->val), root->val);
    29. return ret_sum;
    30. }
    31. private:
    32. int max = INT_MIN;
    33. };
    34. int main(int argc, char *argv[]) {
    35. // root = [1, 2, 3]
    36. TreeNode* Node1 = new TreeNode(1);
    37. TreeNode* Node2 = new TreeNode(2);
    38. TreeNode* Node3 = new TreeNode(3);
    39. Node1->left = Node2;
    40. Node1->right = Node3;
    41. Solution S1;
    42. int res = S1.maxPathSum(Node1);
    43. std::cout << res << std::endl;
    44. return 0;
    45. }

    7--最长连续序列(128)

    主要思路:

            利用 unordered_set 实现元素的去重,遍历数组 nums,只处理连续序列最左边的元素(根据其上一个元素是否在 unordered_set 中存在),循环查询其下一个元素是否存在,存在则序列长度 +1;

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. int longestConsecutive(std::vector<int>& nums) {
    7. std::unordered_set<int> set;
    8. for(int & num : nums){ // 利用 unordered_set 去重
    9. set.insert(num);
    10. }
    11. int max_len = 0;
    12. for(int i = 0; i < nums.size(); i++){
    13. int cur_len = 1;
    14. int cur_num = nums[i];
    15. if(set.count(cur_num - 1) == 0){ // 连续子序列左边界元素,不是左边界元素则跳过,确保每一个元素只遍历一次
    16. while(set.count(cur_num + 1) != 0){ // 利用 unordered_set 实现 O(1) 查询
    17. cur_len++; // 长度++
    18. cur_num = cur_num + 1; // 右移
    19. }
    20. max_len = std::max(max_len, cur_len); // 更新最大长度
    21. }
    22. }
    23. return max_len;
    24. }
    25. };
    26. int main(int argc, char *argv[]) {
    27. // nums = [100, 4, 200, 1, 3, 2]
    28. std::vector<int> test = {100, 4, 200, 1, 3, 2};
    29. Solution S1;
    30. int res = S1.longestConsecutive(test);
    31. std::cout << res << std::endl;
    32. return 0;
    33. }

    8--只出现一次的数字(136)

    主要思路:

               按位异或,出现两次的数字会两两对消,最后只剩下出现过一次的数字;     

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int singleNumber(std::vector<int>& nums) {
    6. int res = 0;
    7. for(int num : nums){
    8. res = res ^ num; // 按位异或
    9. }
    10. return res;
    11. }
    12. };
    13. int main(int argc, char *argv[]) {
    14. // nums = [4, 1, 2, 1, 2]
    15. std::vector<int> test = {4, 1, 2, 1, 2};
    16. Solution S1;
    17. int res = S1.singleNumber(test);
    18. std::cout << res << std::endl;
    19. return 0;
    20. }

    9--单词拆分(139)

    主要思路:

            基于动态规划,dp[i] 表示字符串 s 前 i 个字符匹配(即可以由字典的字符串构建而成);

            初始化 dp[0] = true; 

            状态转移方程为:dp[i] == dp[j] && s.substr(j, i-j),0 <= j <= i,只要有一个情况匹配即可;

    1. #include
    2. #include
    3. #include
    4. #include
    5. class Solution {
    6. public:
    7. bool wordBreak(std::string s, std::vector& wordDict) {
    8. std::unordered_set hash;
    9. for(std::string str : wordDict){
    10. hash.insert(str);
    11. }
    12. std::vector<bool> dp(s.length() + 1, 0); // dp[i] 表示前 i 个字符构成的字符串匹配
    13. // 初始化
    14. dp[0] = true;
    15. // 遍历
    16. for(int i = 1; i <= s.length(); i++){
    17. for(int j = 0; j <= i; j++){
    18. if (dp[j] && hash.find(s.substr(j, i - j)) != hash.end()) { // 前 j 个字符匹配,且后 i -j 个字符也匹配
    19. dp[i] = true;
    20. break; // 找到一种情况就 break
    21. }
    22. }
    23. }
    24. return dp[s.length()];
    25. }
    26. };
    27. int main(int argc, char *argv[]) {
    28. // s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
    29. std::string str = "catsandog";
    30. std::vector wordDict = {"cats", "dog", "sand", "and", "cat"};
    31. Solution S1;
    32. bool res = S1.wordBreak(str, wordDict);
    33. if(res) std::cout << "true" << std::endl;
    34. else std::cout << "false" << std::endl;
    35. return 0;
    36. }

    10--环形链表(141)

    主要思路:

            经典快慢指针,判断两个指针是否相遇即可;

    1. #include
    2. struct ListNode {
    3. int val;
    4. ListNode *next;
    5. ListNode(int x) : val(x), next(NULL) {}
    6. };
    7. class Solution {
    8. public:
    9. bool hasCycle(ListNode *head) {
    10. if(head == nullptr) return false;
    11. ListNode* slow = head;
    12. ListNode* fast = head;
    13. while(fast != nullptr && fast->next != nullptr){
    14. fast = fast->next->next;
    15. slow = slow->next;
    16. if(fast == slow) return true;
    17. }
    18. return false;
    19. }
    20. };
    21. int main(int argc, char *argv[]) {
    22. // head = [3,2,0,-4], pos = 1
    23. ListNode *Node1 = new ListNode(3);
    24. ListNode *Node2 = new ListNode(2);
    25. ListNode *Node3 = new ListNode(0);
    26. ListNode *Node4 = new ListNode(-4);
    27. Node1->next = Node2;
    28. Node2->next = Node3;
    29. Node3->next = Node4;
    30. Node4->next = Node2;
    31. Solution S1;
    32. int res = S1.hasCycle(Node1);
    33. if(res) std::cout << "true" << std::endl;
    34. else std::cout << "false" << std::endl;
    35. return 0;
    36. }

  • 相关阅读:
    【SpringBoot项目】SpringBoot+MyBatis+MySQL电脑商城
    关于rdflib解析三元组介绍
    成都理工大学_Python程序设计_第2章
    【计算机网络】万字总结
    中国智能汽车“芯”的崛起
    Linux入门之使用 ps 查看系统进程
    一分钟学一个 Linux 命令 - cat 和 tail
    内核驱动mmap Handler利用技术(二)
    RT-Thread上部署TinyMaix推理框架,使MCU赋予AI能力
    数组array 和 &array的区别
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/132724627