• Leetcode刷题笔记--Hot51-60


    目录

    1--环形链表II(142)

    2--LRU缓存(146)

    3--排序列表(148)

    4--乘积最大子数组(152)

    5--最小栈(155)

    6--相交链表(160)

    7--多数元素(169)

    8--打家劫舍(198)

    9--岛屿数量(200)

    10--反转链表(206)


    1--环形链表II(142)

    主要思路:

            快慢指针,快指针每次走两步,慢指针每次走一步;

            第一次相遇时,假设慢指针共走了 f 步,则快指针走了 2f 步;

            假设起点到环入口结点的长度为 a(不包括入口结点),环的结点数为 b;快指针比慢指针多走的步数肯定全在环中,则有 2f - f = f = nb;则慢指针共走了 nb 步;

            由于慢指针共走了 nb 步,而起点到环入口结点的步数为 a,则实际慢指针在环内走了 nb - a 步;

            此时让慢指针从起点重新出发走 a 步,每次走 1 步;快指针从相遇的地方出发,每次也走 1 步,快慢指针必然在环入口结点相遇;因此快指针相当于也走了 a 步,恰好与 nb - a 步互补,构成完整圈数的 nb 环;

    1. #include
    2. #include
    3. struct ListNode {
    4. int val;
    5. ListNode *next;
    6. ListNode(int x) : val(x), next(NULL) {}
    7. };
    8. class Solution {
    9. public:
    10. ListNode *detectCycle(ListNode *head) {
    11. if(head == nullptr) return head;
    12. ListNode *slow = head;
    13. ListNode *fast = head;
    14. while(fast != NULL && fast->next != NULL){
    15. fast = fast->next->next;
    16. slow = slow->next;
    17. if(fast == slow) break; // 第一次相遇
    18. }
    19. if(fast == NULL || fast->next == NULL) return NULL; // 没有环
    20. // 从头开始走
    21. slow = head;
    22. while(slow != fast){
    23. slow = slow->next;
    24. fast = fast->next;
    25. }
    26. // 第二次相遇就是环的入口
    27. return slow;
    28. }
    29. };
    30. int main(int argc, char *argv[]) {
    31. // head = [3, 2, 0, -4], pos = 1
    32. ListNode *Node1 = new ListNode(3);
    33. ListNode *Node2 = new ListNode(2);
    34. ListNode *Node3 = new ListNode(0);
    35. ListNode *Node4 = new ListNode(-4);
    36. Node1->next = Node2;
    37. Node2->next = Node3;
    38. Node3->next = Node4;
    39. Node4->next = Node2;
    40. Solution S1;
    41. ListNode* res = S1.detectCycle(Node1);
    42. if(res != nullptr) std::cout << res->val << std::endl;
    43. else std::cout << "nullptr" << std::endl;
    44. return 0;
    45. }

    2--LRU缓存(146)

    主要思路:

            基于双向链表和哈希表;

            访问元素时,若元素不存在则返回;若元素存在,则将元素记录,并将元素移动到双向链表头部;(确保访问热度最高的元素放在双向链表头部,访问热度最低的元素放在双向链表尾部);

            插入元素时,若元素不存在:当容量已满时,先移除双向链表尾部的元素,再将新元素插入到双向链表头部;若元素存在,则取出元素并更新元素的值,将更新后的元素插入到双向链表头部;

    1. #include
    2. #include
    3. class LRUCache {
    4. public:
    5. struct ListNode{
    6. ListNode(int key, int val){
    7. this->key = key;
    8. this->val = val;
    9. }
    10. ListNode* pre = nullptr;
    11. ListNode* next = nullptr;
    12. int val = 0;
    13. int key = 0;
    14. };
    15. LRUCache(int capacity) {
    16. this->cap = capacity; // 容量
    17. head = new ListNode(-1, -1);
    18. tail = new ListNode(-1, -1);
    19. head->next = tail;
    20. tail->pre = head;
    21. }
    22. int get(int key) {
    23. if(hash.count(key) == 0) return -1; // 元素不存在
    24. ListNode* ptr = hash[key]; // 取出元素
    25. remove(ptr); // 从双向链表中删除元素
    26. insert(ptr); // 将元素插入到双向链表头部
    27. return ptr->val; // 返回元素的值
    28. }
    29. void put(int key, int value) {
    30. if(hash.count(key) == 0){ // 元素不存在
    31. if(hash.size() == cap){ // 容量已满
    32. ListNode* ptr = tail->pre;
    33. remove(ptr); // 去除尾部节点
    34. hash.erase(ptr->key);
    35. delete(ptr);
    36. }
    37. ListNode* new_node = new ListNode(key, value); // 新建节点
    38. insert(new_node); // 插入新节点到头部
    39. hash[new_node->key] = new_node;
    40. return;
    41. }
    42. else{ // 元素存在
    43. ListNode* ptr = hash[key]; // 取出元素
    44. ptr->val = value; // 更新元素
    45. remove(ptr); // 先删除元素
    46. insert(ptr); // 再将元素插入到头部
    47. return;
    48. }
    49. }
    50. void remove(ListNode* ptr){
    51. // 取出前驱和后驱元素
    52. ListNode* a = ptr->pre;
    53. ListNode* b = ptr->next;
    54. // 更新前驱和后驱元素的指向
    55. a->next = b;
    56. b->pre = a;
    57. ptr->pre = ptr->next = nullptr;
    58. }
    59. void insert(ListNode* ptr){
    60. ListNode* tmp = head->next; // 头指针的下一个元素
    61. // 将元素插入到双向链表头部
    62. ptr->pre = head;
    63. head->next = ptr;
    64. ptr->next = tmp;
    65. tmp->pre = ptr;
    66. }
    67. private:
    68. int cap = 0; // 容量
    69. std::unordered_map<int, ListNode*> hash; // 哈希表
    70. ListNode* head; // 双向链表哨兵头节点
    71. ListNode* tail; // 双向链表哨兵尾节点
    72. };
    73. int main(int argc, char argv[]){
    74. return 0;
    75. }

    3--排序列表(148)

    主要思路:

            基于归并排序,先二分链表,再对链表进行归并排序;

    1. #include
    2. struct ListNode {
    3. int val;
    4. ListNode *next;
    5. ListNode() : val(0), next(nullptr) {}
    6. ListNode(int x) : val(x), next(nullptr) {}
    7. ListNode(int x, ListNode *next) : val(x), next(next) {}
    8. };
    9. class Solution {
    10. public:
    11. ListNode* sortList(ListNode* head) {
    12. return SplitList(head);
    13. }
    14. ListNode* SplitList(ListNode* head){
    15. if(head == nullptr || head->next == nullptr) return head; // 空或者只有一个结点
    16. ListNode* slow = head, *fast = head, *pre = head;
    17. while(fast != nullptr){
    18. pre = slow; // 慢指针的前继指针
    19. fast = fast->next;
    20. slow = slow->next;
    21. if(fast == nullptr) break;
    22. fast = fast->next;
    23. }
    24. pre->next = nullptr; // 断开
    25. return(merge(SplitList(head), SplitList(slow))); // 递归二分至底层,然后归并
    26. }
    27. ListNode* merge(ListNode* head1, ListNode* head2){
    28. ListNode* dummyNode = new ListNode(0);
    29. ListNode *tmp = dummyNode, *tmp1 = head1, *tmp2 = head2;
    30. while(tmp1 != nullptr && tmp2 != nullptr){
    31. if(tmp1->val <= tmp2->val){
    32. tmp->next = tmp1;
    33. tmp1 = tmp1->next;
    34. }
    35. else{
    36. tmp->next = tmp2;
    37. tmp2 = tmp2->next;
    38. }
    39. tmp = tmp->next;
    40. }
    41. while(tmp1 != nullptr){
    42. tmp->next = tmp1;
    43. tmp1 = tmp1->next;
    44. tmp = tmp->next;
    45. }
    46. while(tmp2 != nullptr){
    47. tmp->next = tmp2;
    48. tmp2 = tmp2->next;
    49. tmp = tmp->next;
    50. }
    51. return dummyNode->next;
    52. }
    53. };
    54. int main(int argc, char *argv[]) {
    55. // head = [4, 2, 1, 3]
    56. ListNode* Node1 = new ListNode(4);
    57. ListNode* Node2 = new ListNode(2);
    58. ListNode* Node3 = new ListNode(1);
    59. ListNode* Node4 = new ListNode(3);
    60. Node1->next = Node2;
    61. Node2->next = Node3;
    62. Node3->next = Node4;
    63. Solution S1;
    64. ListNode* res = S1.sortList(Node1);
    65. while(res != nullptr){
    66. std::cout << res->val << " ";
    67. res = res->next;
    68. }
    69. return 0;
    70. }

    4--乘积最大子数组(152)

    主要思路:

            基于动态规划,dp_max[i] 和 dp_min[i] 分别表示以坐标 i 结尾的连续子数组和的最大值和最小值;

            状态转移方程:dp_max[i]  = std::max(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i], nums[i]);dp_min[i]  = std::min(dp_max[i-1] * nums[i], dp_min[i-1] * nums[i], nums[i]);

            之所以使用 dp_min[i],是由于会出现负数的情况,当负负得正时会出现更大的连续和,因此要考虑 dp_min[i];

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

    5--最小栈(155)

    主要思路:

            使用一个辅助栈,记录截止到当前元素的最小值。

    1. #include
    2. #include
    3. class MinStack{
    4. public:
    5. MinStack(){}
    6. void push(int val){
    7. stk1.push(val);
    8. if(stk2.empty() || val <= stk2.top()){
    9. stk2.push(val);
    10. }
    11. }
    12. void pop(){
    13. int top = stk1.top();
    14. stk1.pop();
    15. if(top == stk2.top()) stk2.pop();
    16. }
    17. int top(){
    18. return stk1.top();
    19. }
    20. int getMin() {
    21. return stk2.top();
    22. }
    23. private:
    24. std::stack<int> stk1, stk2;
    25. };
    26. int main(int argc, char argv[]){
    27. return 0;
    28. }

    6--相交链表(160)

    主要思路:

            走过自己的路,再走你走过的路,要么在相交的节点相遇,要么都是 nullptr;

            非常有哲学意义的一道题!!!!!

    1. #include
    2. #include
    3. struct ListNode{
    4. int val;
    5. ListNode *next;
    6. ListNode(int x) : val(x), next(NULL) {}
    7. };
    8. class Solution{
    9. public:
    10. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    11. if(headA == nullptr || headB == nullptr){
    12. return nullptr;
    13. }
    14. ListNode* tmp1 = headA, *tmp2 = headB;
    15. while(tmp1 != tmp2){
    16. tmp1 = tmp1->next;
    17. tmp2 = tmp2->next;
    18. if(tmp1 == tmp2) break; // 相遇或同时为null
    19. if(tmp1 == nullptr) tmp1 = headB;
    20. if(tmp2 == nullptr) tmp2 = headA;
    21. }
    22. if(tmp1 == nullptr) return nullptr;
    23. return tmp1;
    24. }
    25. };
    26. int main(int argc, char argv[]){
    27. // intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
    28. ListNode *Node1 = new ListNode(4);
    29. ListNode *Node2 = new ListNode(1);
    30. ListNode *Node3 = new ListNode(8);
    31. ListNode *Node4 = new ListNode(4);
    32. ListNode *Node5 = new ListNode(5);
    33. Node1->next = Node2;
    34. Node2->next = Node3;
    35. Node3->next = Node4;
    36. Node4->next = Node5;
    37. ListNode *Node6 = new ListNode(5);
    38. ListNode *Node7 = new ListNode(6);
    39. ListNode *Node8 = new ListNode(1);
    40. Node6->next = Node7;
    41. Node7->next = Node8;
    42. Node8->next = Node3;
    43. Solution S1;
    44. ListNode* res = S1.getIntersectionNode(Node1, Node6);
    45. if(res != nullptr) std::cout << res->val << std::endl;
    46. else std::cout << "null" << std::endl;
    47. return 0;
    48. }

    7--多数元素(169)

    主要思路:

            假设有一个桶,当一个桶为空时,放入一个新的元素;当一个桶不为空时,当准备放入一个新的元素时,如果新的元素与桶内的元素不相同,则不放入这个新元素且将桶内的一个元素拿出来;

            对于本题,最后桶剩下的元素肯定是多数元素,因为题目规定多数元素必定存在;具体到本题可以用一个计数器表示桶内的元素个数,当计数器为空时可以随意放入新的元素;

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int majorityElement(std::vector<int>& nums) {
    6. int count = 0, val = nums[0]; // count 表示桶内元素的个数, val表示桶内的元素值
    7. for(int num : nums){
    8. if(num == val) count++;
    9. else{
    10. if(count == 0){ // 桶为空
    11. val = num;
    12. count = 1;
    13. }
    14. else{ // 桶不为空,抵消一个元素
    15. count--;
    16. }
    17. }
    18. }
    19. return val;
    20. }
    21. };
    22. int main(int argc, char argv[]){
    23. // nums = [3,2,3]
    24. std::vector<int> test = {3, 2, 3};
    25. Solution S1;
    26. int res = S1.majorityElement(test);
    27. std::cout << res << std::endl;
    28. return 0;
    29. }

    8--打家劫舍(198)

    主要思路:

            访问到第i间房屋时,设dp[i][0]表示打劫第i间房屋,dp[i][1]表示不打劫第i间房屋,能够获得的最高金额;

            状态转移方程 dp[i][0] = dp[i-1][1] + nums[i],dp[i][1] = dp[i-1][0]; 

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int rob(std::vector<int>& nums) {
    6. std::vectorint>> dp(nums.size(), std::vector<int>(2, 0));
    7. // 初始化
    8. dp[0][0] = nums[0];
    9. dp[0][1] = 0;
    10. for(int i = 1; i < nums.size(); i++){
    11. dp[i][0] = dp[i-1][1] + nums[i]; // 打劫房屋i,前一间房屋只能不被打劫
    12. dp[i][1] = std::max(dp[i-1][0], dp[i-1][1]); // 不打劫房屋i,前一间房屋可以被打劫也可以不被打劫
    13. }
    14. return std::max(dp[nums.size() - 1][0], dp[nums.size() - 1][1]);
    15. }
    16. };
    17. int main(int argc, char *argv[]) {
    18. std::vector<int> test = {1, 2, 3, 1};
    19. Solution S1;
    20. int res = S1.rob(test);
    21. std::cout << res << std::endl;
    22. return 0;
    23. }

    9--岛屿数量(200)

    主要思路:

            深度优先搜索,从(i, j)出发,递归搜索上下左右四个方向的陆地,将相连的陆地置为0;

    1. #include
    2. #include
    3. class Solution {
    4. public:
    5. int numIslands(std::vectorchar>>& grid) {
    6. int m = grid.size(), n = grid[0].size();
    7. int res = 0;
    8. for(int i = 0; i < m; i++){
    9. for(int j = 0; j < n; j++){
    10. if(grid[i][j] == '1'){
    11. res++;
    12. dfs(grid, i, j);
    13. }
    14. }
    15. }
    16. return res;
    17. }
    18. void dfs(std::vectorchar>>& grid, int i, int j){
    19. if(i >= grid.size() || j >= grid[0].size() || grid[i][j] == '0' || i < 0 || j < 0) return;
    20. grid[i][j] = '0';
    21. dfs(grid, i+1, j);
    22. dfs(grid, i, j+1);
    23. dfs(grid, i-1, j);
    24. dfs(grid, i, j-1);
    25. }
    26. };
    27. int main(int argc, char *argv[]) {
    28. std::vectorchar>> test = {{'1', '1', '0', '0', '0'},
    29. {'1', '1', '0', '0', '0'},
    30. {'0', '0', '1', '0', '0'},
    31. {'0', '0', '0', '1', '1'}};
    32. Solution S1;
    33. int res = S1.numIslands(test);
    34. std::cout << res << std::endl;
    35. return 0;
    36. }

    10--反转链表(206)

    主要思路:

            头插法,使用虚拟头节点。

    1. #include
    2. struct ListNode {
    3. int val;
    4. ListNode *next;
    5. ListNode() : val(0), next(nullptr) {}
    6. ListNode(int x) : val(x), next(nullptr) {}
    7. ListNode(int x, ListNode *next) : val(x), next(next) {}
    8. };
    9. class Solution {
    10. public:
    11. ListNode* reverseList(ListNode* head) {
    12. if(head == nullptr) return nullptr;
    13. ListNode* FHead = new ListNode(-1); // 虚拟头节点
    14. FHead->next = head;
    15. ListNode* tmp = head->next;
    16. head->next= nullptr;
    17. while(tmp != nullptr){
    18. ListNode* next = tmp->next; // 记录下一个结点
    19. // 头插
    20. tmp->next = FHead->next;
    21. FHead->next = tmp;
    22. // 更新
    23. tmp = next;
    24. }
    25. return FHead->next;
    26. }
    27. };
    28. int main(int argc, char *argv[]) {
    29. ListNode *Node1 = new ListNode(1);
    30. ListNode *Node2 = new ListNode(2);
    31. ListNode *Node3 = new ListNode(3);
    32. ListNode *Node4 = new ListNode(4);
    33. ListNode *Node5 = new ListNode(5);
    34. Node1->next = Node2;
    35. Node2->next = Node3;
    36. Node3->next = Node4;
    37. Node4->next = Node5;
    38. Solution S1;
    39. ListNode *res = S1.reverseList(Node1);
    40. ListNode *tmp = res;
    41. while(tmp != nullptr){
    42. std::cout << tmp->val << " ";
    43. tmp = tmp->next;
    44. }
    45. return 0;
    46. }

  • 相关阅读:
    软件测试——从0开始的ios自动化测试(二)
    夏令时及java中常用方法
    Spring boot 实践Rabbitmq消息防丢失
    【RuoYi移动端】uni-app中的单击和双击事件
    使用神经网络实现对天气的预测
    java项目_第166期ssm多人命题系统_java毕业设计_计算机毕业设计
    基于主动视觉机制的深度学习--一个综合池化框架
    【前端学习记录】neffos插件与控制台交互
    R数据分析:用R建立预测模型
    1.什么是Angular?
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/133248745