当前位置:   article > 正文

数据结构:链表 课程总结 + leetcode刷题_将n个数字插入空链表l中,使l形成递减有序链表

将n个数字插入空链表l中,使l形成递减有序链表

Leetcode:

T1:利用 “归并排序” 对 链表 进行 排序:

关键:

(1)merge_sort函数 : 递归 函数--出口,直到只有1个 或者 0个 元素为止,直接返回这个节点,作用就是 链表 分成 2半, 

(2)merge_sort函数中:因为是链表,所以需要 利用 fast ,slow快慢指针 找到中间位置,然后分别找到 left链表 和 right链表的 头节点(注意把 left链表的 尾节点 设置为 NULL)

(3)merge函数:不需要用递归 实现 ,直接用while循环即可, 直到其中有一个指针为NULL,最后接上剩下的 那个链表(等价于上课 讲到的 2个有序 链表的 合并)

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. //其中 一些 注释 用于之前的 调试,因为 函数体里面的 临时指针很容易被 销毁掉
  12. class Solution {
  13. public:
  14. //利用 归并排序加以实现:
  15. ListNode* sortList(ListNode* head) {
  16. head=merge_sort(head);
  17. return head;
  18. }
  19. ListNode* merge_sort(ListNode* head)
  20. {
  21. //static int time=1;
  22. //cout<<"这是第"<<time++<<"次sort"<<endl;
  23. //出口 ,head长度为10 直接返回
  24. if(head == nullptr || head->next==nullptr)
  25. {
  26. return head;
  27. }
  28. //(1)利用 快慢指针 找到中间位置
  29. ListNode* fast=new ListNode(0);
  30. ListNode* slow=new ListNode(0);
  31. fast=head->next; //这种链表没有头节点,每个节点都有val
  32. slow=head;
  33. //fast 每次移动2格 , slow移动一格
  34. while(fast!= NULL && fast->next!=NULL)
  35. {
  36. fast=fast->next->next;
  37. slow=slow->next;
  38. }
  39. //(2)此时slow指向的位置就是向下取整的中间位置
  40. //递归,直到1个元素 或0 个元素
  41. ListNode* right=new ListNode(0);
  42. ListNode* left=new ListNode(0);
  43. right=merge_sort(slow->next); //右边链表的起点
  44. slow->next=NULL; //左边链表的尾部 置NULL
  45. left=merge_sort(head);
  46. head=merge(left,right);
  47. //尝试 添加一个新的 堆区空间
  48. ListNode* ans=new ListNode(0);
  49. ans=head;
  50. //show(ans);
  51. return ans; //head这个指针式传递进来的,可以返回,不是临时变量
  52. }
  53. //输出 函数
  54. void show(ListNode* node)
  55. {
  56. while(node!=NULL)
  57. {
  58. cout<<node->val<<endl;
  59. node=node->next;
  60. }
  61. }
  62. ListNode* merge(ListNode* left,ListNode* right)
  63. {
  64. //cout<<"left:"<<left->val<<"right:"<<right->val<<"merge一下"<<endl;
  65. //既然传递进来的 leftright都是 有序的 ,相当于 上课讲的链表merge合并
  66. ListNode* root_ =new ListNode(0);//堆区,防止销毁临时变量
  67. ListNode* root=new ListNode(0);
  68. root=root_;
  69. root->next=NULL;
  70. while(left!=NULL && right!=NULL)
  71. {
  72. if(left->val < right->val)
  73. {
  74. //left先进
  75. root->next= left;
  76. root=root->next;
  77. left=left->next;
  78. }
  79. else
  80. {
  81. //right
  82. root->next=right;
  83. root=root->next;
  84. right=right->next;
  85. }
  86. }
  87. //(2)处理 剩余的 节点
  88. if(left!=NULL)
  89. {
  90. //左边非空
  91. root->next=left;
  92. }
  93. else if(right != NULL)
  94. {
  95. root->next= right;
  96. }
  97. return root_->next;
  98. }
  99. };

T2: 重排链表 (有点像 2个 链表交错)

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

 L0 → L1 → … → Ln-1 → Ln 
请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解:

1.关键:

(1)总的来说 : 就是3个 操作: 快慢指针找到中间点  --  翻转后半链表  --  “交错合并”2个链表

(2) 注意 ,交错合并的 时候 指针的指向 次序 需要 “小心翼翼”

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. void reorderList(ListNode* head) {
  14. //妙啊,这种 “交错的结构”--找中间点 , 然后对后半部分翻转 ,最后合并2个list
  15. //特殊,只有一个节点,直接返回
  16. if(head==NULL || head->next == NULL)
  17. {
  18. return ;
  19. }
  20. //1.找到中间点:(利用快慢指针 -- 找到“向下取整”的中间点)
  21. ListNode* fast=head->next;
  22. ListNode* slow=head;
  23. while(fast!=NULL && fast->next!=NULL)
  24. {
  25. fast=fast->next->next;
  26. slow=slow->next;
  27. }
  28. //此时slow就是那个“向下取整的”中间点
  29. //无所谓 ,反正 多出的哪一个 最后接上就好了
  30. //2.将slow->next 一直到 NULL前的 node进行翻转
  31. ListNode* start=slow->next;
  32. ListNode* tmp=start->next; //tmp永远维持 start的原序的 下一个节点
  33. start->next=NULL; //翻转后的 “尾部” 置NULL
  34. //别忘记了,需要把 head的 最后一个节点的nextNULL
  35. slow->next=NULL;
  36. while(tmp!=NULL && start!=NULL)
  37. {
  38. //tmp需要 指向现在的 start然后tmp给start,把u给tmp
  39. ListNode* u=tmp->next;
  40. tmp->next=start;
  41. start=tmp;
  42. tmp=u;
  43. //成功翻转
  44. }
  45. //----测试: --好了, 上面的 步骤都没问题
  46. //show(head);
  47. //show(start);
  48. //tmp==NULL,start指向新的起点
  49. //3.好了2个链表的起点分别是head 和 start
  50. //开始 “交错合并”2个链表
  51. ListNode* pa=head;
  52. ListNode* pb=start;
  53. ListNode* pc=new ListNode(0);//头节点
  54. ListNode* new_head=pc;
  55. while(pa!=NULL && pb!=NULL)
  56. {
  57. pc->next=pa;
  58. pa=pa->next;
  59. //--次序
  60. pc=pc->next;
  61. pc->next=pb;
  62. pb=pb->next;
  63. pc=pc->next;
  64. }
  65. if(pa!=NULL)
  66. {
  67. pc->next = pa;
  68. }
  69. if(pb!=NULL)
  70. {
  71. pc->next=pb;
  72. }
  73. head=new_head->next;
  74. }
  75. //测试函数
  76. void show(ListNode* node)
  77. {
  78. while(node!=NULL)
  79. {
  80. cout<<node->val<<" ";
  81. node=node->next;
  82. }
  83. cout<<endl;
  84. }
  85. };

T3:(链表的必备熟练操作之一)翻转链表

给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。

解:

1.关键:

由于 翻转的 指针变化 ,需要逻辑清晰的 变化tmp , head ,临时u这3个指针,次序很重要

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* reverseList(ListNode* head) {
  14. //这是一个基本的 操作,应该作为像“冒泡排序”一样熟练
  15. //tmp指向 head的 下一个节点,u作为临时节点
  16. //特殊情况
  17. if(head == NULL || head->next == NULL)
  18. {
  19. return head;
  20. }
  21. //--
  22. ListNode* tmp=head->next;
  23. head->next=NULL; //翻转后的 为节点的 next指针 置NULL
  24. while(tmp!=NULL && head!=NULL)
  25. {
  26. //将tmp->next 给到u ,然后及那个tmp->next指向head,tmp给head,u给tmp
  27. ListNode* u=tmp->next;
  28. tmp->next=head;
  29. head=tmp;
  30. tmp=u;
  31. }
  32. return head;
  33. }
  34. };

T4: 判断一个链表是否为 “回文链表”

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

解:

1.关键:

(1) 就是 3个 基本 操作的 组合: 快慢指针找到中间点 -- 翻转链表 -- 2个链表对应位置的比较

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. bool isPalindrome(ListNode* head) {
  14. //3个操作: 找中间点 - 翻转后半部分 - 2个链表的val比较
  15. //--特殊情况:
  16. if(head == NULL || head->next == NULL)
  17. {
  18. return true;
  19. }
  20. //1.利用 快慢指针 找到中间点
  21. ListNode* fast=head->next;
  22. ListNode* slow=head;
  23. while(fast!=NULL && fast->next!=NULL)
  24. {
  25. fast=fast->next->next;
  26. slow=slow->next;
  27. }
  28. ListNode* start=slow->next;
  29. slow->next= NULL;
  30. //2.翻转后半 链表
  31. ListNode *tmp = start->next;
  32. start->next=NULL; //翻转 后的 为节点的 next指针 置NULL
  33. while(tmp!=NULL && start!=NULL)
  34. {
  35. ListNode* u=tmp->next;
  36. tmp->next=start;
  37. start=tmp;
  38. tmp=u;
  39. }
  40. //3. 2个链表之间的 比较 ,头节点分别 是 head 和 start
  41. while(head !=NULL && start!=NULL)
  42. {
  43. if(head->val != start->val)
  44. {
  45. return false;
  46. }
  47. head = head->next;
  48. start=start->next;
  49. }
  50. return true;
  51. }
  52. };

T5:合并 多个 有序链表 

给定一个链表数组,每个链表都已经按升序排列。

请将所有链表合并到一个升序链表中,返回合并后的链表。

解:

1.关键:

(1)基础:2个有序链表的 排序

(2)思维: 利用 多元gcd的求解思维, 多元 == 依次二元

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* mergeKLists(vector<ListNode*>& lists) {
  14. //这个题目 就是 多个 链表的 merge
  15. //借鉴 求解多元gcd的 思路,多个 == 依次 2个,所以只要不断调用 2个链表的 合并
  16. //特殊情况
  17. if(lists.size() == 0)
  18. {
  19. return NULL;
  20. }
  21. ListNode* ans = lists[0];
  22. int size= lists.size();
  23. for(int i=1;i<size;i++)
  24. {
  25. ans=merge(ans,lists[i]);
  26. }
  27. return ans;
  28. }
  29. //2个 有序链表的 合并函数
  30. ListNode* merge(ListNode* left, ListNode* right)
  31. {
  32. ListNode* tmp=new ListNode(0);
  33. ListNode* ans=tmp;
  34. while(left!=NULL && right!=NULL)
  35. {
  36. //case1 : left小:
  37. if(left->val < right->val)
  38. {
  39. tmp->next=left;
  40. left=left->next;
  41. tmp=tmp->next;
  42. }
  43. else
  44. {
  45. tmp->next=right;
  46. right=right->next;
  47. tmp=tmp->next;
  48. }
  49. }
  50. //--那个还有剩余
  51. if(left!=NULL)
  52. {
  53. tmp->next=left;
  54. }
  55. if(right!=NULL)
  56. {
  57. tmp->next=right;
  58. }
  59. return ans->next;
  60. }
  61. };

T6:复杂链表的 复制

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

解:

1.关键:

(1)这是一道 关于  unordered_map 的问题

(2)首先 遍历 一次 , 存储下 对应的 next指针 和 在map中存储 对应的Node的对应关系

(3)最后遍历一次, 存储对应的 random指针

2.代码:

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public:
  5. int val;
  6. Node* next;
  7. Node* random;
  8. Node(int _val) {
  9. val = _val;
  10. next = NULL;
  11. random = NULL;
  12. }
  13. };
  14. */
  15. class Solution {
  16. public:
  17. Node* copyRandomList(Node* head) {
  18. //特殊
  19. if(head == NULL)
  20. {
  21. return NULL;
  22. }
  23. //设置一个 递增变量指针
  24. Node* tmp=head;
  25. //设置一个map用于存储对应的 Node的关系
  26. unordered_map<Node*,Node*> Map;
  27. //利用一个map进行存储 两者对应的节点
  28. Node* new_head=new Node(head->val);
  29. Map[head]=new_head;
  30. tmp=tmp->next;
  31. Node* tmp2=new_head;
  32. while(tmp!=NULL)
  33. {
  34. //每次开辟一个新的 NOde空间,
  35. Node* new_node=new Node(tmp->val);
  36. Map[tmp]=new_node;
  37. tmp2->next=new_node;
  38. tmp2=tmp2->next;
  39. tmp=tmp->next;
  40. //Map中存储对应的 Node的 关系:
  41. }
  42. //2.遍历了之后,需要 再次遍历 ,并且处理 random指针
  43. tmp=head;
  44. tmp2=new_head;
  45. while(tmp!=NULL)
  46. {
  47. tmp2->random = Map[tmp->random];
  48. tmp=tmp->next;
  49. tmp2=tmp2->next;
  50. }
  51. return new_head;
  52. }
  53. };

T7: 向 有序 环形链表中 insert一个节点

给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。

给定的可以是这个列表中任意一个顶点的指针,并不一定是这个列表中最小元素的指针。

如果有多个满足条件的插入位置,可以选择任意一个位置插入新的值,插入后整个列表仍然保持有序。

如果列表为空(给定的节点是 null),需要创建一个循环有序列表并返回这个节点。否则。请返回原先给定的节点。

解:

1.关键:

(1) 模拟遍历查找 + insert节点操作(熟练)

(2)需要考虑 一些 特殊操作 和 取等、、、那些情况

2.代码:

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public:
  5. int val;
  6. Node* next;
  7. Node() {}
  8. Node(int _val) {
  9. val = _val;
  10. next = NULL;
  11. }
  12. Node(int _val, Node* _next) {
  13. val = _val;
  14. next = _next;
  15. }
  16. };
  17. */
  18. class Solution {
  19. public:
  20. Node* insert(Node* head, int insertVal) {
  21. //思路: 直接模拟 遍历查找 + 插入一个节点的 操作即可
  22. //特殊情况:
  23. if(head == NULL)
  24. {
  25. Node* new_head =new Node(insertVal);
  26. new_head->next = new_head;
  27. return new_head;
  28. }
  29. if(head->next == head)
  30. {
  31. Node* new_head =new Node(insertVal);
  32. new_head->next = head;
  33. head->next = new_head;
  34. return head;
  35. }
  36. //1copy 保留开始 的head节点
  37. Node* tmp = head;
  38. Node* new_node=new Node(insertVal);
  39. //将这个new_node通过遍历 找到合适的 位置, 然后 执行insert操作
  40. //(2)遍历
  41. //符合条件的位置: tmp->val < next->val tmp->val < insertVal && tmp->next->val
  42. //或者: tmp->val > next->val && tmp->val < insertVal
  43. int i=1;
  44. while(1)
  45. {
  46. if(tmp == head && i!=1)
  47. {
  48. //说明 所有的val都相同
  49. new_node->next = tmp->next;
  50. tmp->next = new_node;
  51. return head;
  52. }
  53. i++;
  54. cout<<"来过吗 "<<tmp->val <<endl; //--陷入死循环
  55. if(tmp->val < tmp->next->val && tmp->next->val >= insertVal && tmp->val <= insertVal)
  56. {
  57. new_node->next = tmp->next;
  58. tmp->next = new_node;
  59. return head;
  60. }
  61. else if(tmp->val > tmp->next->val && (tmp->val <= insertVal || tmp->next->val >= insertVal))
  62. {
  63. new_node->next = tmp->next;
  64. tmp->next = new_node;
  65. return head;
  66. }
  67. //case3:
  68. tmp=tmp->next;
  69. }
  70. //:只剩 一种case 需要考虑, 就是所有的 val都相等怎么办?
  71. //很简单 , 那时候tmp一定会回到head,直接insert到这个位置即可
  72. }
  73. };

T8:删除 链表 节点(基本操作)

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

解:

1.关键:

(1)利用 “双指针 ”找到需要删除的 节点的前驱节点

(2)然后 next 指next->next即可

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode* deleteNode(ListNode* head, int val) {
  12. //找到这个 节点的 前驱节点的指针即可
  13. ListNode* tmp=head->next;
  14. ListNode*tmp2=head;
  15. //特殊情况:
  16. if(head->val == val)
  17. {
  18. head = head->next;
  19. return head;
  20. }
  21. //--一般情况:
  22. while(tmp->val !=val)
  23. {
  24. tmp2=tmp;
  25. tmp = tmp->next;
  26. }
  27. tmp2->next = tmp2->next->next;
  28. return head;
  29. }
  30. };

T9:展平 多级 双向 链表 

多级双向链表中,除了指向下一个节点和前一个节点指针之外,它还有一个子链表指针,可能指向单独的双向链表。这些子列表也可能会有一个或多个自己的子项,依此类推,生成多级数据结构,如下面的示例所示。

给定位于列表第一级的头节点,请扁平化列表,即将这样的多级双向链表展平成普通的双向链表,使所有结点出现在单级双链表中。

解:

1.关键:

(1)这个 “结构图 ”一看 马上 反应过来 就是 dfs(深搜) , 然后 直接用递归实现

(2)重点是 , 处理好 每次dfs的 出口时 head->next == NULL   以及 最后一个 节点的 child是否还有,

!! 每次返回的 都是 “当前这一层的 最后一个 节点的指针” , 除非是 next为 NULL,而child不为NULL,这时候就需要 返回 下一层的 最后一个节点 ,作为 “这一层的”最后一个节点

2.代码:

  1. /*
  2. // Definition for a Node.
  3. class Node {
  4. public:
  5. int val;
  6. Node* prev;
  7. Node* next;
  8. Node* child;
  9. };
  10. */
  11. class Solution {
  12. public:
  13. Node* flatten(Node* head) {
  14. //很明显的 一个 dfs深度优先搜索的 结构
  15. //特殊:
  16. if(head == NULL)
  17. {
  18. return NULL;
  19. }
  20. dfs(head);
  21. //--遍历一次, 置空所有的 CHILD指针域
  22. Node* tmp=head;
  23. while(tmp!=NULL)
  24. {
  25. tmp->child = NULL;
  26. tmp= tmp->next;
  27. }
  28. return head;
  29. }
  30. Node* dfs(Node* head)
  31. {
  32. //这个递归 函数的 出口就是 next =NULL ,返回 每一层的 最后一个NOde的指针
  33. Node* pa = new Node;
  34. pa = head; //和head指向同一个 对象
  35. //1.出口
  36. if(head->next == NULL)
  37. {
  38. if(head->child != NULL)
  39. {
  40. head->next = head->child;
  41. head->child->prev = head;
  42. head=dfs(head->child); //先深搜一次(下一层的连接)然后再 进行连接
  43. return head;
  44. }
  45. else{
  46. return head;
  47. }
  48. }
  49. //2.递归:
  50. //--递归 搜索
  51. while(pa->next !=NULL)
  52. {
  53. if(pa->child != NULL)
  54. {
  55. //需要 再深入 一层递归 , 并且返回 递归结果的头节点指针
  56. //(1)存下 这一层的 next 的指针
  57. Node* next_=pa->next;
  58. //(2)让pa的next 和 pre 和 child进行 互相交织
  59. pa->next = pa->child;
  60. pa->child->prev = pa;
  61. //(3)返回递归 得到的 下一层的最后一个 node的 指针
  62. //然后和 next_进行交织
  63. Node* tmp=dfs(pa->child); //返回 下一层的 最后一个节点的指针
  64. tmp->next = next_;
  65. next_->prev = tmp;
  66. //--更新pa 到next_
  67. pa = next_;
  68. }
  69. else
  70. {
  71. pa= pa->next; //直接更新 pa
  72. }
  73. //pa指向 下一个位置
  74. }
  75. //有一种可能, pa->next == NULlL ,倒是child还有
  76. if(pa->child != NULL)
  77. {
  78. pa->next = pa->child;
  79. pa->child->prev = pa;
  80. pa=dfs(pa->child); //先深搜一次(下一层的连接)然后再 进行连接
  81. }
  82. return pa; //此时pa就是这一层的 最后一个节点
  83. }
  84. //最后一步 , 就是 将所有的child 域全部置空
  85. };

T10:判断链表是否 相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交

解:

1.关键:

(1)利用 set 容器 存储 第一条链表中的 所有 节点

(2)第二次 遍历 一次 第二条 链表 判断是否 count 重复 出现的 节点,如果有 就直接返回了

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  12. //利用set容器存储 第一条链表的 所有节点,然后遍历一次第二条链表即可
  13. unordered_set<ListNode*> Set;
  14. //(1)遍历第一条 链表
  15. ListNode* node1=headA;
  16. while(node1!=NULL)
  17. {
  18. Set.insert(node1);
  19. node1=node1->next;
  20. }
  21. //(2)遍历第二条 链表 + 判断是否在set中出现过
  22. node1 = headB;
  23. while(node1!=NULL)
  24. {
  25. if(Set.count(node1))
  26. {
  27. return node1;
  28. }
  29. node1=node1->next;
  30. }
  31. return NULL;
  32. }
  33. };

T11:又是 对一个链表进行 排序(熟练掌握 归并排序)

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

解:

1.关键:

(1)归并排序 = 递归函数merge_sort + 2个有序链表的“合并”的merge函数

(2)merge_sort函数:递归

<1>出口if(head == NULL || head->next==NULL) (0个 或 1个节点,就是有序的,直接返回)

<2>将传递进来的1个链表head 拆分为 大小几乎相同的 两个链表, 利用快慢指针找到“中间点”(记得将 left链表的 最后一个节点的 next指针置空)

<3>然后 left = merge_sort(head) ,right = merge_sort(start) ,分别递归, 返回的时候 都是 2个有序链表的 头节点

<4>最后 merge(left,right), 2个链表合并为一个有序的 链表 ,最终返回的 就是一个 有序的 链表

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* sortList(ListNode* head) {
  14. //利用 归并 排序 ,对一条链表进行 排序
  15. return merge_sort(head);
  16. }
  17. ListNode* merge_sort(ListNode* head)
  18. {
  19. //特殊
  20. if(head==NULL || head->next == NULL)
  21. {
  22. return head;
  23. }
  24. //不断的 分治 , 先递归 进入到 最深的 只有一个元素的有序情况,然后调用merge
  25. //(1)找到 当前 这一层的 中间点
  26. //利用 快慢指针实现
  27. ListNode* fast= head->next;
  28. ListNode* slow= head;
  29. while(fast!=NULL && fast->next!=NULL)
  30. {
  31. fast= fast->next->next;
  32. slow = slow->next;
  33. }
  34. //此时slow已经指向 “向下取整的中间点”
  35. //(2)分成2个链表进行递归
  36. ListNode* start=slow->next;
  37. slow->next= NULL;
  38. //head 和 start
  39. ListNode* left = merge_sort(head);
  40. ListNode* right = merge_sort(start);
  41. //--返回的 都是 已经排序完成的 头节点
  42. return merge(left,right);
  43. }
  44. ListNode* merge(ListNode* pa,ListNode* pb)
  45. {
  46. //特殊
  47. if(pa == NULL)
  48. {
  49. return pb;
  50. }
  51. if(pb == NULL)
  52. {
  53. return pa;
  54. }
  55. //(1)这个函数 ,传递进来2个 头节点的 指针,都是有序的 ,相当于是合并2个有序
  56. //自己设计一个 头节点,然后 返回头节点 next指针即可
  57. ListNode* pc=new ListNode(0);
  58. ListNode* tmp=pc;
  59. while(pa!=NULL && pb!=NULL)
  60. {
  61. //case1:
  62. if(pa->val < pb->val)
  63. {
  64. pc->next = pa;
  65. pa=pa->next;
  66. pc=pc->next;
  67. }
  68. else
  69. {
  70. pc->next = pb;
  71. pb= pb->next;
  72. pc=pc->next;
  73. }
  74. }
  75. //--收尾
  76. if(pa!=NULL)
  77. {
  78. pc->next = pa;
  79. }
  80. if(pb!=NULL)
  81. {
  82. pc->next = pb;
  83. }
  84. return tmp->next;
  85. }
  86. };

T12:用 链表表示的 2个整数的 求和

给定两个用链表表示的整数,每个节点包含一个数位。

这些数位是反向存放的,也就是个位排在链表首部。

编写函数对这两个整数求和,并用链表形式返回结果。

解:

1.关键:

(1)思路一:先 求出总和 , 然后造一个链表(利用 除10 mod10的思路)

(2)思路二: 借助 全加器的 思想, 利用 串行进位的 while循环方法 , a,b,c得到本位和 然后同时进行链表的 构造 , 不断更新进位, 别忘了 最后一个进位c不为0的时候 ,需要创造一个 节点

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
  12. //第二种方法: 相当于是 写一个 全加器, 然后 循环 串行相加
  13. //特殊情况:
  14. if(l1 == NULL)
  15. {
  16. return l2;
  17. }
  18. if(l2 == NULL)
  19. {
  20. return l1;
  21. }
  22. //--
  23. ListNode* l3=new ListNode(0);
  24. ListNode* head = l3;
  25. int a=l1->val,b=l2->val,c=0,sum=0;
  26. while(l1!=NULL && l2!=NULL)
  27. {
  28. a=l1->val;
  29. b=l2->val;
  30. sum=(a+b+c)%10; //本位和
  31. //--造链表
  32. ListNode* node= new ListNode(sum);
  33. l3->next = node;
  34. l3=l3->next;
  35. c=(a+b+c)/10; //进位和
  36. l1=l1->next;
  37. l2=l2->next;
  38. }
  39. //收尾:
  40. while(l1 != NULL)
  41. {
  42. sum=( c+ l1->val)%10;
  43. c=(c+l1->val)/10;
  44. //--造链表
  45. ListNode* node= new ListNode(sum);
  46. l3->next = node;
  47. l3=l3->next;
  48. c=(c+l1->val)/10;
  49. l1=l1->next;
  50. }
  51. //--
  52. while(l2!=NULL)
  53. {
  54. sum=(c+l2->val)%10;
  55. c=(c+l2->val)/10;
  56. ListNode* node= new ListNode(sum);
  57. l3->next = node;
  58. l3=l3->next;
  59. c=(c+l2->val)/10;
  60. l2=l2->next;
  61. }
  62. //如果 最终的 c的值不为0 ,还需要 造一个节点
  63. if(c!= 0 )
  64. {
  65. ListNode* node= new ListNode(c);
  66. l3->next = node;
  67. l3=l3->next;
  68. }
  69. return head->next;
  70. }
  71. };

T13:分隔链表(以数值x为界限)

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

解:

1.关键:

(1)利用 2个 标记位置不动的节点head1,head2 ,以及 不断移动的 2个节点p1,p2

(2)遍历一次 “原链表” ,然后 在“对应的 位置” 执行 “节点插入的 基本操作”

(3)最后执行一次 “删除节点的 基本操作”

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* partition(ListNode* head, int x) {
  14. //特殊情况:
  15. if(head == NULL || head->next== NULL)
  16. {
  17. return head;
  18. }
  19. //采用 不断插入的 思想,2个 相对插入位置
  20. ListNode* p1=new ListNode(0);
  21. ListNode* p2=new ListNode(0);
  22. ListNode* head1 = p1;
  23. ListNode* head2 = p2; //head不动, p指针不断移动
  24. //(1)初始链表
  25. p1->next=p2;
  26. //(2)while循环插入
  27. //val < x则 插入到p1 和 head2之间,并且更新p1
  28. //否则 ,连接接到p2之后,并且 更新p2
  29. while(head!=NULL)
  30. {
  31. //保留head->next
  32. ListNode* tmp = head->next;
  33. if(head->val < x)
  34. {
  35. head->next = p1->next;
  36. p1->next = head;
  37. p1 = p1->next;
  38. }
  39. else
  40. {
  41. p2->next = head;
  42. p2= p2->next;
  43. }
  44. head=tmp;
  45. }
  46. p2->next=NULL;
  47. //3)删除 标志节点:head2 需要删除
  48. p1->next = p1->next->next;
  49. return head1->next;
  50. }
  51. };

T14:分割链表 , 将一个 链表分割为 k个部分

给你一个头结点为 head 的单链表和一个整数 k ,请你设计一个算法将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等:任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。

这 k 个部分应该按照在链表中出现的顺序排列,并且排在前面的部分的长度应该大于或等于排在后面的长度。

返回一个由上述 k 部分组成的数组。

解:

1.关键:

(1)遍历一次 ,得到 链表的 总长度

(2)特殊情况 ,k > n时 需要 返回一个 长度为 k 的vector

(3)一般情况,大概是 一个 数论题目: cnt2 = n/k--表示后面的 子链表长度,cnt1长度的子链表 一共有 n%k =diff 个 ,然后 遍历一次 对链表进行 切割 即可

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. vector<ListNode*> splitListToParts(ListNode* head, int k) {
  14. //就是将 一个 链表 分割为k个 链表 ,重点在于 计算链表的长度,和数论有关
  15. vector<ListNode*> vec;
  16. //(1)遍历一次 ,得到总长度n
  17. ListNode* tmp=head;
  18. int n=0;
  19. while(tmp !=NULL)
  20. {
  21. n++;
  22. tmp = tmp->next;
  23. }
  24. //特殊 : k > n
  25. //需要 将 原链表全部割开
  26. if(k > n)
  27. {
  28. vector<ListNode*> ans(k); //大小为k
  29. int i=0;
  30. while(head !=NULL)
  31. {
  32. ListNode* u= head->next;
  33. head->next = NULL;
  34. ans[i++] = head;
  35. head = u;
  36. }
  37. return ans;
  38. }
  39. //(2)计算 各个数值
  40. int cnt2= n/k;
  41. int diff=n%k;
  42. //需要有 diff 个前面的 链表长度为cnt2+1
  43. int cnt1= cnt2+1;
  44. //(3)再次遍历:
  45. int i=1;
  46. for(i=1 ; i <= diff ;i++)
  47. {
  48. ListNode* new_head=head;
  49. //直接原地 分割好了
  50. for(int j=1;j<=cnt1-1;j++)
  51. {
  52. head = head->next;
  53. }
  54. ListNode* next_head = head->next;
  55. head->next= NULL;
  56. vec.push_back(new_head);
  57. head = next_head;
  58. //此时 head指向的是 长度为cnt1的 子链表的最后一个节点
  59. }
  60. //--继续
  61. for(i=diff+1 ; i <= k ;i++)
  62. {
  63. cout<<"第二部分 来过几次 "<<endl; //只来过一次? why
  64. ListNode* new_head=head;
  65. //直接原地 分割好了
  66. for(int j=1;j<=cnt2-1;j++)
  67. {
  68. head = head->next;
  69. }
  70. ListNode* next_head = NULL;
  71. if(head != NULL)
  72. {
  73. next_head = head->next;
  74. }
  75. else
  76. {
  77. continue;
  78. }
  79. head->next= NULL;
  80. vec.push_back(new_head);
  81. head = next_head;
  82. //此时 head指向的是 长度为cnt1的 子链表的最后一个节点
  83. }
  84. return vec;
  85. //还要 考虑 k > n的情况
  86. }
  87. };

T15:从尾到头 打印 链表 中的 元素

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

解:

1.关键:

(1)等价于 stack栈结构 中的 “后进先出”

(2)不需要 反转 链表 ,只要 通过遍历一次 得到 链表的 总长度 ,然后 反向 存储 数值即可

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. vector<int> reversePrint(ListNode* head) {
  12. //先遍历一次 获取长度n,(不需要反转),然后 从vec的后面开始存放数值
  13. //其实利用一个stack更好, 后进先出
  14. int n=0;
  15. ListNode* tmp= head;
  16. while(tmp!= NULL)
  17. {
  18. n++;
  19. tmp=tmp->next;
  20. }
  21. vector<int> ans(n);
  22. while(head != NULL)
  23. {
  24. ans[--n] = head->val;
  25. head = head->next;
  26. }
  27. return ans;
  28. }
  29. };

T16:链表 表示的 2个整数 相加(高位 在前 version)

给定两个 非空链表 l1和 l2 来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

可以假设除了数字 0 之外,这两个数字都不会以零开头。

解:

1.关键:

(1)从高位 开始 求和 是做不到的 ,所以 ,先反转 ,然后 回到 从低位开始 串行进位全加器

(2)串行进位 全加器

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
  14. //这一次 是 高位在 链表的 前面,低位在 链表的 后面
  15. //特殊情况:
  16. if(l1 == NULL)
  17. {
  18. return l2;
  19. }
  20. if(l2 == NULL)
  21. {
  22. return l1;
  23. }
  24. //思路一:可以考虑 反转链表之后 按照原来的 方法进行求解
  25. //因为 从高位 到 低位的 串行进位 全加器是 做不到的 ,所以 还是 反转吧
  26. l1=reverse_list(l1);
  27. l2=reverse_list(l2);
  28. //--按照反转之后的计算(串行进位 全加器)
  29. int a=0,b=0,c=0,sum=0;
  30. ListNode* head=new ListNode(0);
  31. ListNode* l3=head;
  32. while(l1!=NULL && l2!=NULL)
  33. {
  34. a=l1->val;
  35. b=l2->val;
  36. sum=a+b+c;
  37. c=sum/10;
  38. ListNode* node=new ListNode(sum%10);
  39. l3->next = node;
  40. l3=l3->next;
  41. l1=l1->next;
  42. l2=l2->next;
  43. }
  44. //收尾
  45. while(l1 !=NULL)
  46. {
  47. sum = l1->val +c;
  48. c=sum/10;
  49. ListNode* node=new ListNode(sum%10);
  50. l3->next = node;
  51. l3=l3->next;
  52. l1=l1->next;
  53. }
  54. while(l2 !=NULL)
  55. {
  56. sum = l2->val +c;
  57. c=sum/10;
  58. ListNode* node=new ListNode(sum%10);
  59. l3->next = node;
  60. l3=l3->next;
  61. l2=l2->next;
  62. }
  63. //--
  64. if(c!=0)
  65. {
  66. ListNode* node=new ListNode(c);
  67. l3->next = node;
  68. l3=l3->next;
  69. }
  70. head = reverse_list(head->next);
  71. return head;
  72. }
  73. //反转链表函数
  74. ListNode* reverse_list(ListNode* list)
  75. {
  76. //特殊 情况:
  77. if(list == NULL || list->next == NULL)
  78. {
  79. return list;
  80. }
  81. //--一般情况:
  82. ListNode* tmp=list->next;
  83. list->next =NULL;
  84. while(tmp!=NULL)
  85. {
  86. ListNode* u= tmp->next;
  87. tmp->next = list;
  88. list = tmp;
  89. tmp = u;
  90. }
  91. return list;
  92. //悟:按 “值返回一个指针” 等价于“返回这个 指针的指向(本质上一个地址而已)”
  93. }
  94. };

T17:判断 链表中 环的 入口节点

给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

解:

1.关键:

(1)这和 那个 判断“2个链表相交节点很像”, 都是 利用Set 存储第一次 “经过!”该节点,然后利用Set判断是否 有 第一次 重复经过

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode *detectCycle(ListNode *head) {
  12. //直接 利用 set进行判断 发生重复的 第一个节点就是入环的 节点
  13. unordered_set<ListNode*> Set;
  14. ListNode* tmp = head;
  15. //(1)遍历一次
  16. while(tmp!=NULL)
  17. {
  18. if(Set.count(tmp))
  19. {
  20. return tmp;
  21. }
  22. else
  23. {
  24. Set.insert(tmp);
  25. }
  26. tmp = tmp->next;
  27. }
  28. return NULL; //出来了,说明没有环
  29. }
  30. };

T18:链表随机节点 

给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。

实现 Solution 类:

  • Solution(ListNode head) 使用整数数组初始化对象。
  • int getRandom() 从链表中随机选择一个节点并返回该节点的值。链表中所有节点被选中的概率相等

解:

1.关键:

(1)由于 链表结构 不利于 随机访问 , 所以 使用 一个 数组 进行存储

(2)利用rand()%size 进行随机 读取

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. //借助一个 数据数组进行存储
  14. vector<int> vec;
  15. int size;
  16. Solution(ListNode* head) {
  17. //利用一个 链表中的数据 进行初始化 数据数组
  18. //随机访问 直接返回 数组中的元素即可
  19. while(head != NULL)
  20. {
  21. vec.push_back(head->val);
  22. head = head->next;
  23. }
  24. size = vec.size();
  25. }
  26. int getRandom() {
  27. //随机
  28. //srand((unsigned)time(NULL)); //种下随机数种子
  29. int index = rand()%size;
  30. return vec[rand()%size];
  31. }
  32. };
  33. /**
  34. * Your Solution object will be instantiated and called as such:
  35. * Solution* obj = new Solution(head);
  36. * int param_1 = obj->getRandom();
  37. */

T19:返回链表中 倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

解:

1.关键:

(1)利用 快慢指针 让2个指针之间 的 距离 是k-1个节点

(2)当fast到达 最后一个节点时, slow就是 倒数第k个节点

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * ListNode *next;
  6. * ListNode(int x) : val(x), next(NULL) {}
  7. * };
  8. */
  9. class Solution {
  10. public:
  11. ListNode* getKthFromEnd(ListNode* head, int k) {
  12. //特殊:
  13. if(head == NULL || head->next == NULL)
  14. {
  15. return head;
  16. }
  17. //利用快慢指针 返回 单数第k个节点
  18. ListNode* fast=head;
  19. ListNode* slow=head;
  20. for(int i=1;i<=k-1;i++)
  21. {
  22. fast=fast->next;
  23. }
  24. while(fast->next!=NULL)
  25. {
  26. fast = fast->next;
  27. slow = slow->next;
  28. }
  29. return slow;
  30. }
  31. };

T20:中序遍历 二叉搜索树 构建 双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

解:

1.关键:(这个思路 是我 经历了很多 失败后, 无奈的 选择,虽然比较笨,但还是比较通用)

(1) 利用递归 写一个中序遍历的 代码

(2)将 “访问该节点” 改为 “将该节点 加入到vec中”

(3)最后对 vec进行连接 即可

2.代码:

  1. class Solution {
  2. public:
  3. vector<Node*> vec;
  4. Node* treeToDoublyList(Node* root) {
  5. //特殊:
  6. if(root == NULL)
  7. {
  8. return root;
  9. }
  10. if(root->left ==NULL && root->right == NULL)
  11. {
  12. root->left =root;
  13. root->right =root;
  14. return root;
  15. }
  16. //干脆用一个全局vec进行存储,tmd
  17. travel(root);
  18. int size=vec.size();
  19. //(遍历进行连接
  20. vec[0]->left = vec[size-1];
  21. vec[0]->right =vec[1];
  22. vec[size-1]->right = vec[0];
  23. vec[size-1]->left =vec[size-2];
  24. //
  25. for(int i=1;i<=size-2;i++)
  26. {
  27. vec[i]->left = vec[i-1];
  28. vec[i]->right = vec[i+1];
  29. }
  30. return vec[0];
  31. }
  32. //利用 递归 来写 中序遍历
  33. void travel(Node* root) //需要 返回 遍历之后的最后一个节点
  34. {
  35. //1)递归出口
  36. if(root ==NULL)
  37. {
  38. return ;
  39. }
  40. //(2)递归
  41. travel(root->left);
  42. //此时的root就是 你想要的 次序
  43. vec.push_back(root);
  44. travel(root->right);
  45. }
  46. };

T21:二进制链表 转为 一个十进制整数

给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。

请你返回该链表所表示数字的 十进制值 。

解:

1.关键:

(1)一般,都是按照 从低位 到 高位 的次序进行处理的

(2)如果链表先存储高位 ,可以 反转 ,或者  改用一个vector进行存储 ,最后反向取出即可

(3)权值 : power*=2 进行更新

2.代码:

  1. class Solution {
  2. public:
  3. int getDecimalValue(ListNode* head) {
  4. //(1)可以反转 、 也可以先用一个vec存储
  5. vector<int> vec;
  6. while(head!=NULL)
  7. {
  8. vec.push_back(head->val);
  9. head= head->next;
  10. }
  11. //(2)权值和
  12. long long power=1;
  13. long long sum=0;
  14. int size = vec.size();
  15. for(int i=size-1;i >=0 ;i--)
  16. {
  17. sum+= (vec[i] * power);
  18. power *=2;
  19. }
  20. return sum;
  21. }
  22. };

T22: 一颗二叉树 的每一层 节点构成一个链表 返回它们组成的 vector

给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组

解:

1.关键:

(1)二叉树的 层序遍历:果断 bfs广搜,

(2)这里需要 2层 交替, 所以创新 使用2个队列queue 交替存储下一层 树节点的 同时, 将这一层的 所有节点 全部取出 链接为 一个 新的链表

(3)bfs函数中 : 主要负责 判断2个队列是否都为空的结束情况,并将每次 搜索的返回的链表入vec

(4)update函数中: 主要负责 从其中一个不为空的队列中取出所有节点 构建一条链表, 同时向2个方向搜索的节点 一次加入到 另一个队列中

2.代码:

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  8. * };
  9. */
  10. /**
  11. * Definition for singly-linked list.
  12. * struct ListNode {
  13. * int val;
  14. * ListNode *next;
  15. * ListNode(int x) : val(x), next(NULL) {}
  16. * };
  17. */
  18. class Solution {
  19. public:
  20. vector<ListNode*> listOfDepth(TreeNode* tree) {
  21. //采用 一种 不一样的 bfs广搜的 方式,每次都将 queue队列中的 元素全部取出
  22. return bfs(tree);
  23. }
  24. vector<ListNode*> bfs(TreeNode* root)
  25. {
  26. //考虑使用 2个队列交替 运作的 方式,每次都 只搜索一层
  27. queue<TreeNode*> Que1; //不需要map因为 不会重复
  28. queue<TreeNode*> Que2;
  29. Que1.push(root); //第一层 再Que1
  30. vector<ListNode*> ans;
  31. while((!Que1.empty()) || (!Que2.empty()))
  32. {
  33. //只有当 2 个队列都为空的 时候 才需要退出
  34. ans.push_back(update(Que1,Que2));
  35. }
  36. return ans;
  37. }
  38. ListNode* update(queue<TreeNode*>& Que1,queue<TreeNode*>& Que2) //2个搜索方向
  39. {
  40. //传进来的队列 一个为空, 另一个个不为空
  41. //忘记 新的元素加入队列了
  42. //case1:Que2为空
  43. if(!Que1.empty())
  44. {
  45. //好了,这个 队列中的元素 一次作为一条新链表的 元素
  46. //ListNode* head=new ListNode(Que1.front()->val);
  47. TreeNode* head =Que1.front();
  48. ListNode* head_=new ListNode(head->val);
  49. //两个节点不是同一种
  50. Que1.pop();
  51. ListNode* tmp=new ListNode(0);
  52. ListNode* real_head = head_;
  53. while(head_!=NULL)
  54. {
  55. //--先元素新的 子节点入队
  56. if(head->left!=NULL)
  57. {
  58. Que2.push(head->left);
  59. }
  60. if(head->right != NULL)
  61. {
  62. Que2.push(head->right);
  63. }
  64. tmp->next = head_;
  65. tmp = tmp->next;
  66. if(Que1.empty())
  67. {
  68. break;
  69. }
  70. head = Que1.front();
  71. head_ = new ListNode(head->val);
  72. Que1.pop();
  73. }
  74. return real_head;
  75. }
  76. else
  77. {
  78. TreeNode* head=Que2.front();
  79. ListNode* head_=new ListNode(head->val);
  80. Que2.pop();
  81. ListNode* tmp=new ListNode(0);
  82. ListNode* real_head = head_;
  83. while(head_!=NULL)
  84. {
  85. if(head->left!=NULL)
  86. {
  87. Que1.push(head->left);
  88. }
  89. if(head->right != NULL)
  90. {
  91. Que1.push(head->right);
  92. }
  93. tmp->next = head_;
  94. tmp = tmp->next;
  95. if(Que2.empty())
  96. {
  97. break;
  98. }
  99. head = Que2.front();
  100. head_ = new ListNode(head->val);
  101. Que2.pop();
  102. }
  103. return real_head;
  104. }
  105. }
  106. };

T23:反转 部分链表

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

解:

1.关键:

(0)先考虑好 那些  特殊情况,比如left == right,无需反转

(1)找到left节点 的前一个位置的节点作为head1,right的那个节点作为head2

(2)然后 利用 “基本操作”对 head1->next 到 head2之间的 节点进行反转,并且 和head1 和 head2->next进行连接

(3)最后需要分情况 返回 哪一个 才是反转之后的 头节点

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* reverseBetween(ListNode* head, int left, int right) {
  14. //反转第leftright之间的 节点
  15. //特殊情况: 如果left==1
  16. if(head == NULL || head->next==NULL || left==right)
  17. {
  18. return head;
  19. }
  20. //1)先获取到 left的前一个节点 和 right节点
  21. ListNode* head1 = new ListNode(0);
  22. ListNode* head2 = new ListNode(0);
  23. ListNode* start = head;
  24. int k=1;
  25. for(k=1; k <= left-2 ; k++)
  26. {
  27. start = start->next;
  28. }//现在start指向的 就是 left前一个节点
  29. if(left == 1)
  30. {
  31. start = head1;
  32. head1->next = head;
  33. }
  34. head1 =start; //将这个节点给到head1
  35. //cout<<"左节点"<<head1->val<<endl; //有问题,需要left前一个节点
  36. int diff = right - left;
  37. for(k=1;k<=diff+1 ; k++)
  38. {
  39. start = start->next;
  40. }
  41. head2 = start; //现在head2就是 指向right节点
  42. //cout<<"右节点"<<head2->val<<endl;
  43. //(2)然后开始 反转
  44. start = head1->next;
  45. ListNode* tmp = start->next;
  46. start->next = head2->next; //连上右侧
  47. head2->next = NULL;
  48. while(tmp!=NULL && start!=NULL)
  49. {
  50. ListNode* u =tmp->next;
  51. tmp->next = start;
  52. start= tmp;
  53. tmp= u;
  54. }
  55. //此时start指向尾节点
  56. head1->next = start;
  57. if(left != 1)
  58. {
  59. return head;
  60. }
  61. else
  62. {
  63. return start;
  64. }
  65. }
  66. };

T24:先 删除 部分 链表 ,然后 在对应位置连接 一个 新的 链表

给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。

请你将 list1 中下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点的位置。

解:

1.关键:

(1) 坑: 这个题中 下标 从0开始计数

(2)其实和T23一样, 重点是 得到 left的前一个节点 head1 , 和 right的那个节点 head2

(3)然后只要 改变对应指针的 指向即可

2.代码:

  1. /**
  2. * Definition for singly-linked list.
  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. */
  11. class Solution {
  12. public:
  13. ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
  14. //删除节点 + 插入节点
  15. //(1)先找到a前面的 那个节点 和 b的那个节点
  16. ListNode* head=new ListNode(0);
  17. head->next = list1;
  18. ListNode* tmp=head;
  19. ListNode* head1= tmp;
  20. a+=1;
  21. b+=1;
  22. for(int k=1; k<= a-1; k++)
  23. {
  24. tmp = tmp->next;
  25. }
  26. head1 = tmp ;//a前面的节点
  27. cout<<"left: "<<head1->val<<endl; //left找错了
  28. //tmd 的 下标从0开始用
  29. int diff = b-a;
  30. for(int k=1;k<= diff+1;k++)
  31. {
  32. tmp = tmp->next;
  33. }
  34. ListNode* head2= tmp; //b的那个节点
  35. cout<<"right: "<<head2->val<<endl;
  36. //(2)开始删除
  37. head1->next = list2;
  38. while(list2->next !=NULL)
  39. {
  40. list2 = list2->next; //找到list2的最后一个节点
  41. }
  42. list2->next = head2->next;
  43. //
  44. return head->next;
  45. }
  46. };

T25:删除 利用val区分不同节点的 链表中的给定节点

有一个单链表的 head,我们想删除它其中的一个节点 node

给你一个需要删除的节点 node 。你将 无法访问 第一个节点  head

链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。

删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:

  • 给定节点的值不应该存在于链表中。
  • 链表中的节点数应该减少 1。
  • node 前面的所有值顺序相同。
  • node 后面的所有值顺序相同。
  • 解:

解:

1.关键:

(1)利用不同 val进行区分 , 暗示 可以修改 节点的val值

(2)一句话:“先把自己变成儿子,然后干掉儿子”

2.代码:

  1. class Solution {
  2. public:
  3. void deleteNode(ListNode* node) {
  4. //妙啊:这题 可以改变节点的 数值,因为每个节点的数值都不同,所以利用val区分
  5. //一句话:“先把自己变成儿子,然后干掉儿子”
  6. //之所以不是 末尾节点,就是保证有儿子
  7. node->val = node->next->val;
  8. node->next=node->next->next;
  9. }
  10. };

T26:删除 链表的 “中间点” 

给你一个链表的头节点 head 。删除 链表的 中间节点 ,并返回修改后的链表的头节点 head 。

长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点(下标从 0 开始),其中 ⌊x⌋ 表示小于或等于 x 的最大整数。

对于 n = 1234 和 5 的情况,中间节点的下标分别是 0112 和 2 。

解:

1.关键:

(1)这里的 中间点的 “下标”是从0开始计算的 ,但是 n又是从1开始算的

(2)如果fast的终止条件是 fast!=NULL && fast->next!=NULL 那么

奇数个点: slow指向的正好是“真中间点”  偶数个点: slow指向 “向下取整”的中间点

(3)所以终止条件改为 fast!=NULL && fast->next!=NULL && fast->next->next!=NULL

得到的slow才是 前一个节点(偶数点时)

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* deleteMiddle(ListNode* head) {
  4. //利用 快慢指针 找到“向下取整的 ”中间节点 或者是 “真中间点”
  5. //特殊
  6. if(head == NULL || head->next == NULL)
  7. {
  8. return NULL;
  9. }
  10. //--
  11. ListNode* fast = head->next;
  12. ListNode* slow =head;
  13. while(fast!=NULL && fast->next!=NULL && fast->next->next!=NULL)
  14. {
  15. fast = fast->next->next;
  16. slow = slow->next;
  17. }
  18. slow->next = slow->next->next;
  19. return head;
  20. }
  21. };

T27:最大孪生和

在一个大小为 n 且 n 为 偶数 的链表中,对于 0 <= i <= (n / 2) - 1 的 i ,第 i 个节点(下标从 0 开始)的孪生节点为第 (n-1-i) 个节点 。

  • 比方说,n = 4 那么节点 0 是节点 3 的孪生节点,节点 1 是节点 2 的孪生节点。这是长度为 n = 4 的链表中所有的孪生节点。

孪生和 定义为一个节点和它孪生节点两者值之和。

给你一个长度为偶数的链表的头节点 head ,请你返回链表的 最大孪生和 。

解:

1.关键:

(1)其实 主要考察 反转 或者 改用 vector 存储 利于 随机访问

2.代码:

  1. class Solution {
  2. public:
  3. int pairSum(ListNode* head) {
  4. //最大孪生和 , 可以反转 ,也可以 先存到vec中
  5. vector<int> vec;
  6. while(head!=NULL)
  7. {
  8. vec.push_back(head->val);
  9. head = head->next;
  10. }
  11. int size= vec.size();
  12. //--打擂台
  13. int half = size/2;
  14. int max=-1;
  15. for(int i=0;i<=half-1;i++)
  16. {
  17. int sum = vec[i] + vec[size-1-i];
  18. if(sum > max)
  19. {
  20. max = sum;
  21. }
  22. }
  23. return max;
  24. }
  25. };

T28:给一个 从高位开始 表示的 链表整数 +1

给定一个用链表表示的非负整数, 然后将这个整数 再加上 1 。

这些数字的存储是这样的:最高位有效的数字位于链表的首位 head 。

解:

1.关键:

(1) 等价于 只有个位 的一个进位c=1

(2)然而,无论如何 都只能 从低位开始向高位 串行进位

(3)所以,翻转+串行进位+翻转 

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* plusOne(ListNode* head) {
  4. //2个链表 表示的 整数的 求和思路一样 , 都是串行进位求和
  5. //(1)翻转
  6. head = reverse(head);
  7. //show(head);
  8. ListNode* new_head =head;
  9. //(2)串行进位求和
  10. int c=1;
  11. ListNode* before_head=head;
  12. while(head!=NULL)
  13. {
  14. int sum = c + head->val;
  15. c=sum/10;
  16. head->val = sum%10;
  17. before_head = head;
  18. head = head->next;
  19. }
  20. //收尾 -- 很可惜 ,这是的head是一个NULL指针
  21. if(c!=0)
  22. {
  23. ListNode* node = new ListNode(c);
  24. before_head->next = node;
  25. }
  26. head = reverse(new_head);
  27. return head;
  28. }
  29. //翻转函数
  30. ListNode* reverse(ListNode* head)
  31. {
  32. ListNode* start = head; //start指向翻转后的 头节点
  33. if(head!=NULL && head->next != NULL)
  34. {
  35. ListNode* tmp =start->next;
  36. start->next =NULL;
  37. while(tmp!=NULL && start!=NULL)
  38. {
  39. ListNode* u = tmp->next;
  40. tmp->next = start;
  41. start= tmp;
  42. tmp= u;
  43. }
  44. }
  45. return start;
  46. }
  47. //测试 函数
  48. void show(ListNode* head)
  49. {
  50. ListNode* tmp = head;
  51. while(tmp!=NULL)
  52. {
  53. cout<<tmp->val <<" ";
  54. tmp = tmp->next;
  55. }
  56. cout<<endl;
  57. }
  58. };

T29:交换单链表中 正数 第k个节点 和 倒数 第k个节点

给你链表的头节点 head 和一个整数 k 。

交换 链表正数第 k 个节点和倒数第 k 个节点的值后,返回链表的头节点(链表 从 1 开始索引)。

解:

1.关键:

(1)从head移动k-1次得到 正数第k个节点 

(2)利用 相隔k个节点的 fast slow快慢指针 找到 倒数 第k个节点 , 当fast==NULL时终止

(3)值交换即可

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* swapNodes(ListNode* head, int k) {
  4. //交换 链表 正数 和 倒数的 第k个 节点的!值!,好吧,“值交换”
  5. //还不如 分别 取到 第k个节点 和 快慢指针得到的 倒数 第k个节点
  6. ListNode* node1 = head;
  7. for(int i=1;i<=k-1;i++)
  8. {
  9. node1 = node1->next;
  10. }//此时 node1指向的 就是 正数 第k个节点
  11. //(2)
  12. ListNode* fast =head;
  13. ListNode* slow =head; //fast==NULL时,如果2者相隔k个节点
  14. for(int i=1;i<=k;i++)
  15. {
  16. fast = fast->next;
  17. }
  18. while(fast!=NULL)
  19. {
  20. fast = fast->next;
  21. slow = slow->next;
  22. }
  23. //交换
  24. swap(slow->val , node1->val);
  25. return head;
  26. }
  27. };

T30:限定insert排序,对链表进行 insert排序

给定单个链表的头 head ,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。

插入排序 算法的步骤:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。

对链表进行插入排序。

解:

1.关键:

(1)设置 一个 tail节点 每次 都指向 已经排序好的 最后一个节点

(2)设置一个 u节点 ,指向 下一个 需要排序的节点

(3)内层 循环 判断 node1 的val是否在 xx和xx之间,或者放到 末尾 并且 更新tail指针

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* insertionSortList(ListNode* head) {
  4. //训练 插入排序的熟练程度
  5. //特殊:
  6. if(head == NULL || head->next ==NULL)
  7. {
  8. return head;
  9. }
  10. //我需要 一个 tail 节点 -- 已经排序完成的 最后一个节点
  11. //只有当 node1 放到 tail的后面时 才需要 更新tail
  12. ListNode* tail =head;
  13. //--一般:
  14. ListNode* node1 = head->next;
  15. while(node1 !=NULL)
  16. {
  17. //特殊:
  18. ListNode* u = node1->next; //作为 最后 node1的更新节点
  19. tail->next = u ;//将u连接到 tail后面
  20. if(node1->val < head->val)
  21. {
  22. node1->next = head;
  23. head = node1;
  24. node1 = u;
  25. continue;
  26. }
  27. ListNode* tmp = head;
  28. //内层循环
  29. int flag = 0;
  30. while(tmp!= tail) //终点为 tail节点
  31. {
  32. if( node1->val >= tmp->val && node1->val <=tmp->next->val)
  33. {
  34. //可以放到它两之间了
  35. node1->next = tmp->next;
  36. tmp->next = node1;
  37. node1 = u;
  38. flag =1;
  39. break;
  40. }
  41. else
  42. {
  43. tmp = tmp->next;
  44. }
  45. }
  46. if(flag == 1)
  47. {
  48. continue;
  49. }
  50. else{
  51. //需要更新tial节点,此时 tmp指向 tail
  52. tmp->next = node1;
  53. tail = node1;
  54. node1 = u;
  55. }
  56. node1 = u;
  57. }
  58. return head;
  59. }
  60. };

T31: 逆序打印 节点不可变链表

给您一个不可变的链表,使用下列接口逆序打印每个节点的值:

  • ImmutableListNode: 描述不可变链表的接口,链表的头节点已给出。

您需要使用以下函数来访问此链表(您 不能 直接访问 ImmutableListNode):

  • ImmutableListNode.printValue():打印当前节点的值。
  • ImmutableListNode.getNext():返回下一个节点。

输入只用来内部初始化链表。您不可以通过修改链表解决问题。也就是说,您只能通过上述 API 来操作链表。

解:

1.关键:

(1)其实 , 逆序 本质就是 “后进先出”的栈 结构

(2)这里有很多方法,比如先用vec存储再逆序,或者stack存储

(3)这里我 采用 递归 进行逆序输出(递归的 本质就是 stack 栈结构)

2.代码:

  1. class Solution {
  2. public:
  3. void printLinkedListInReverse(ImmutableListNode* head) {
  4. //递归打印,相当于 一个栈结构
  5. //1)出口
  6. if(head == NULL)
  7. {
  8. return ;
  9. }
  10. //递归
  11. printLinkedListInReverse(head->getNext());
  12. head->printValue();
  13. }
  14. };

T32:判断 二叉树 中是否存在 一条 链表

给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。

如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。

一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。

解:

1.关键:

(1)利用 递归的 写法travel(这里是 先序 ,中序和后序都可以)一遍二叉树中的节点,判断是否可以作为 链表的 第一个节点

(2)如果可以 ,就对 这个节点进行 dfs 深搜 , 观察是否 可以在 最深层返回 true, 或者返回false

2.代码

  1. class Solution {
  2. public:
  3. bool Flag = false;
  4. bool isSubPath(ListNode* head, TreeNode* root) {
  5. //二叉树 中是否存在 一条链表值(和搜索 相关)
  6. //先序 遍历的 同时 ,把 “访问 ” 换成 “dfs搜索即可”
  7. travel(head,root);
  8. return Flag;
  9. }
  10. void travel(ListNode* head,TreeNode* root)
  11. {
  12. //1)出口
  13. if(root == NULL)
  14. {
  15. return ;
  16. }
  17. //(dfs)--只有 root->val == head->val才需要
  18. if(root->val == head->val)
  19. {
  20. if(dfs(root,head))
  21. {
  22. Flag = true;
  23. return ;
  24. }
  25. }
  26. //--递归
  27. travel(head,root->left);
  28. travel(head,root->right);
  29. }
  30. bool dfs(TreeNode* root ,ListNode* head)
  31. {
  32. bool flag=true;
  33. //不要更改head
  34. ListNode* tmp = head;
  35. //(1)出口:
  36. if(head == NULL)
  37. {
  38. //true
  39. return true;
  40. }
  41. else if(head!=NULL && root==NULL)
  42. {
  43. return false;
  44. }
  45. //case1: root->val 和 head->val 不匹配
  46. if(root->val != head->val)
  47. {
  48. //无需再搜索了
  49. flag = false;
  50. }
  51. else
  52. {
  53. //继续 递归
  54. if(dfs(root->left,tmp->next))
  55. {
  56. return true;
  57. }
  58. else if(dfs(root->right,tmp->next))
  59. {
  60. return true;
  61. }
  62. else
  63. {
  64. return false;
  65. }
  66. }
  67. return flag;
  68. }
  69. };

T33:二叉树 展开为 单链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同

解:

1.关键:

(1)递归形式的 二叉树 先序遍历, 将“访问” 改为 “链接” 即可

(2) 

//核心:任何一个节点 只有在经过之后 left和right指针才会被修改,所以

        //只要在 第一个经过的 时候 留下 原来的 left 和 right指针即可

2.代码:

  1. class Solution {
  2. public:
  3. TreeNode* pc;
  4. void flatten(TreeNode* root) {
  5. //法一:利用O(n)空间,将先序遍历中的访问 改为连接即可
  6. pc=new TreeNode(0);
  7. //TreeNode* new_head = pc;
  8. traval(root);
  9. //他不是看变量root , 而是看 “那一块内存空间”
  10. }
  11. void traval(TreeNode* root)
  12. {
  13. //(1)递归出口
  14. if(root == NULL)
  15. {
  16. return ;
  17. }
  18. //--先序 连接
  19. //核心:任何一个节点 只有在经过之后 leftright指针才会被修改,所以
  20. //只要在 第一个经过的 时候 留下 原来的 leftright指针即可
  21. TreeNode* left_node = root->left;
  22. TreeNode* right_node =root->right;
  23. pc->right = root;
  24. pc->left = NULL;
  25. pc = pc->right;
  26. //记得将root->right 和 root->left保留下来
  27. traval(left_node);
  28. traval(right_node);
  29. }
  30. };

T34:k个节点一组 翻转 一条链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

解:

1.关键:

(1)朴素就好了, 基础就是 那个题(一条链表的 部分节点的 翻转)

(2)第一次特殊处理,得到新的 new_head头节点作为返回值

(3)利用“死循环” 对后续的 节点进行 “相同的处理”, 重点是2中退出的case,case1是pc->next == NULL,说明上一次 已经是 k|n的最后一组了,可以直接返回new_head

case2:如果head2到达了一个NULL,说明 不够了,可以直接返回new_head

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* reverseKGroup(ListNode* head, int k) {
  4. //特殊:
  5. if(head == NULL || head->next == NULL || k==1)
  6. {
  7. return head;
  8. }
  9. //k个一组 进行 链表节点的 翻转
  10. //没有更好的策略,只能依次翻转
  11. //设置一个头节点
  12. ListNode* pc=new ListNode(0);
  13. pc->next = head;
  14. //1.第一次翻转,找到left的前一个节点 和 right节点
  15. //先以 前k个节点为例
  16. ListNode* head1=pc; //指向left前一个
  17. ListNode* head2=head; //指向right
  18. for(int i=1;i<=k-1;i++)
  19. {
  20. if(head2 == NULL)
  21. {
  22. return head; //原来节点的数量数少于k个
  23. }
  24. head2 = head2->next;
  25. }
  26. if(head2 == NULL)
  27. {
  28. return head;
  29. }
  30. //现在 head2 就是right位置的 节点
  31. //(1)先 让head1->next保留下来 然后更新为 head2-
  32. ListNode* start = head1->next;
  33. ListNode* static_start=start;
  34. head1->next = head2;
  35. //--翻转 , 最后 让这个 翻转后的 最后start->next 更新为head2->next
  36. ListNode* tmp=start->next;
  37. start->next = head2->next;
  38. head2->next = NULL ; //记得置空!--用于tmp判断翻转的终点
  39. while(tmp!=NULL && start!=NULL)
  40. {
  41. //cout<<"start:"<<start->val<<endl;
  42. ListNode* u=tmp->next;
  43. tmp->next = start;
  44. start = tmp;
  45. tmp = u;
  46. }
  47. //完成了 依次翻转, 头节点为head1->next
  48. //几个重要的 指针:
  49. ListNode* new_head = head1->next; //可以返回的 “真”头节点
  50. //下一次开始的left节点的 前一个节点
  51. pc = static_start;//开始写之后的 循环
  52. //设计好 循环的 终点 --利用2中情况的break作为循环的退出
  53. //show(new_head); //结果显示 第一次交换没问题
  54. //cout<< "pc:"<<pc->val<<endl; //好吧,pc指向错误
  55. while(1)
  56. {
  57. //pc一定不是 NULL,但是 pc->next如果是NULL,即为case1 ,k|n,正好分组
  58. if(pc->next == NULL)
  59. {
  60. break; //正好分组
  61. }
  62. //找到head1 和 head2,如果没有找到head2,就是case2,退出循环
  63. head1 = pc;
  64. head2 = pc->next;
  65. for(int i=1;i<=k-1;i++)
  66. {
  67. if(head2 == NULL)
  68. {
  69. return new_head;
  70. }
  71. head2 = head2->next;
  72. }
  73. if(head2 == NULL)
  74. {
  75. return new_head;
  76. }
  77. //找到了 head1 和 head2
  78. ListNode* start = head1->next;
  79. ListNode* static_start=start;
  80. //pc->next!=NULL,所以start一定不是NULL
  81. head1->next = head2;
  82. ListNode* tmp=start->next;
  83. start->next = head2->next; //所以它的意思是head2可能为NULL
  84. head2->next = NULL ; //记得置空!--用于tmp判断翻转的终点
  85. while(tmp!=NULL && start!=NULL)
  86. {
  87. ListNode* u=tmp->next;
  88. tmp->next = start;
  89. start = tmp;
  90. tmp = u;
  91. }
  92. pc = static_start;
  93. }
  94. return new_head;
  95. }
  96. //这个错误 是 第1个节点 一直和后面的节点在交换
  97. //--辅助 测试函数
  98. void show(ListNode* head)
  99. {
  100. ListNode* tmp =head;
  101. while(tmp!=NULL)
  102. {
  103. cout<<tmp->val<<" ";
  104. tmp = tmp->next;
  105. }
  106. cout<<endl;
  107. }
  108. };

T35:按照 一定严格递减规则 删除 节点

给你一个链表的头节点 head 。

对于列表中的每个节点 node ,如果其右侧存在一个具有 严格更大 值的节点,则移除 node 。

返回修改后链表的头节点 head 

解:

1.关键:

(1)思路: 翻转 + 快慢指针遍历,打擂台删除节点 + 翻转

(2)学会转化,因为 前面看不到 后面有啥, 不如先翻转,逆向思维进行考虑

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* removeNodes(ListNode* head) {
  4. //特殊:
  5. if(head == NULL || head->next== NULL)
  6. {
  7. return head;
  8. }
  9. //先翻转, 然后设置一个max打擂台,如果 后续节点有比max严格小的节点
  10. //就利用快慢(相隔为1)指针删除该节点
  11. //最后再翻转 依次,并且返回 翻转后的头节点
  12. //(1)第一次翻转
  13. head = reverse(head);
  14. //(2)遍历依次 打擂台 + 快慢指针删除节点
  15. ListNode* fast = head->next;
  16. ListNode* slow = head;
  17. int max = slow->val;
  18. while(fast!=NULL)
  19. {
  20. if(fast->val < max)
  21. {
  22. //删除fast节点
  23. fast=fast->next;
  24. slow->next = slow->next->next;
  25. continue;
  26. }
  27. else
  28. {
  29. //是否需要更新max
  30. if(fast->val > max)
  31. {
  32. max = fast->val;
  33. }
  34. fast = fast->next;
  35. slow = slow->next;
  36. }
  37. }//此时fast已经指向NULL
  38. //(3)再次翻转
  39. head = reverse(head);
  40. return head;
  41. }
  42. //--辅助翻转函数 , 返回翻转之后的 头节点
  43. ListNode* reverse(ListNode* head)
  44. {
  45. //特殊:
  46. if(head ==NULL || head->next == NULL)
  47. {
  48. return head;
  49. }
  50. //--一般
  51. ListNode* start = head;
  52. ListNode* tmp = start->next;
  53. start->next =NULL;
  54. while(tmp!=NULL && start!=NULL)
  55. {
  56. ListNode* u = tmp->next;
  57. tmp->next = start;
  58. start = tmp;
  59. tmp = u;
  60. }
  61. return start;
  62. }
  63. };

T36:对按照 绝对值升序 排序的 链表 进行按照 值排序

给你一个链表的头结点 head ,这个链表是根据结点的绝对值进行升序排序, 返回重新根据节点的值进行升序排序的链表。

解:

1.关键:

(1)为了 方便 访问, 先改用 一个 vec1存储 所有的 数值

(2)“按照绝对值 排序”有一些 特点和规律, 本质 就是 把 负数 和 非负数 分开来观察:

可见,负数其实 后面的 更小, 非负数 前面的更小

(3)分成2次 遍历vec1 分别 取出 负数 和 非负数 ,然后 按照 从小到大的 次序存储到 vec2中,最后遍历依次链表 并且修改链表的 数值即可

2.代码

  1. class Solution {
  2. public:
  3. ListNode* sortLinkedList(ListNode* head) {
  4. //特殊:
  5. if(head == NULL || head->next ==NULL)
  6. {
  7. return head;
  8. }
  9. //1)先遍历依次 将数值存储到vec1
  10. //2)然后 从后 向前 只取负数 存储到vec2
  11. //(3)然后 从前 往后 遍历vec1 取出>=0 的数字 存储到vec2
  12. //(4)最后 按照 vec2中的数值 更新链表的 数值
  13. vector<int> vec1;
  14. //(1)
  15. ListNode* tmp = head;
  16. while(tmp !=NULL)
  17. {
  18. vec1.push_back(tmp->val);
  19. tmp=tmp->next;
  20. }
  21. //(2)
  22. int size= vec1.size();
  23. vector<int> vec2;
  24. for(int i=size-1;i>=0;i--)
  25. {
  26. if(vec1[i] < 0)
  27. {
  28. vec2.push_back(vec1[i]);
  29. }
  30. }
  31. //(3)
  32. for(int i=0 ;i <=size-1;i++)
  33. {
  34. if(vec1[i] >= 0)
  35. {
  36. vec2.push_back(vec1[i]);
  37. }
  38. }
  39. //(4)
  40. tmp = head;
  41. for(int i=0;i<=size-1;i++)
  42. {
  43. tmp->val = vec2[i];
  44. tmp= tmp->next;
  45. }
  46. return head;
  47. }
  48. };

T37:删除 排序链表 中的 重复 节点

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

解:

1.关键:

(1)利用 相隔 为1的 快慢指针进行判断

(2)如果fast->val == slow->val ,就删除这个fast节点

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* deleteDuplicates(ListNode* head) {
  4. //利用 相隔为1的 快慢指针进行删除
  5. //特殊:
  6. if(head == NULL || head->next == NULL)
  7. {
  8. return head;
  9. }
  10. //--一般:
  11. ListNode* fast = head->next;
  12. ListNode* slow = head;
  13. while(fast!=NULL)
  14. {
  15. if(fast->val == slow->val)
  16. {
  17. //删除fast节点
  18. fast = fast->next;
  19. slow->next = slow->next->next;
  20. continue;
  21. }
  22. else
  23. {
  24. fast = fast->next;
  25. slow = slow->next;
  26. }
  27. }
  28. return head;
  29. }
  30. };

T38:多项式 链表求和

多项式链表是一种特殊形式的链表,每个节点表示多项式的一项。

每个节点有三个属性:

  • coefficient:该项的系数。项 9x4 的系数是 9 。
  • power:该项的指数。项 9x4 的指数是 4 。
  • next:指向下一个节点的指针(引用),如果当前节点为链表的最后一个节点则为 null 

解:

1.关键:(这是 一道上课 ppt的题目)

(1)构建一个新的 指针 p3用来 指向已经链接好的 最后一个节点

(2) p1 和 p2 指针 分别指向 list1 和 list2 遍历的 最新的 那个节点

(3)遍历终止条件 就是 p1==NULL or p2 == NULL ,

(4)每次 都是 比较2者的 系数 然后 分情况讨论

2.代码:

  1. class Solution {
  2. public:
  3. PolyNode* addPoly(PolyNode* poly1, PolyNode* poly2) {
  4. //上课的最后一题
  5. //它是用的 原地 串联法
  6. //分情况:case1,p1->power 更大 ,case2:power相等, case3:p2的power更大
  7. //其中case2中 还要分情况考虑 coef之和为0与否
  8. PolyNode* p1=poly1;
  9. PolyNode* p2=poly2;
  10. PolyNode* p3=new PolyNode(0,0);
  11. PolyNode* new_head=p3;
  12. while(p1!=NULL && p2!=NULL)
  13. {
  14. //(1)比较power
  15. if(p1->power > p2->power)
  16. {
  17. p3->next = p1;
  18. p1= p1->next;
  19. p3=p3->next; //p3始终指向已经链接好的 最后一个节点
  20. }
  21. else if(p1->power < p2->power)
  22. {
  23. p3->next = p2;
  24. p2 = p2->next;
  25. p3 = p3->next;
  26. }
  27. else{
  28. //power相等
  29. int coef_sum = p1->coefficient + p2->coefficient;
  30. //case1:
  31. if(coef_sum != 0)
  32. {
  33. p1->coefficient = coef_sum;
  34. p3->next = p1;
  35. p1 = p1->next;
  36. p3 = p3->next;
  37. p2 = p2->next;
  38. }
  39. else{
  40. //2个节点都不要了
  41. p2 = p2->next;
  42. p1 = p1->next;
  43. }
  44. }
  45. }
  46. //收尾:
  47. if(p1 != NULL)
  48. {
  49. p3->next = p1;
  50. }
  51. else if(p2 !=NULL)
  52. {
  53. p3->next = p2;
  54. }
  55. else{
  56. p3->next = NULL;
  57. }
  58. return new_head->next;
  59. }
  60. };

T39:"两两交换"一条链表中 相邻 节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

解:

1.关键:

(1)一种 链表结构 的 重要意识,设置好一个 永远指向 已经交换好的 部分的 最后一个节点指针pc

(2)然后就是以head == NULL or head->next == NULL 为结束标志的 遍历,注意next指针修改的 次序

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* swapPairs(ListNode* head) {
  4. //特殊:
  5. if(head == NULL || head->next == NULL)
  6. {
  7. return head;
  8. }
  9. //交换 相邻的 节点
  10. //(1)设置一个 交换好的部分的 最后一个节点的 指针pc
  11. ListNode* pc = new ListNode(0);
  12. ListNode* new_head = pc;
  13. ListNode* tmp = head; //tmp 永远记录下一次开始的 第一个节点指针
  14. //(2)
  15. //cout<<"来过吗"<<endl;
  16. while(head!=NULL && head->next!=NULL)
  17. {
  18. tmp = head->next->next; //保留 head->next->next
  19. pc->next = head->next; //pc先链接到head->next
  20. pc = pc->next; //pc到达next
  21. pc->next = head; //pc再链接到head
  22. pc = pc->next; //pc到next
  23. head = tmp; //新的开始点给到head
  24. }
  25. if(head == NULL)
  26. {
  27. pc->next = NULL;
  28. }
  29. else
  30. {
  31. pc->next = head;
  32. head->next = NULL;
  33. }
  34. return new_head->next;
  35. }
  36. //--辅助函数
  37. void show(ListNode* head)
  38. {
  39. ListNode* tmp = head;
  40. while( tmp !=NULL)
  41. {
  42. cout<<tmp->val<<" ";
  43. tmp = tmp->next;
  44. }
  45. cout<<endl;
  46. }
  47. };

T40:只保留 链表中 没有 重复的 数字

给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。

解:

1.关键:

(1)借鉴之前的 删除重复节点的思想 , 这里采用创新的 “快 中 慢 ”三个指针的思想进行遍历删除

(2)slow--指向 已经处理好的 部分的 最后一个节点

    mid--指向 下一次需要 比较的 第一个节点

    fast--指向mid之后的 节点 ,它的 值需要和mid节点的 值进行比价

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* deleteDuplicates(ListNode* head) {
  4. //特殊:
  5. if(head == NULL || head->next == NULL)
  6. {
  7. return head;
  8. }
  9. //这一次 需要删除所有 只要出现重复的 数字
  10. //我建议 使用 fast mid slow 三指针"快中满"
  11. //slow -- 永远指向 已经处理好的部分的 最后一个节点
  12. //mid -- 指向 下一个部分的 第一个节点 即slow->next
  13. //fast -- 一路高歌
  14. ListNode* pc = new ListNode(0);
  15. ListNode* slow = pc;
  16. pc->next = head;
  17. ListNode* mid = pc->next;
  18. ListNode* fast = mid->next;
  19. //遍历
  20. while(fast!=NULL)
  21. {
  22. //存下这一次的 mid的val
  23. int compare_val = mid->val;
  24. if(fast->val != compare_val)
  25. {
  26. //不相等:
  27. slow = mid;
  28. mid = fast;
  29. fast =fast->next;
  30. continue;
  31. }
  32. while(fast!=NULL && fast->val == compare_val)
  33. {
  34. fast = fast->next;
  35. }//出来的时候,fast要么为一个新的val,要么就是NULL
  36. slow->next = fast;
  37. mid = fast;
  38. if(fast == NULL)
  39. {
  40. break;
  41. }
  42. fast = mid->next;
  43. }
  44. return pc->next;
  45. }
  46. };

T41:链表中 下一个 更大的 节点

给定一个长度为 n 的链表 head

对于列表中的每个节点,查找下一个 更大节点 的值。也就是说,对于每个节点,找到它旁边的第一个节点的值,这个节点的值 严格大于 它的值。

返回一个整数数组 answer ,其中 answer[i] 是第 i 个节点( 从1开始 )的下一个更大的节点的值。如果第 i 个节点没有下一个更大的节点,设置 answer[i] = 0 。

解:

法一: 朴素 暴力 搜索法

1.关键:模拟题目要求 , 对于当前的 这个节点 head 依次向后去找 比它大 的节点

2.代码:

  1. class Solution {
  2. public:
  3. vector<int> nextLargerNodes(ListNode* head) {
  4. //特殊 :
  5. if(head->next == NULL )
  6. {
  7. return {0};
  8. }
  9. //法一: 最朴素的 方法 , 利用 双层 循环进行 暴力搜索
  10. ListNode* tmp = head;
  11. vector<int> ans;
  12. while(head->next!=NULL)
  13. {
  14. tmp = head->next;
  15. int flag = 0;
  16. int num_head = head->val;
  17. while(tmp!=NULL)
  18. {
  19. int num_tmp = tmp->val;
  20. if(num_tmp > num_head)
  21. {
  22. ans.push_back(num_tmp);
  23. flag = 1;
  24. break;
  25. }
  26. tmp= tmp->next;
  27. }
  28. if(flag == 0)
  29. {
  30. ans.push_back(0);
  31. }
  32. head = head->next;
  33. }
  34. ans.push_back(0);
  35. return ans;
  36. }
  37. };

法二:借鉴 讨论区的 思路 -- “单调栈!”(有点 贪婪思想的味道)

1.关键

(1) 遍历一次 ,将链表中的元素 存储到 vec中,方便访问

(2)从后向前遍历 , 利用一个stack栈作为 工具,分情况讨论

case1: 如果 stack为空, 说明ans中该位置只能填0

case2:依次检索 栈顶top元素,如果比tmp_val大,就说明是“它”,填入ans,同时把tmp_val入栈

case3:如果"不比" tmp_val大,  记得出栈

2.代码:

  1. class Solution {
  2. public:
  3. vector<int> nextLargerNodes(ListNode* head) {
  4. //借助贪婪算法的 思想 设计一个 “单调栈结构”
  5. stack<int> Stack;
  6. //(1)先 遍历一次 , 然后 从后 往前 进行 搜素
  7. vector<int> vec;
  8. while(head!=NULL)
  9. {
  10. vec.push_back( head->val);
  11. head = head->next;
  12. }
  13. int size = vec.size();
  14. vector<int> ans(size,0);
  15. //(2)然后 从后 往前进行搜索
  16. //分析:先从 栈顶开始遍历,如果 找比当前位置更大的 值, ans中对应位置填它
  17. //如果“<=”当前位置的 值, 就出栈
  18. //如果栈为空, 对应ans中 当前位置 填0 ,然后 将这个val入栈,继续遍历下一个位置
  19. for(int i =size-1 ;i >=0 ;i--)
  20. {
  21. int tmp_val = vec[i];
  22. int flag = 0; //flag == 0,代表栈 已空
  23. while(!Stack.empty())
  24. {
  25. //栈非空,一次检查栈顶
  26. int top = Stack.top();
  27. if(top > tmp_val)
  28. {
  29. ans[i] = top;
  30. //nice , 记得入栈
  31. Stack.push(tmp_val);
  32. flag= 1;
  33. break;
  34. }
  35. else
  36. {
  37. //出栈
  38. Stack.pop();
  39. }
  40. }
  41. if(flag == 0)
  42. {
  43. //入栈,并且 ans中对应位置填0
  44. Stack.push(tmp_val);
  45. ans[i] = 0;
  46. }
  47. }
  48. return ans;
  49. }
  50. };

T42:找出 极值点 之间的 最小 和 最大距离

链表中的 临界点 定义为一个 局部极大值点  局部极小值点 。

如果当前节点的值 严格大于 前一个节点和后一个节点,那么这个节点就是一个  局部极大值点 。

如果当前节点的值 严格小于 前一个节点和后一个节点,那么这个节点就是一个  局部极小值点 。

注意:节点只有在同时存在前一个节点和后一个节点的情况下,才能成为一个 局部极大值点 / 极小值点 。

给你一个链表 head ,返回一个长度为 2 的数组 [minDistance, maxDistance] ,其中 minDistance 是任意两个不同临界点之间的最小距离,maxDistance 是任意两个不同临界点之间的最大距离。如果临界点少于两个,则返回 [-1,-1] 。

解:(比较朴素的 方法 ,以后 有更好的 想法 再加以补充)

1.关键:

(1)转存到 vec中

(2)然后 遍历一次 ,找到 那些 符合 极值点的 下标 存到ans数组中

(3)最后遍历一次ans数组 找到 最小距离min

2.代码:

  1. class Solution {
  2. public:
  3. vector<int> nodesBetweenCriticalPoints(ListNode* head) {
  4. //特殊:
  5. if(head == NULL || head->next ==NULL)
  6. {
  7. return {-1,-1};
  8. }
  9. //返回2个临界节点之间的 最小距离 和 最大距离
  10. //(1)先转存到vec中
  11. vector<int> vec;
  12. while(head!=NULL)
  13. {
  14. vec.push_back(head->val);
  15. head = head->next;
  16. }
  17. //(2)遍历一次,如果是临界点,记录将下标 丢到一个ans的数组中
  18. vector<int> ans;
  19. int size= vec.size();
  20. for(int i=1;i<=size-2;i++)
  21. {
  22. if((vec[i] > vec[i-1] && vec[i] > vec[i+1])||(vec[i] < vec[i-1] && vec[i] < vec[i+1]))
  23. {
  24. //是第i个点 是临界点
  25. ans.push_back(i);
  26. }
  27. }
  28. //(3)检查ans数组:
  29. int size2= ans.size();
  30. if(size2 < 2 )
  31. {
  32. return {-1,-1};
  33. }
  34. //max容易找但是min似乎需要遍历
  35. int max = ans[size2-1] - ans[0];
  36. int min=max;
  37. //打擂台
  38. for(int i=1;i<=size2-1;i++)
  39. {
  40. if(ans[i] - ans[i-1] < min)
  41. {
  42. min = ans[i] - ans[ i-1];
  43. }
  44. }
  45. return {min,max};
  46. }
  47. };

T43:对一条 链表进行分组 ,然后 翻转其中偶数节点的 组

给你一个链表的头节点 head 。

链表中的节点 按顺序 划分成若干 非空 组,这些非空组的长度构成一个自然数序列(1, 2, 3, 4, ...)。一个组的 长度 就是组中分配到的节点数目。换句话说:

  • 节点 1 分配给第一组
  • 节点 2 和 3 分配给第二组
  • 节点 45 和 6 分配给第三组,以此类推

注意,最后一组的长度可能小于或者等于 1 + 倒数第二组的长度 。

反转 每个 偶数 长度组中的节点,并返回修改后链表的头节点 head 

解:(借助vec数组 进行 偷懒!!哎,没办法 ,现在的我 还是 太菜了)

1.关键:

(1)将链表 转存到 vec中

(2)这个题目 考察的 是数学中的 “高斯求和公式 ”n*(n+1)/2的一些处理

(3)对 那些 分组为 偶数的 部分 找到 起点 和 终点 后 进行 对撞指针的 翻转 操作

(4)最后 将 vec中的数组 再 存储回到 链表中即可

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* reverseEvenLengthGroups(ListNode* head) {
  4. //特殊 :
  5. if(head == NULL || head->next == NULL)
  6. {
  7. return head;
  8. }
  9. //虽然可以 直接按照 翻转链表的 思想进行处理,
  10. //但是 需要结合长度的 判断, 感觉比较麻烦 -- 直接 考虑转存到 vec中
  11. //最后 再根据vec中的数值 构造一条 新的链 / 或者 修改原链表的val
  12. //(1)转存
  13. vector<int> vec;
  14. ListNode* tmp = head;
  15. while(tmp!=NULL)
  16. {
  17. vec.push_back(tmp->val);
  18. tmp = tmp->next;
  19. }
  20. //(2) 根据题目要求 对 vec进行 变换
  21. //分组 然后 翻转 偶数节点数的部分 -- 直接利用对撞指针 就可以 修改vec数组
  22. int size= vec.size();
  23. //其实 就是 高斯求和n* (n+1)/2 ,n=(sqrt(8*k+1)-1)/2向下取整,然后就是剩余的最后一组的 元素的 个数
  24. int n = (sqrt(8*size+1)-1)/2;
  25. int remain = size - (n*n + n)/2;
  26. //每一个偶数部分的 起点 和 终点 都可以 利用数学公式进行计算
  27. //最后 对 remain进行判断即可
  28. //起点 (i-1)*i/2 终点 i*(i+1)/2-1
  29. for(int i=2;i<=n;i+=2)
  30. {
  31. int start = (i-1)*i/2;
  32. int end = i*(i+1)/2-1;
  33. //翻转 从startend 下标 之间的 数值
  34. while(end > start)
  35. {
  36. swap(vec[start],vec[end]);
  37. start++;
  38. end--;
  39. }
  40. }
  41. //这个for循环中 有问题 , 应该是 起点 和 终点 找错了
  42. if(remain % 2 ==0)
  43. {
  44. int end = size-1;
  45. int start = size-remain;
  46. while(end > start)
  47. {
  48. swap(vec[start],vec[end]);
  49. start++;
  50. end--;
  51. }
  52. }
  53. //修改原来链表中的所有数值val
  54. tmp = head;
  55. for(int i=0;i<size;i++)
  56. {
  57. tmp->val = vec[i];
  58. tmp = tmp->next;
  59. }
  60. return head;
  61. }
  62. };

T44: 删除 重复 出现的节点(无需连续)

编写代码,移除未排序链表中的重复节点。保留最开始出现的节点。

解:

1.关键:

(1)Set容器 + 快慢指针即可

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* removeDuplicateNodes(ListNode* head) {
  4. //tmd看错题了,原来 不需要连续,只要重复出现立刻删除这个节点
  5. //利用set + 快慢指针即可
  6. //特殊:
  7. if(head == NULL|| head->next ==NULL)
  8. {
  9. return head;
  10. }
  11. //
  12. unordered_set<int> Set;
  13. //(1)遍历 + 快慢指针
  14. ListNode* fast = head->next;
  15. ListNode* slow = head;
  16. Set.insert(slow->val);
  17. while(fast!=NULL)
  18. {
  19. //可能需要删除fast这个节点
  20. if(Set.count(fast->val))
  21. {
  22. slow->next = fast->next;
  23. fast = fast->next; // 删除 节点 fast
  24. }
  25. else
  26. {
  27. Set.insert(fast->val);
  28. slow = slow->next;
  29. fast = fast->next;
  30. }
  31. }
  32. return head;
  33. }
  34. };

T45.利用 一个链表的数值 对一个矩阵 进行 螺旋填充

给你两个整数:m 和 n ,表示矩阵的维数。

另给你一个整数链表的头节点 head 。

请你生成一个大小为 m x n 的螺旋矩阵,矩阵包含链表中的所有整数。链表中的整数从矩阵 左上角 开始、顺时针 按 螺旋 顺序填充。如果还存在剩余的空格,则用 -1 填充。

返回生成的矩阵。

解:

1.关键:

(1)设置好 up down left right 4个边界 ,并且在循环中 不断更新,注意 x的方向(反直觉),其中down从1开始!

(2) 

//内层:;以 下标 是否到达边界位置为准<=

        //外层: 以head访问的 位置是否 为NULL为标准

(3)分4个方向按照一定的顺序进行 填充,特别,一个方向完成后,记得 更新边界值 + 跳到下一次开始的位置

2.代码:

  1. class Solution {
  2. public:
  3. vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
  4. //用一个 链表的数值 对 一个 矩阵 进行螺旋填充
  5. //总的来说就是 利用 一个 路径的 设计 然后 遍历一个 链表
  6. //我觉得可以 设计up down left right 4个 二维数组访问边界,然后
  7. int up=m-1 , down = 1 , left = 0 ,right =n-1;//这些边界 都是 从0开始的下标边界
  8. //每次按照一定的 方向对 二维数组进行填充
  9. //填充方向:右 - - -
  10. //(1)利用-1对二维数组进行初始化
  11. vector<vector<int>> vec(m,vector<int>(n,-1)); //m行 n 列
  12. //利用2层 while循环:
  13. //内层:;以 下标 是否到达边界位置为准<=
  14. //外层: 以head访问的 位置是否 为NULL为标准
  15. int x=0,y=0; //x行 y 列
  16. while( head!=NULL)
  17. {
  18. //记得 每次需要 更新4个边界的 数值
  19. //(1) y++ ,while循环 , 向右填充 , 右边界right
  20. while(head!=NULL && y<=right)
  21. {
  22. int val = head->val;
  23. head = head->next;
  24. vec[x][y] = val;
  25. y++;
  26. }
  27. x++;
  28. y--; //防止越界
  29. right--;//更新边界
  30. //(2)x++ ,while循环 ,向高的行数填充, 边界up
  31. while(head!=NULL && x<=up)
  32. {
  33. int val = head->val;
  34. head = head->next;
  35. vec[x][y] =val;
  36. x++;
  37. }
  38. y--;
  39. x--;
  40. up--; //更新 up行数边界
  41. //(3)y--,while循环, 向左 填充,边界left
  42. while(head!=NULL && y>=left)
  43. {
  44. int val = head->val;
  45. head = head->next;
  46. vec[x][y] = val;
  47. y--;
  48. }
  49. x--;
  50. y++;
  51. left++; //更新 左边界
  52. //(4)x--, while循环 ,向 低 行数 填充, 边界 down
  53. while(head!=NULL && x>=down)
  54. {
  55. int val = head->val;
  56. head = head->next;
  57. vec[x][y] = val;
  58. x--;
  59. }
  60. y++;
  61. x++;
  62. down++;
  63. }
  64. return vec;
  65. }
  66. };

T46: 将 二叉树 通过中序遍历 得到 一个链表

二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。

返回转换后的单向链表的头节点。

1.关键:

(1)还是 调用 递归形式的 中序遍历

(2)将 “访问” 修改为: !逻辑: 当代码执行到 这一步时 , 说明 root节点的 左节点 已经访问过了,但是右节点还没有 访问过 ,先将 root 链接到 全局链表上, 然后 再去访问root的right节点

2.代码

  1. class Solution {
  2. public:
  3. TreeNode* f;
  4. TreeNode* convertBiNode(TreeNode* root) {
  5. f= new TreeNode(0);
  6. TreeNode* pre= f;
  7. dfs(root);
  8. return pre->right;
  9. }
  10. void dfs(TreeNode* root)
  11. {
  12. if(root == NULL)
  13. return ;
  14. dfs(root->left);
  15. f->right = root;
  16. root->left = NULL;
  17. f = f->right;
  18. dfs(root->right);
  19. }
  20. };

T47:合并val为0之间的节点, 并且保证合并之后的链表中没有val为0的节点

给你一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val == 0 。

对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有任何 0 。

 返回修改后链表的头节点 head 。

解:

1.关键:

(1)利用 “快慢指针” 加以实现

(2)slow指针 , 每次指向 “开始0”的 前一个节点 ,然后 再 让fast指针向前探索

2.代码:

  1. class Solution {
  2. public:
  3. ListNode* mergeNodes(ListNode* head) {
  4. //保证 开头 和 结尾的2个节点的val都为0
  5. //将 val=0 之间的节点的数值求和,然后 作为一个节点
  6. //利用 “快慢指针”:slow慢指针 指向“开始0”的前一个节点,fast快指针不断向前
  7. ListNode* new_head = new ListNode(0);
  8. new_head->next = head;
  9. ListNode* slow = new_head;
  10. ListNode* fast = head;
  11. while(slow!=NULL && slow->next!=NULL) //先以 slow!=NULL作为结束条件
  12. {
  13. //(1)当slow->next->val == 0的时候 ,就可以开始合并了
  14. if(slow->next->val == 0) // !!注意,slow->next??可能为NULL
  15. {
  16. fast = slow->next->next;
  17. //--开始移动fast--不对,应该要fast 最后的位置处于0
  18. int sum= 0;
  19. while(fast!=NULL && fast->val!=0)
  20. {
  21. sum += fast->val;
  22. fast = fast->next;
  23. }
  24. if(sum == 0) //无论fast == NULL or fast->val == 0都可以
  25. {
  26. //
  27. slow->next = fast; //跳过中间的一些值
  28. }
  29. else //无论 fast==NULL 或者fast->val == 0都可以
  30. {
  31. //创建一个新的节点连接到slow 和 fast之间
  32. ListNode* new_node= new ListNode(sum);
  33. slow->next = new_node;
  34. new_node->next = fast;
  35. }
  36. }
  37. //这里只要移动slow指针即可
  38. slow = slow->next;
  39. }
  40. return new_head->next;
  41. }
  42. };

T48: 判断一个 给定的 字符串string是否为 一个小数或者一个整数(似乎和 链表没关系)

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

  1. 若干空格
  2. 一个 小数 或者 整数
  3. (可选)一个 'e' 或 'E' ,后面跟着一个 整数
  4. 若干空格

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-'
  2. 下述格式之一:
    1. 至少一位数字,后面跟着一个点 '.'
    2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
    3. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-'
  2. 至少一位数字

部分数值列举如下:

  • ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]

部分非数值列举如下:

  • ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4

解:

1.关键:

(1)这个题目 就是 按照 题干的要求不断判断当前检测的 字符是否符合要求

(2) 怎么说呢? 反正就是 有序的 一些 if判断 ,只要有一点不满足就直接返回false ,同时 注意结合 辅助函数的 调用 (尤其是 那个 判断substr是否全部为 空格 的函数)

2.代码:

  1. class Solution {
  2. public:
  3. bool isNumber(string s) {
  4. //看来是我误会了, 如果是空格不能 两边都有东西
  5. //看来空格两边必须有一个全部为空格
  6. //判断一个 字符串 是否 表示一个 正数 或小数, 返回true or false;
  7. //从s[0] 一致判断到 s[size-1]多个if 中只要 1false直接false
  8. int size= s.size();
  9. //特殊:
  10. if(size == 1)
  11. {
  12. if(is_num(s[0]))
  13. {
  14. return true;
  15. }
  16. else{
  17. return false;
  18. }
  19. }
  20. int num_flag=0; // ==1代表已经出现过数字
  21. int dot_flag = 0; //dot_flag=1 代表小数点已经出现一次了
  22. for(int i=0 ;i<=size-1 ;i++)
  23. {
  24. //(-1)其它字符,字节false
  25. if(is_else_char(s[i]))
  26. {
  27. return false;
  28. }
  29. //(0)如果遇到的 是一个符号
  30. if(s[i]=='+' || s[i] == '-')
  31. {
  32. if(num_flag ==1)
  33. {
  34. return false;
  35. }
  36. else
  37. {
  38. continue;
  39. }
  40. }
  41. //1)如果 遇到 空格,直接continue
  42. if(s[i] == ' ')
  43. {
  44. if((!is_all_blank(s.substr(0,i))) && (!is_all_blank(s.substr(i+1,size-i-1))))
  45. {
  46. return false;
  47. }
  48. else{
  49. continue;
  50. }
  51. }
  52. //(2)如果是e 后面必须接一个 整数
  53. if(s[i]=='e' || s[i]=='E')
  54. {
  55. if(i==size-1 || is_all_blank(s.substr(i+1,size-i-1)))
  56. {
  57. return false;
  58. }
  59. if(num_flag == 0)
  60. {
  61. return false;
  62. }
  63. cout<<is_integer(s.substr(i+1,size-i-1))<<endl;
  64. cout<<"果然是这里"<<endl;
  65. //说明 is_integer这个 函数 返回值有问题,
  66. //此时i==3
  67. return is_integer(s.substr(i+1,size-i-1));
  68. }
  69. //(3)如果是 num okcontinue
  70. if(is_num(s[i]))
  71. {
  72. num_flag =1;
  73. continue;
  74. }
  75. //(4)如果是 小数点
  76. if(s[i] == '.')
  77. {
  78. if(dot_flag == 1)
  79. {
  80. return false;
  81. }
  82. //1.前面是数字
  83. if(i>0 && is_num(s[i-1]))
  84. {
  85. dot_flag = 1;
  86. continue;
  87. }
  88. //2.后面是数字
  89. if(i!=size-1 && is_num(s[i+1]))
  90. {
  91. dot_flag = 1;
  92. continue;
  93. }
  94. //3.后面是e和一个整数 ok
  95. if(i>0 && is_num(s[i-1]) && i<=size-3 && (s[i+1]=='e' || s[i+1]=='E') && is_integer(s.substr(i+2,size-i-2)))
  96. {
  97. return true;
  98. }
  99. //cout<<"是这里返回的false吗?"<<endl; --不是
  100. return false;
  101. }
  102. }
  103. if(num_flag == 0)
  104. {
  105. return false; //没出现数字,不行
  106. }
  107. return true;
  108. }
  109. //辅助函数
  110. //判断 是出现了 规定之外的字符
  111. bool is_else_char(char ch)
  112. {
  113. if(ch == 'e' || ch=='E')
  114. {
  115. return false;
  116. }
  117. if(ch <= '9' && ch>='0')
  118. {
  119. return false;
  120. }
  121. if(ch == '+'|| ch == '-')
  122. {
  123. return false;
  124. }
  125. if(ch == ' '|| ch=='.')
  126. {
  127. return false;
  128. }
  129. return true;
  130. }
  131. //判断一个字符串是否为整数
  132. bool is_integer(string s)
  133. {
  134. int size = s.size();
  135. if(size == 0)
  136. {
  137. return false;
  138. }
  139. //忘记考虑一种情况了,就是如果 遇到了 “末尾空格” 就可以直接返回true
  140. //s.substr(start_index,len)返回一个string类型变量
  141. if(s[0]=='+' || s[0]=='-')
  142. {
  143. if(size == 1)
  144. {
  145. return false;
  146. }
  147. for(int i=1;i<=size-1;i++)
  148. {
  149. if(s[i]==' ')
  150. {
  151. return is_all_blank(s.substr(i,size-i));
  152. }
  153. if(! is_num(s[i]))
  154. {
  155. return false;
  156. }
  157. }
  158. return true;
  159. }
  160. for(int i =0;i<=size-1;i++)
  161. {
  162. if(s[i]==' ')
  163. {
  164. return is_all_blank(s.substr(i,size-i));
  165. }
  166. if(!is_num( s[i]))
  167. {
  168. return false;
  169. }
  170. }
  171. return true;
  172. }
  173. //设计一个 判断 一个字符串全部为 空格的 辅助函数
  174. bool is_all_blank(string s)
  175. {
  176. int size = s.size();
  177. for(int i=0;i<=size-1;i++)
  178. {
  179. if( s[i]!=' ')
  180. {
  181. return false;
  182. }
  183. }
  184. return true;
  185. }
  186. bool is_num(char ch)
  187. {
  188. if(ch <= '9' && ch>='0')
  189. {
  190. return true;
  191. }
  192. else
  193. {
  194. return false;
  195. }
  196. }
  197. };

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/523693
推荐阅读
相关标签
  

闽ICP备14008679号