• Leetcode刷题笔记--Hot61-70


    目录

    1--课程表(207)

    2--实现Trie(前缀树)(208)

    3--数组中的第K个最大的元素(215)

    4--最大正方形(221)

    5--翻转二叉树(226)

    6--回文链表(234)

    7--二叉树的最近公共祖先(236)

    8--除自身以外数组的乘积(238)

    9--滑动窗口最大值(239)

    10--搜索二维矩阵II(240)


    1--课程表(207)

    主要思路:

            用 in 记录每一门课程剩余的先修课程个数,当剩余先修课程个数为0时,将该课程加入到队列q中。

            每修队列q中的课程,以该课程作为先修课程的所有课程,其剩余先修课程个数减1;

            不断将剩余先修课程数为0的课程加入到队列q中,当队列为空时,若修的课程数等于总课程数,则返回true,否则返回false;

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. bool canFinish(int numCourses, std::vectorint>>& prerequisites) {
    7. std::vectorint>> out; // 存储每一个先修课程对应的课程
    8. std::vector<int> in; // 存储每一个课程对应的剩余先修课程的个数
    9. std::queue<int> q; // 存储可以修的课程
    10. out.resize(numCourses);
    11. in.resize(numCourses);
    12. // 初始化
    13. for(auto pair : prerequisites){
    14. int cur = pair[0]; // 当前课程
    15. int pre = pair[1]; // 当前课程的先修课程
    16. out[pre].push_back(cur); // 初始化out
    17. in[cur]++;
    18. }
    19. // 选取可以直接修的课程加入到队列q中
    20. for(int i = 0; i < numCourses; i++){
    21. if(in[i] == 0) q.push(i);
    22. }
    23. int num = 0; // 已经修过的课程数
    24. while(!q.empty()){
    25. int tmp = q.front(); // 修弹出的课程
    26. q.pop();
    27. num++;
    28. // 以tmp作为先修课程的课程,其剩余的先修课程数减1
    29. for(auto course : out[tmp]){
    30. in[course] --;
    31. if(in[course] == 0) q.push(course); // course没有需要先修的课程了,因此可以加入到队列q中
    32. }
    33. }
    34. if(num == numCourses) return true;
    35. else return false;
    36. }
    37. };
    38. int main(int argc, char* argv[]){
    39. // numCourses = 2, prerequisites = [[1,0],[0,1]]
    40. std::vectorint>> test = {{1, 0}, {0, 1}};
    41. int numCourses = 2;
    42. Solution S1;
    43. bool res = S1.canFinish(numCourses, test);
    44. if(res) std::cout << "true" << std::endl;
    45. else std::cout << "false" << std::endl;
    46. return 0;
    47. }

    2--实现Trie(前缀树)(208)

    主要思路:

            参考之前的笔记:前缀树的实现

    3--数组中的第K个最大的元素(215)

    主要思路:

            基于随机化的快排(即随机选取基准元素)划分数组,其时间复杂度为O(n);

            根据第K个最大的元素在哪一个数组,继续递归随机化快排,直到找到第K个最大的元素。

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. int findKthLargest(std::vector<int>& nums, int k){
    7. return quickSelect(nums, k);
    8. }
    9. int quickSelect(std::vector<int>& nums, int k){
    10. std::vector<int> large;
    11. std::vector<int> equal;
    12. std::vector<int> less;
    13. // 随机选取基准元素
    14. int pivot = nums[rand() % nums.size()]; // 返回[0, nums.size()-1]范围内的一个随机数
    15. for(int num : nums){
    16. if(num > pivot) large.push_back(num);
    17. else if(num == pivot) equal.push_back(num);
    18. else less.push_back(num);
    19. }
    20. // large, equal, less
    21. // 第k大的元素在large中
    22. if(k <= large.size()) return quickSelect(large, k);
    23. // 第k大的元素在less中
    24. else if(k > (nums.size() - less.size())) return quickSelect(less, k-(nums.size() - less.size()));
    25. else return pivot;
    26. }
    27. };
    28. int main(int argc, char *argv[]){
    29. // [3, 2, 1, 5, 6, 4], k = 2
    30. std::vector<int> test = {3, 2, 1, 5, 6, 4};
    31. int k = 2;
    32. Solution S1;
    33. int res = S1.findKthLargest(test, k);
    34. std::cout << res << std::endl;
    35. return 0;
    36. }

    4--最大正方形(221)

    主要思路:

            基于动态规划,dp[i][j]表示以(i, j)为右下角,所构成正方形的最大边长。

            状态转移方程: dp[i][j] = std::min(dp[i-1][j-1], std::min(dp[i-1][j], dp[i][j-1])) + 1;

            具体推导参考: 统计全为 1 的正方形子矩阵

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int maximalSquare(std::vectorchar>>& matrix) {
    6. // dp[i][j]表示以(i, j)作为右下角构成正方形的最大边长
    7. std::vectorint>> dp(matrix.size(), std::vector<int>(matrix[0].size(), 0));
    8. // 初始化
    9. int max = 0;
    10. for(int i = 0; i < matrix.size(); i++){
    11. if(matrix[i][0] == '1'){
    12. dp[i][0] = 1;
    13. max = 1;
    14. }
    15. }
    16. for(int j = 0; j < matrix[0].size(); j++){
    17. if(matrix[0][j] == '1'){
    18. dp[0][j] = 1;
    19. max = 1;
    20. }
    21. }
    22. for(int i = 1; i < matrix.size(); i++){
    23. for(int j = 1; j < matrix[0].size(); j++){
    24. if(matrix[i][j] == '1'){
    25. dp[i][j] = std::min(dp[i-1][j-1], std::min(dp[i-1][j], dp[i][j-1])) + 1;
    26. }
    27. max = std::max(max, dp[i][j]);
    28. }
    29. }
    30. return max * max; // 返回面积
    31. }
    32. };
    33. int main(int argc, char *argv[]){
    34. // matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
    35. std::vectorchar>> test = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'},
    36. {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'}};
    37. Solution S1;
    38. int res = S1.maximalSquare(test);
    39. std::cout << res << std::endl;
    40. return 0;
    41. }

    5--翻转二叉树(226)

    主要思路:

            递归交换左右子树即可。

    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* invertTree(TreeNode* root) {
    15. return dfs(root);
    16. }
    17. TreeNode* dfs(TreeNode* root){
    18. if(root == nullptr) return nullptr;
    19. TreeNode* left = dfs(root->right);
    20. TreeNode* right = dfs(root->left);
    21. root->left = left;
    22. root->right = right;
    23. return root;
    24. }
    25. };
    26. int main(int argc, char *argv[]){
    27. // root = [4, 2, 7, 1, 3, 6, 9]
    28. TreeNode *Node1 = new TreeNode(4);
    29. TreeNode *Node2 = new TreeNode(2);
    30. TreeNode *Node3 = new TreeNode(7);
    31. TreeNode *Node4 = new TreeNode(1);
    32. TreeNode *Node5 = new TreeNode(3);
    33. TreeNode *Node6 = new TreeNode(6);
    34. TreeNode *Node7 = new TreeNode(9);
    35. Node1->left = Node2;
    36. Node1->right = Node3;
    37. Node2->left = Node4;
    38. Node2->right = Node5;
    39. Node3->left= Node6;
    40. Node3->right = Node7;
    41. Solution S1;
    42. TreeNode *res = S1.invertTree(Node1);
    43. // 层次遍历打印
    44. std::queue q;
    45. q.push(res);
    46. while(!q.empty()){
    47. TreeNode* top = q.front();
    48. q.pop();
    49. std::cout << top->val << " ";
    50. if(top->left != nullptr) q.push(top->left);
    51. if(top->right != nullptr) q.push(top->right);
    52. }
    53. std::cout << std::endl;
    54. return 0;
    55. }

    6--回文链表(234)

    主要思路:

            基于快慢指针,将链表划分为两部分,判断两部分是否相同即可。

            其中第一部分为链表的前半部分,在快慢指针遍历的时候需要重构链表,将指针前指。

    1. #include
    2. #include
    3. struct ListNode {
    4. int val;
    5. ListNode *next;
    6. ListNode() : val(0), next(nullptr) {}
    7. ListNode(int x) : val(x), next(nullptr) {}
    8. ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. };
    10. class Solution {
    11. public:
    12. bool isPalindrome(ListNode* head) {
    13. // 1 2 2 1
    14. // 1 2 1 2 1
    15. ListNode* slow = head;
    16. ListNode* fast = head->next;
    17. ListNode* pre = nullptr;
    18. ListNode* next = nullptr;
    19. while(fast != nullptr){
    20. // 前指
    21. next = slow->next;
    22. slow->next = pre;
    23. pre = slow;
    24. slow = next;
    25. fast = fast->next;
    26. if(fast == nullptr){
    27. break;
    28. }
    29. fast = fast->next;
    30. if(fast == nullptr){
    31. slow = slow->next;
    32. }
    33. }
    34. while(slow != nullptr && pre != nullptr){
    35. if(slow->val != pre->val) return false;
    36. slow = slow->next;
    37. pre = pre->next;
    38. }
    39. return true;
    40. }
    41. };
    42. int main(int argc, char *argv[]){
    43. // head = [1, 2, 1, 2, 1]
    44. ListNode *Node1 = new ListNode(1);
    45. ListNode *Node2 = new ListNode(2);
    46. ListNode *Node3 = new ListNode(1);
    47. ListNode *Node4 = new ListNode(2);
    48. ListNode *Node5 = new ListNode(1);
    49. Node1->next = Node2;
    50. Node2->next = Node3;
    51. Node3->next = Node4;
    52. Node4->next = Node5;
    53. Solution S1;
    54. bool res = S1.isPalindrome(Node1);
    55. if(res) std::cout << "true" << std::endl;
    56. else std::cout << "false" << std::endl;
    57. return 0;
    58. }

    7--二叉树的最近公共祖先(236)

    主要思路:

            经典二叉数递归搜索。之前笔记分析过很多次了,主要思想就是递归找到目标节点,返回即可。

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

    8--除自身以外数组的乘积(238)

    主要思路:

            遍历两遍,第一遍求解L[i],L[i]表示第i位左边所有数的乘积;第二遍求解R[i],R[i]表示第i位右边所有数的乘积。

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. std::vector<int> productExceptSelf(std::vector<int>& nums) {
    6. // 先计算L[i], L[i]表示第i位左边所有数的乘积
    7. std::vector<int> res(nums.size(), 0);
    8. res[0] = 1;
    9. for(int i = 1; i < nums.size(); i++){
    10. res[i] = res[i-1] * nums[i-1];
    11. }
    12. // 再计算R[i], R[i]表示第i位右边所有数的乘积
    13. int R = 1;
    14. for(int i = nums.size() - 1; i >= 0; i--){
    15. res[i] = res[i] * R;
    16. R = R*nums[i];
    17. }
    18. return res;
    19. }
    20. };
    21. int main(int argc, char argv[]){
    22. // nums = [1, 2, 3, 4]
    23. std::vector<int> test = {1, 2, 3, 4};
    24. Solution S1;
    25. std::vector<int> res = S1.productExceptSelf(test);
    26. for(int num : res) std::cout << num << " ";
    27. std::cout << std::endl;
    28. return 0;
    29. }

    9--滑动窗口最大值(239)

    主要思路:

            经典单调队列,二刷一次过!!

    1. #include
    2. #include
    3. #include
    4. class Solution {
    5. public:
    6. std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {
    7. std::deque<int> max;
    8. std::vector<int> res;
    9. for(int i = 0; i < k; i++){
    10. if(max.empty()) max.push_back(nums[i]);
    11. else{
    12. while(!max.empty() && nums[i] > max.back()){ // 弹出不可能成为最大值的值
    13. max.pop_back();
    14. }
    15. max.push_back(nums[i]);
    16. }
    17. }
    18. for(int i = k; i < nums.size(); i++){
    19. res.push_back(max.front());
    20. int left_val = nums[i - k];
    21. if(left_val == max.front()){ // 判断当前离开窗口的值是否等于队头的值
    22. max.pop_front();
    23. }
    24. while(!max.empty() && nums[i] > max.back()){ // 弹出比当前准备进队值要小的元素
    25. max.pop_back();
    26. }
    27. max.push_back(nums[i]);
    28. }
    29. res.push_back(max.front());
    30. return res;
    31. }
    32. };
    33. int main(int argc, char argv[]){
    34. // nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3
    35. int k = 3;
    36. std::vector<int> test = {1, 3, -1, -3, 5, 3, 6, 7};
    37. Solution S1;
    38. std::vector<int> res = S1.maxSlidingWindow(test, k);
    39. for(auto num : res){
    40. std::cout << num << " ";
    41. }
    42. std::cout << std::endl;
    43. return 0;
    44. }

    10--搜索二维矩阵II(240)

    主要思路:

            从右上角开始搜索,当matrix[x][y] > target 时,y--;当matrix[x][y] < target时,x++;当matrix[x][y] == target时,返回true;

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. bool searchMatrix(std::vectorint>>& matrix, int target) {
    6. int m = matrix.size(), n = matrix[0].size();
    7. // 从右上角开始搜索
    8. int x = 0, y = n - 1;
    9. while(x < m && y >= 0){
    10. if(matrix[x][y] == target) return true;
    11. else if(matrix[x][y] > target) y--;
    12. else x++; // matrix[x][y] < target
    13. }
    14. return false;
    15. }
    16. };
    17. int main(int argc, char* argv[]){
    18. // matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
    19. std::vectorint>> test = {{1, 4, 7, 11, 15}, {2, 5, 8, 12, 19},
    20. {3, 6, 9, 12, 19}, {10, 13, 14, 17, 24}, {18, 21, 23, 26, 30}};
    21. int target = 5;
    22. Solution S1;
    23. bool res = S1.searchMatrix(test, target);
    24. if(res) std::cout << "true" << std::endl;
    25. else std::cout << "false" << std::endl;
    26. return 0;
    27. }

  • 相关阅读:
    C++ Reference: Standard C++ Library reference: C Library: cwchar: wcsncpy
    多标签页文件管理器 - Win系统
    阴影进阶,实现更加的立体的阴影效果!
    MongoDB的介绍和使用
    RabbitMQ学习笔记——消息转化器
    Network: use `--host` to expose
    Leetcode中等:137. 只出现一次的数字II
    python实现冒泡排序
    (四)线性插值
    【原创】辟谣,实测MyBatisPlus批量新增更新方法确实有效,且可单独使用无需跟随IService
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/133829342