当前位置:   article > 正文

LeetCode链表OJ题

LeetCode链表OJ题

目录

206. 反转链表 - 力扣(LeetCode)

​编辑

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

​编辑

链表分割_牛客题霸_牛客网 (nowcoder.com)

 《27. 移除元素 - 力扣(LeetCode)》

160. 相交链表 - 力扣(LeetCode)

88. 合并两个有序数组 - 力扣(LeetCode)

876. 链表的中间结点 - 力扣(LeetCode)

链表的回文结构__牛客网 (nowcoder.com)

141. 环形链表 - 力扣(LeetCode)

 142. 环形链表 II - 力扣(LeetCode)


第一种方法:

代码:

  1. struct ListNode* reverseList(struct ListNode* head){
  2. if(head==NULL)
  3. {
  4. return NULL;
  5. }
  6. struct ListNode* tail=NULL;
  7. struct ListNode* src=head;
  8. struct ListNode* dst=src->next;
  9. while(src)
  10. {
  11. src->next=tail;
  12. tail=src;
  13. src=dst;
  14. if(dst)
  15. dst=dst->next;
  16. }
  17. return tail;
  18. }

思路:

 首先把tail放到NULL的位置,然后把src的next指向tail,然后把tail给给src,把src给给dst

然后再重复上一个步骤就可以实现第二个节点指向第一个节点 

然后重复即可。 

报错:

 修改:假如只有两个节点:

 那dst=dst->next要把dst跳到哪里呢?

所以我们要判断一下dst是否为空:

 报错:

 解析:这个就是head为空的问题,head如果为空,我们怎么连下一个节点呢,,没必要连了,所以判断一下,如果head为空,那么就什么都不返回。

第二种方法:头插法

思路是:创建一个cur,cur在head的位置上(cur=head),然后再创建一个next变量,这个变量在cur的下一个节点上。(next=cur->next)再创建一个空节点,然后让rhead这个变量等于NULL。(rhea=NULL;)

 然后我们把cur跟NULL连起来,(cur->next=rhead)再把rhead给cur。(cur=rhead)

 然后再把cur给给next。(next=cur);再让next跳到下一个节点上

 然后再重复上个步骤,再让cur指向rhead:

 依次类推,最终会实现逆转:

 代码:

  1. struct ListNode* reverseList(struct ListNode* head){
  2. if(head==NULL)
  3. {
  4. return NULL;
  5. }
  6. struct ListNode* rhead=NULL;
  7. struct ListNode* cur=head;
  8. while(cur)
  9. {
  10. struct ListNode* next=cur->next;
  11. cur->next=rhead;
  12. rhead=cur;
  13. cur=next;
  14. }
  15. return rhead;
  16. }

要注意的两点:

第一点:传值

  1. while(head)
  2. {
  3. struct ListNode* next=cur->next;
  4. cur->next=rhead;
  5. rhead=cur;
  6. cur=next;
  7. }

 像这里就会报错,因为我们传head进去,判断head是否会空,我们知道head是但是当第一次运算完之后这个节点就指向空了,

 第二次判断就直接退出循环了,所以我们要传cur。

第二点:返回值:

return cur

这里也会报错,因为我们最后要返回的是一个头结点,而cur最后会到最后那个节点 

 

 所以我们应该传rhead ,它才是头结点。

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

思路:快慢指针

slow,fast两个指针

让快指针先走k个节点,然后停下再让,slow指针开始走,然后fast指针再走,两个分别一人走一下,直到fast指针走完结束,此刻slow指针就是我们要找的倒数第k个节点

代码:

  1. struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
  2. struct ListNode* fast= pListHead,* slow=pListHead;
  3. // write code here
  4. while(k--)
  5. {
  6. fast=fast->next;
  7. }
  8. while(fast)
  9. {
  10. slow=slow->next;
  11. fast=fast->next;
  12. }
  13. return slow;
  14. }

报错:

根据下面的三个图我们可以看出问题所在: 

 

 从第三个案列开始,K都超过了链表的长度,然后预期输出里面都返回的空值,而我们的实际输出根本就输出不出来,因为我们没有设置如果k>链表长度则retuen NULL;这个条件。

 看第三个案列:

6{12,3,4,5}

 我们的链表总共就5个节点,fast走到第五个节点再往下走第六个节点就是NULL了

 所以我们只需要设置:

  1. if(fast==NULL)
  2. return NULL;

可运行的 代码:

链表分割_牛客题霸_牛客网 (nowcoder.com)

思路:尾插法

比如有一串数字:

 我们把小于9的放一个链表,大于9的放一个链表:

 因为是尾插,所以顺序不会变,然后我们把这两个链表链接起来。

 代码:

  1. class Partition {
  2. public:
  3. ListNode* partition(ListNode* pHead, int x) {
  4. // write code here
  5. struct ListNode* lesshead,*lesstail;
  6. struct ListNode* greathead,*greattail;
  7. lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
  8. greathead=greattail=(struct ListNode*)malloc(sizeof(struct ListNode));
  9. struct ListNode* cur=pHead;
  10. if(pHead==NULL)
  11. {
  12. return NULL;
  13. }
  14. while(cur)
  15. {
  16. if(cur->val<x)
  17. {
  18. lesstail->next=cur;
  19. lesstail=lesstail->next;
  20. }
  21. else
  22. {
  23. greattail->next=cur;
  24. greattail=greattail->next;
  25. }
  26. cur=cur->next;
  27. }
  28. lesstail->next=greathead->next;
  29. free(lesshead);
  30. free(greathead);
  31. return pHead;
  32. }
  33. };

 报错:

 我们自己输入一个测试用例然后运行,输出结果为空,就是输出不出来。

 

 然后我们再看报错提示:就是数组越界,堆栈溢出。

其实就是没有释放哨兵位:

我们应该把创建的

  1. struct ListNode* lesshead,*lesstail;
  2. struct ListNode* greathead,*greattail;

 释放掉。

虽然我们已经用free释放了,

  1. free(lesshead);
  2. free(greathead);

但是我们应该把哨兵位归为,把lesshead回到开头的位置。什么意思,就是把lesshead放到head前面,再释放它,对整个链表不影响。

  1. head=lesshead->next;
  2. free(lesshead);
  3. free(greathead);

 去掉lesshead不影响链表 

 改完之后还有个报错:

 极端的错误:

要么全是小于x的

要么全是大于x的

要么有大有小的

我们自测输入,输入全是小于x的,正常运行

 输入全大于x的,正常运行

 

 输入有大有小的,

 报错了,根据实际输出可以看出来陷入了死循环。也就是

这个样子,我们的 greattail节点本来应该指向NULL,但是却指向了lesstail,就造成了死循环。

正常的应该是这个样子:

所以我们只需要把gretail的next指向NULL就可以了

greattail->next=NULL;

 

 通过代码:

  1. class Partition {
  2. public:
  3. ListNode* partition(ListNode* pHead, int x) {
  4. // write code here
  5. struct ListNode* lesshead,*lesstail;
  6. struct ListNode* greathead,*greattail;
  7. lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
  8. greathead=greattail=(struct ListNode*)malloc(sizeof(struct ListNode));
  9. struct ListNode* cur=pHead;
  10. if(pHead==NULL)
  11. {
  12. return NULL;
  13. }
  14. while(cur)
  15. {
  16. if(cur->val<x)
  17. {
  18. lesstail->next=cur;
  19. lesstail=lesstail->next;
  20. }
  21. else
  22. {
  23. greattail->next=cur;
  24. greattail=greattail->next;
  25. }
  26. cur=cur->next;
  27. }
  28. lesstail->next=greathead->next;
  29. pHead=lesshead->next;
  30. greattail->next=NULL;
  31. free(lesshead);
  32. free(greathead);
  33. return pHead;
  34. }
  35. };

 《27. 移除元素 - 力扣(LeetCode)

这道题怎么解:

用一个src和一个dec

​都从第一个数开始跑,但是src要先跑,并且dec不可以超过src。

接着判断src是否等于val的值,如果等于val,那么就只有src++,dec不动。如果src不等于val,那么src就赋值给dec,把dec的值覆盖掉,这样dec就只肯定不是val的值了,就可以保证前面一定不是val的值。然后再把src和dec都++,继续重复。

刚开始:

 

 

 只考虑前半段,因为前半段是有效的后半段是无效的,

 前面的{2,2}就是我们要的结果

 代码:

  1. int removeElement(int* nums, int numsSize, int val){
  2. int DEC=0;
  3. int src=0;
  4. while(src<numsSize)
  5. {
  6. if(nums[src]!=val)
  7. {
  8. nums[DEC++]=nums[src++];
  9. }
  10. else
  11. {
  12. src++;
  13. }
  14. }
  15. return DEC;
  16. }

160. 相交链表 - 力扣(LeetCode)

1.判断两个链表的尾节点是否相等,相等则相交。

但是时间复杂度太大,更简单一点的,我们向前再挪一点,判断c1是否相等不是可以少计算一点吗,时间复杂度也会小点。

那应该如何去判断呢?

 让src去遍历B链表的每个节点从B1直到c3节点 ,看是否与a1相等,如果不相等,src++,到a2头上,再去遍历B链表,直到src到c1头上时去遍历B链表,也遍历到了B链表的c1节点,发现相等,那就相交。

但是这个时间复杂度:src一次要遍历n个节点,O(N)。要遍历N遍,那就是O(N*N)

有个时间复杂度少一点的:谁长谁先走一步。

然后同时走,每走一个就对比一下节点是否为同一个节点,直到走到相同节点,那就相交。

 

 这个时间复杂度就是O(N).

这个代码报错了,为什么?

  1. struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
  2. struct ListNode * longLlist=headA;
  3. struct ListNode * shortLlist=headB;
  4. if(shortLlist>longLlist)
  5. {
  6. shortLlist=shortLlist->next;
  7. }
  8. while(longLlist->next&&shortLlist->next){
  9. if(shortLlist->next==longLlist->next)
  10. {
  11. return longLlist;
  12. }
  13. }
  14. return NULL;
  15. }

第一,这里只定义了longLlist,shortLlist.但是没有实际意义,因为根据就没有去计算链表的长度。

我们应该让src一直走,每走一个len++,把A链表的长度计算出来,

B链表同理。

第2,不是谁长谁先走一步,而是谁长谁先长的步数,比如说下图:

 这让dst先走一步,再让src和dst同步走,两个也走不到一个节点上。只能让dst先走abs(dst-src)步,再让两者同步走。

  1. struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
  2. struct ListNode * src=headA;
  3. struct ListNode * dst=headB;
  4. int lenA=1,lenB=1,len=1;
  5. while(src->next)
  6. {
  7. src=src->next;
  8. lenA++;
  9. }
  10. while(dst->next)
  11. {
  12. dst=dst->next;
  13. lenB++;
  14. }
  15. if(src!=dst)
  16. {
  17. return NULL;
  18. }
  19. struct ListNode * longLlist= headA;
  20. struct ListNode * shortLlist=headB;
  21. int gap=0;
  22. gap=abs(lenA-lenB);
  23. if(lenA<lenB)
  24. {
  25. longLlist= headB;
  26. shortLlist=headA;
  27. }
  28. while(gap--)
  29. {
  30. longLlist=longLlist->next;
  31. }
  32. while(longLlist!=shortLlist)
  33. {
  34. longLlist= longLlist->next;
  35. shortLlist=shortLlist->next;
  36. }
  37. return longLlist;
  38. }

88. 合并两个有序数组 - 力扣(LeetCode)

思路:

这道题的意思是,A链表开辟m+n个空间,但是元素只有m个,B链表开辟n个空间,元素有n个

 

 然后按照升序排列把B链表合并过去,我们要的最终效果是这个样子:

 思路:

 1.设end1=m;

end2=n-1

i=m+n-1;

-1的原因是下标从0开始

 2.进行比较。

if     num1[end1]<nums2[end2]

这种情况的话就把nums2[end2]赋值给nums1[end2]

比如nums2[end2]=6,nums1[end1]=3,就把nums2[end2]复制给nums1[i];

 然后end2--,i--, end2和end1对比,end2大,执行nums1[i--]=nums2[end2--];

 end2和end1对比,end1大,执行  nums1[i--]=nums1[end1--];

然后执行end1--,i--,end2和end1对比,一样大,执行  nums1[i--]=nums2[end2--];
}

end2<0就结束了。

}

还有一种情况就是end1先走完。

 

 这种情况就是end1<0了,但是end2还大于0,但是还没完成合并,所以这里要单独写

while(end2>0)

就把nums2[end2]赋值给nums1[i]的位置

 结束

  1. void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
  2. int end1=m-1;
  3. int end2=n-1;
  4. int i=m+n-1;
  5. while(end1>=0&&end2>=0)
  6. {
  7. if(nums1[end1]>nums2[end2])
  8. {
  9. nums1[i--]=nums1[end1--];
  10. }
  11. else
  12. {
  13. nums1[i--]=nums2[end2--];
  14. }
  15. }
  16. while(end2>=0)
  17. {
  18. nums1[i--]=nums2[end2--];
  19. }
  20. }

876. 链表的中间结点 - 力扣(LeetCode)

思路:快慢指针

当链表为奇数链表时:

fast一次走两步,slow一次走一步,fast走完(fast=NULL),slow刚好走到中间 

当链表为偶数链表时,fast走超了一节点(fast->next->next=NULL),slow刚好走到中间:

  1. struct ListNode* middleNode(struct ListNode* head){
  2. struct ListNode* slow=head;
  3. struct ListNode* fast=head;
  4. while(fast&&fast->next)//fast!=NULL是奇数链表, fast->next不为空是偶数链表
  5. {
  6. fast=fast->next->next;
  7. slow=slow->next;
  8. }
  9. return slow;
  10. }

链表的回文结构__牛客网 (nowcoder.com)

思路:找到链表中间节点,876. 链表的中间结点 - 力扣(LeetCode)

从中间节点开始逆置206. 反转链表 - 力扣(LeetCode)

  1. //反转函数
  2. struct ListNode* middleNode(struct ListNode* head) {
  3. struct ListNode* slow = head;
  4. struct ListNode* fast = head;
  5. while (fast && fast->next) {
  6. fast = fast->next->next;
  7. slow = slow->next;
  8. }
  9. return slow;
  10. }
  11. //逆置函数
  12. struct ListNode* reverseList(struct ListNode* head) {
  13. if (head == NULL) {
  14. return NULL;
  15. }
  16. struct ListNode* tail = NULL;
  17. struct ListNode* src = head;
  18. struct ListNode* dst = src->next;
  19. while (src) {
  20. src->next = tail;
  21. tail = src;
  22. src = dst;
  23. if (dst)
  24. dst = dst->next;
  25. }
  26. return tail;
  27. }
  28. //判断函数
  29. class PalindromeList {
  30. public:
  31. bool chkPalindrome(ListNode* A) {
  32. // write code here
  33. ListNode* Pops=middleNode(A);
  34. ListNode* rmid=reverseList(Pops) ;
  35. while(rmid)
  36. {
  37. if(rmid->val!=A->val)//判断反转逆置完的第一个数的值是否相同
  38. {
  39. return false;
  40. }
  41. else//相同则判断下个节点的值是否相同
  42. {
  43. A=A->next;
  44. rmid=rmid->next;
  45. }
  46. }
  47. return true;
  48. }
  49. };

141. 环形链表 - 力扣(LeetCode)

首先,怎么判断是不是环形链表

1.我们可以通过快慢指针的方式,如果fast走到空就不是环形

2.如果fast->next-走到空就不是环形

上面这两种情况都不是环形,不是环形就返回false

 3.否则就是环形,是环形救直接返回true值吗,并不是,因为还要判断一下是否一个节点的next会回到前面的节点

快指针一次走两步,慢指针一次走一步,进入环里后,两个指针早晚会相遇。

这是因为假设fast和slow的距离为N(fast去追slow)

fast每次走两步,slow一次走1步,每同时走一次,fast和slow之间距离就缩小一步,也就是N-1.

再走一次就是N-2,最后会走到4,3,2,1,0,走到0的时候fast和slow就相遇了。

如果相遇,就返回true值

如果fast一次走3步,slow一次走1步,会相遇吗?

fast一次走3步,slow一次走一步,距离就缩小2步,N-2,第二次是N -4,最后会走成3,1,-1。

走到-1就错过了。

 如果错过了,fast和slow会继续走,fast会再次去追slow追几圈能追上不知道,能不能追上要看N-1减到最后是0还是-1,如果是0就追上了,如果是-1那就是没追上。

  1. bool hasCycle(struct ListNode *head) {
  2. struct ListNode* slow=head;
  3. struct ListNode* fast=head;
  4. while(fast&&fast->next)
  5. {
  6. fast=fast->next->next;
  7. slow=slow->next;
  8. if(slow==fast)
  9. {return true;
  10. }
  11. }
  12. return false;
  13. }

 142. 环形链表 II - 力扣(LeetCode)

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

闽ICP备14008679号