当前位置:   article > 正文

算法思想总结:链表

算法思想总结:链表

一、链表的常见技巧总结

二、两数相加

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
  4. //利用t来存进位信息
  5. int t=0;
  6. ListNode*newhead=new ListNode(0);//创建一个哨兵节点,方便尾插
  7. ListNode*ptail=newhead;//ptail方便尾插
  8. ListNode* cur1=l1,*cur2=l2;
  9. while(cur1||cur2||t==1)//t==1防止后面有进位没加上
  10. {
  11. if(cur1) {t+=cur1->val; cur1=cur1->next;}
  12. if(cur2) {t+=cur2->val;cur2=cur2->next;}
  13. ptail->next=new ListNode(t%10);
  14. ptail=ptail->next;
  15. t/=10;
  16. }
  17. ListNode*ret=newhead->next;
  18. delete newhead;
  19. return ret;
  20. }
  21. };

 三、两两交换链表中的节点

 四、重排链表

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. void reorderList(ListNode* head)
  4. {
  5. //方法1,利用一个数据结构将每个节点存起来,通过下标去访问
  6. //方法2, (1)利用快慢指针,找中点 (2) 拆开链表 从中点开始往后翻转 (3)进行合并成新链表
  7. if(head==nullptr||head->next==nullptr||head->next->next==nullptr) return;
  8. ListNode*mid=midnode(head);//找到中间节点
  9. //断开链表
  10. ListNode*l1=head;
  11. ListNode*l2=mid->next;
  12. mid->next=nullptr;
  13. //然后反转2
  14. l2=reverseList(l2);
  15. //合并链表
  16. mergeList(l1,l2);
  17. }
  18. ListNode*midnode(ListNode* head)
  19. {
  20. ListNode*fast=head;
  21. ListNode*slow=head;
  22. while(fast->next!=nullptr&&fast->next->next!=nullptr)//确保后面两步能走
  23. {
  24. fast=fast->next->next;
  25. slow=slow->next;
  26. }
  27. return slow;//此时慢指针指向的就是最小的节点
  28. }
  29. ListNode* reverseList(ListNode* head)
  30. {
  31. ListNode*p1=nullptr;
  32. ListNode*p2=head;
  33. ListNode*p3=head->next;//记录下一个要遍历的点
  34. while(p2)
  35. {
  36. p2->next=p1;
  37. p1=p2;
  38. p2=p3;
  39. if(p3) p3=p3->next ;
  40. }
  41. return p1;
  42. }
  43. void mergeList(ListNode* l1, ListNode* l2)
  44. {
  45. ListNode* temp1,*temp2;
  46. while(l1!=nullptr&&l2!=nullptr)
  47. {
  48. temp1=l1->next;
  49. temp2=l2->next;
  50. l1->next=l2;
  51. l1=temp1;//回到原链表0
  52. l2->next=l1;
  53. l2=temp2;//回到原链表
  54. }
  55. }
  56. };

五、合并k个升序链表

. - 力扣(LeetCode)

 优先级队列

  1. class Solution {
  2. public:
  3. //建小堆需要greater
  4. struct greater //构造一个仿函数
  5. {
  6. bool operator()(const ListNode*l1,const ListNode*l2)
  7. {
  8. return l1->val>l2->val;
  9. }
  10. };
  11. ListNode* mergeKLists(vector<ListNode*>& lists)
  12. {
  13. //建立优先级队列(小堆),每次将堆顶元素插入进去,然后再删除堆顶元素,插入下个位置
  14. priority_queue<ListNode*,vector<ListNode*>,greater> heap;//建立一个小堆
  15. //入堆
  16. for(auto l:lists) if(l) heap.push(l);//因为有可能里面存的是一个空链表
  17. //开始合并k个有序链表
  18. ListNode*newnode=new ListNode(0);
  19. ListNode*ptail=newnode;//用于帮助我们进行尾插
  20. while(!heap.empty())
  21. {
  22. //进行尾插
  23. ListNode*it=heap.top();
  24. ptail->next=it;
  25. ptail=it;//去到下一个位置准备尾插
  26. //删除堆顶元素并将该节点的下一个节点入堆 ,为空就不入
  27. heap.pop();
  28. if(it->next) heap.push(it->next);
  29. }
  30. //此时全部的元素都插入完成了,返回最终的链表
  31. ListNode*ret=newnode->next;
  32. delete newnode;
  33. return ret;
  34. //时间复杂度o(n*k*logk)
  35. }
  36. };

分治思想:

  1. //策略,利用递归解决问题,结合归并排序,合并两个有序链表 (利用分治思想)
  2. class Solution {
  3. public:
  4. ListNode* mergeKLists(vector<ListNode*>& lists)
  5. {
  6. int n=lists.size();
  7. return merge(lists,0,n-1);//让merge帮助我们完成整个区间的归并
  8. }
  9. ListNode* merge(vector<ListNode*>& lists,int left,int right)
  10. {
  11. //首先,处理边界情况,如果不存在链表或者是只有一个链表,此时没有必要进行下去
  12. if(left>right) return nullptr;
  13. if(left==right) return lists[left];
  14. //让merge帮助我们归并左右区间
  15. int mid=(left+right)>>1;
  16. ListNode*l1=merge(lists,left,mid);
  17. ListNode*l2=merge(lists,mid+1,right);
  18. //然后开始进行合并两个有序链表
  19. return mergetwolist(l1,l2);
  20. }
  21. ListNode*mergetwolist(ListNode*l1,ListNode*l2)
  22. {
  23. //考虑两个链表为空的情况
  24. if(l1==nullptr) return l2;
  25. if(l2==nullptr) return l1;
  26. //此时两个链表必然不为空,开始进行合并
  27. ListNode*newhead=new ListNode(0);//哨兵节点
  28. ListNode*ptail=newhead;//帮助我们进行尾插
  29. ListNode*cur1=l1,*cur2=l2;//两个指针分别指向两个链表
  30. while(cur1&&cur2)//当两个都不为空的时候
  31. {
  32. if(cur1->val<cur2->val)
  33. {
  34. //此时要尾插cur1
  35. ptail->next=cur1;
  36. ptail=cur1;//更新到下一个位置
  37. cur1=cur1->next;//继续去下一个节点遍历
  38. }
  39. else
  40. {
  41. ptail->next=cur2;
  42. ptail=cur2;//更新到下一个位置
  43. cur2=cur2->next;//继续去下一个节点遍历
  44. }
  45. }
  46. //可能有的链表没有遍历完
  47. if(cur1) ptail->next=cur1;
  48. if(cur2) ptail->next=cur2;
  49. //此时返回到目标的位置
  50. ListNode*ret=newhead->next;
  51. delete newhead;
  52. return ret;
  53. }
  54. };

六、k个一组翻转链表

. - 力扣(LeetCode)

  1. class Solution {
  2. public:
  3. ListNode* reverseKGroup(ListNode* head, int k)
  4. {
  5. int n=0;//记录总数
  6. ListNode*cur=head;
  7. while(cur)//统计节点个数,并推测有多少组
  8. {
  9. cur=cur->next;
  10. ++n;
  11. }
  12. n/=k;//看看一共需要几组
  13. ListNode*newhead=new ListNode(0);//创建一个哨兵节点
  14. ListNode*prev=newhead;//记住被头插的点
  15. cur=head;//从head开始进行头插
  16. //翻转n组,每组翻转k个
  17. for(int i=0;i<n;++i)
  18. {
  19. ListNode*temp=cur;
  20. for(int j=0;j<k;++j)
  21. {
  22. //用头插的逻辑
  23. ListNode*next=cur->next;;
  24. cur->next=prev->next;
  25. prev->next=cur;
  26. cur=next;//继续去链表的下一个点
  27. }
  28. prev=temp;//更新prev
  29. }
  30. //循环结束后,将后面的不需要逆序的部分接上
  31. prev->next=cur;
  32. ListNode*ret=newhead->next;
  33. delete newhead;
  34. return ret;
  35. }
  36. };

七、旋转链表

. - 力扣(LeetCode)

思路1:截断再连接

  1. class Solution {
  2. public:
  3. ListNode* rotateRight(ListNode* head, int k)
  4. {
  5. //让链表成环(闭合成环),然后在指定位置断开
  6. if(head==nullptr||head->next==nullptr||k==0) return head;
  7. int count=1;//数节点数量
  8. ListNode*ptail=head;
  9. while(ptail->next!=nullptr) //找到尾节点,并统计节点数
  10. {
  11. ptail=ptail->next;
  12. ++count;
  13. }
  14. int add=count-k%count;//看看具体是翻转几次
  15. if(add==count) return head;//避免不需要翻转的情况
  16. //截断重连
  17. ListNode*cur=head;
  18. while(--add) cur=cur->next; //找到被截断的位置
  19. ListNode*ret=cur->next;
  20. cur->next=nullptr;//断开
  21. cur=ret;
  22. while(cur->next!=nullptr) cur=cur->next;//找到尾节点
  23. cur->next=head;//连接
  24. return ret;
  25. }
  26. };

思路2:链表成环,指定位置截断

  1. class Solution {
  2. public:
  3. ListNode* rotateRight(ListNode* head, int k)
  4. {
  5. //让链表成环,然后在指定位置断开
  6. if(head==nullptr||head->next==nullptr||k==0) return head;
  7. int count=1;//数节点数量
  8. ListNode*ptail=head;
  9. while(ptail->next!=nullptr) //找到尾节点,并统计节点数
  10. {
  11. ptail=ptail->next;
  12. ++count;
  13. }
  14. int add=count-k%count;//看看具体是翻转几次
  15. ptail->next=head;//头尾相连
  16. while(add--) ptail=ptail->next;
  17. ListNode*ret=ptail->next;
  18. ptail->next=nullptr;
  19. return ret;
  20. }
  21. };

思路3:逆置前n-k个,再逆置后k个,最后整体逆置

  1. class Solution {
  2. public:
  3. ListNode* rotateRight(ListNode* head, int k)
  4. {
  5. if(head==nullptr||head->next==nullptr||k==0) return head;
  6. //先逆置前n-k个,再逆置后k个,再整体逆置
  7. int count=1;//数节点数量
  8. ListNode*ptail=head;
  9. while(ptail->next!=nullptr) //找到尾节点,并统计节点数
  10. {
  11. ptail=ptail->next;
  12. ++count;
  13. }
  14. int add=count-k%count;//看看具体是翻转几次
  15. if(add==count) return head;
  16. //开始找前n-k个节点
  17. ListNode*cur=head;
  18. while(--add) cur=cur->next;
  19. ListNode*l2=cur->next;//第二个链表
  20. cur->next=nullptr;//断开
  21. ListNode* l1=reverse(head);
  22. l2=reverse(l2);
  23. head->next=ptail;//连接起来
  24. return reverse(l1);//然后整体翻转
  25. }
  26. ListNode*reverse(ListNode* head)
  27. { //只有一个节点,没什么好逆置的
  28. if(head==nullptr||head->next==nullptr) return head;
  29. ListNode*p1=nullptr,*p2=head,*p3=head->next;
  30. while(p2)
  31. {
  32. p2->next=p1;
  33. p1=p2;
  34. p2=p3;
  35. if(p3) p3=p3->next;
  36. }
  37. return p1;
  38. }
  39. };

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

闽ICP备14008679号