当前位置:   article > 正文

链表刷题(4~8)

链表刷题(4~8)

目录

反转链表

 返回中间节点

 倒数k个节点

 链表分割

 判断回文


反转链表

单链表刷题时我们遇到过一个反转链表,那时我们采用的是头插的方式达到反转的效果,那能不能把指针反过来呢?答案是可以的。

  

这里用三个指针是为了记录后面节点的数据,不然就导致后面的地址缺失,毕竟逻辑结构可不同于物理结构。可以看到结束条件是n2遍历到空就停止。

基于前面刷题的反转链表,现在我们除了头插外有一种新的办法:

力扣

  1. struct ListNode* reverseList(struct ListNode* head)
  2. {
  3. if(head == NULL)//防止空指针解引用
  4. {
  5. return NULL;
  6. }
  7. struct ListNode* n1,*n2,*n3;
  8. n1 = NULL;
  9. n2 = head;
  10. n3 = n2->next;
  11. while(n2)//结束条件
  12. {
  13. n2->next = n1;
  14. n1 = n2;
  15. n2 = n3;
  16. if(n3)//n3为空时保持原样
  17. {
  18. n3 = n3->next;
  19. }
  20. }
  21. return n1;
  22. }

 返回中间节点

力扣

查找链表的中间节点有一个思想就是快慢指针slow指针每走一步,fast指针就走两步。这样做只遍历了一遍链表就找到了中间节点。

  1. struct ListNode* middleNode(struct ListNode* head)
  2. {
  3. struct ListNode* fast=head,*slow = head;
  4. while(fast && fast->next)
  5. {
  6. fast = fast ->next->next;
  7. //fast更新后判断,返回第一个中间结点:if(fast!=NULL)
  8. slow = slow->next;
  9. }
  10. return slow;//快慢指针
  11. }

 倒数k个节点

链表中倒数最后k个结点_牛客题霸_牛客网

这道题与前一道题类似,不同的是不知道k为多长,为此,我们可以设置快慢指针让fast先走k步或k-1步,然后两个指针共同移动

需要注意不足k步时返回空链表

  1. truct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
  2. struct ListNode* slow,*fast;
  3. slow = fast = pHead;
  4. while(k--)//k步
  5. {
  6. if(fast == NULL)//不足k步
  7. {
  8. return NULL;
  9. }
  10. fast = fast->next;
  11. }
  12. while(fast)//走k步结束条件
  13. {
  14. slow = slow->next;
  15. fast = fast->next;
  16. }
  17. return slow;
  18. }

 链表分割

 这个题目算是比较难的题,它要求分割后的相对顺序不能改变。这里我们建立两个链表

一个记录比x小的数据,一个记录相等和比它大的数据,依次进行尾插。最后将两个链表相连。新的链表我们都创建一个哨兵节点(为了避免初始判空和其中一个链表为空的情况进行处理。)

  

这个图我画的比较乱,但思路却很明确。创建哨兵节点,比较,插入,置空,连结,释放,一气呵成。

这里不支持用C实现,我们就先用C++实现。这里的差别在于结构体定义可以不写struct。

  1. class Partition {
  2. public:
  3. ListNode* partition(ListNode* pHead, int x) {
  4. ListNode *greaterhead,*greatertail;
  5. greaterhead=greatertail=(ListNode *)malloc(sizeof(ListNode));
  6. ListNode *smallerhead, *smallertail;
  7. smallertail=smallerhead=(ListNode *)malloc(sizeof(ListNode));
  8. while(pHead==NULL)
  9. {
  10. return NULL;
  11. }
  12. ListNode* cur = pHead;
  13. while(cur)
  14. { if(cur->val<x)
  15. {
  16. smallertail->next = cur;
  17. smallertail = smallertail->next;
  18. }
  19. if(cur->val>=x)
  20. {
  21. greatertail->next = cur;
  22. greatertail = greatertail->next;
  23. }
  24. cur = cur->next;
  25. }
  26. smallertail->next = greaterhead->next;//连结
  27. greatertail->next = NULL;//置空
  28. ListNode*Next = smallerhead->next;//返回
  29. free(smallerhead);
  30. free(greaterhead);
  31. return Next;
  32. }
  33. };

这里有个小误区就是物理结构与逻辑结构的区别,从图看起来尾插的数据后面没有别的内容,实际上它的next指针还是指向原链表的下一个节点的,虽然是两个新链表和成的一个新链表,但实际上只有哨兵是新开辟的,只是对原链表进行操作,所以要对较大指针的最后一个节点置空。 

判断回文

 正常思路是复制一份链表,然后再此基础上进行反转。但这样的空间复杂度就变成了O(N)。这里介绍一种新奇的方法:根据回文的对称性,反转链表中间及以后的节点。

  

上面两种情况,比较结束条件为一方为空停止比较。

恰好我们实现了查找中间节点和反转链表,所以这里我们可以直接拷贝一份代码。

  1. struct ListNode* reverseList(struct ListNode* head) {
  2. if (head == NULL) { //防止空指针解引用
  3. return NULL;
  4. }
  5. struct ListNode* n1, *n2, *n3;
  6. n1 = NULL;
  7. n2 = head;
  8. n3 = n2->next;
  9. while (n2) { //结束条件
  10. n2->next = n1;
  11. n1 = n2;
  12. n2 = n3;
  13. if (n3) { //n3为空时保持原样
  14. n3 = n3->next;
  15. }
  16. }
  17. return n1;
  18. }
  19. struct ListNode* middleNode(struct ListNode* head) {
  20. struct ListNode* fast = head, *slow = head;
  21. while (fast && fast->next) {
  22. fast = fast ->next->next;
  23. //fast更新后判断,返回第一个中间结点:if(fast!=NULL)
  24. slow = slow->next;
  25. }
  26. return slow;//快慢指针
  27. }
  28. class PalindromeList {
  29. public:
  30. bool chkPalindrome(ListNode* A) {
  31. struct ListNode* mid = middleNode(A);
  32. struct ListNode* rhead = reverseList(mid);
  33. while(A && rhead)//一方为空停止
  34. {
  35. if( A->val != rhead->val)
  36. {
  37. return false;
  38. }
  39. A = A->next;
  40. rhead = rhead->next;
  41. }
  42. return true;
  43. }
  44. };
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/659419
推荐阅读
相关标签
  

闽ICP备14008679号