当前位置:   article > 正文

【数据结构】链表力扣刷题详解

【数据结构】链表力扣刷题详解

前言

题目链接

移除链表元素

链表的中间结点

反转链表

分割链表

环形链表的约瑟夫问题


 

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~


移除链表元素

题述

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 

思路1:直接删除原链表中不符合的节点

遍历原链表,遇到val就执行删除操作
执行删除操作修改指针的指向有点麻烦,还有其他办法

思路2:满足要求的节点放在新链表上

定义新链表,利用pcur遍历原链表,找不为val的节点,尾插在新链表中
新链表:newHead  newTail

要考虑链表是否为空
链表为空:插入进来的节点就是链表的头节点和尾节点
链表不为空,插入进来的节点就是链表的新的尾节点

代码实现思路2

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * struct ListNode *next;
  6. * };
  7. */
  8. typedef struct ListNode ListNode;
  9. struct ListNode* removeElements(struct ListNode* head, int val) {
  10. //定义新链表的头尾指针
  11. ListNode* newHead,* newTail;
  12. newHead=newTail=NULL;
  13. ListNode* pcur=head;
  14. while(pcur)
  15. {
  16. //不是val,尾插到新链表
  17. if(pcur->val!=val){
  18. //链表为空
  19. if(newHead==NULL)
  20. {
  21. newHead=newTail=pcur;
  22. }
  23. else{
  24. //链表不为空
  25. newTail->next=pcur;
  26. newTail=newTail->next;//
  27. }
  28. }
  29. //pcur继续往下走
  30. pcur=pcur->next;
  31. }
  32. if(newTail)//要先判断newTail本来是否为空
  33. {
  34. newTail->next=NULL;
  35. }
  36. return newHead;
  37. }

链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点

思路1:统计链表中节点的个数,通过除2找到中间节点

for(统计链表个数)
    for(根据除2结果走到中间节点)

思路2:快慢指针

slow指针每次走1步,fast走2步
当fast->next或者fast走到为空,则slow刚好指向的就是中间节点
 

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * struct ListNode *next;
  6. * };
  7. */
  8. typedef struct ListNode ListNode;
  9. struct ListNode* middleNode(struct ListNode* head) {
  10. ListNode* fast,*slow;
  11. fast=slow=head;
  12. while(fast&&fast->next)//条件fast和fast->next不能相反,会造成短路
  13. {
  14. slow=slow->next;//slow走1
  15. fast=fast->next->next;//fast走2
  16. }
  17. return slow;
  18. }

 反转链表


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

思路1

创建新链表,遍历原链表的节点将其插入到新链表中

思路2:创建3个指针

分别记录前驱节点,当前节点,后继节点,改变原链表的指向1

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * struct ListNode *next;
  6. * };
  7. */
  8. typedef struct ListNode ListNode;
  9. struct ListNode* reverseList(struct ListNode* head) {
  10. //要先处理空链表
  11. if(head==NULL)
  12. {
  13. return head;
  14. }
  15. ListNode *n1,*n2,*n3;
  16. n1=NULL;
  17. n2=head;
  18. n3=head->next;
  19. while(n2)
  20. {
  21. //修改n2的指向
  22. n2->next=n1;
  23. //n1,n2,n3往下走
  24. n1=n2;
  25. n2=n3;
  26. if(n3)//要先判断n3不为空,才能往下走
  27. {
  28. n3=n3->next;
  29. }
  30. }
  31. return n1;
  32. }

合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

代码实现

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * struct ListNode *next;
  6. * };
  7. */
  8. typedef struct ListNode ListNode;
  9. struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
  10. if (list1 == NULL)
  11. return list2;
  12. if (list2 == NULL)
  13. return list1;
  14. ListNode *l1, *l2;
  15. l1 = list1, l2 = list2;
  16. //创建头节点
  17. ListNode *newHead, *newTail;
  18. newHead = newTail = (ListNode*)malloc(sizeof(ListNode));
  19. while (l1 && l2) {
  20. if (l1->val < l2->val) {
  21. // l1小,l1上,但要先判断l1不为空指针
  22. // if (newHead == NULL)
  23. // newHead = newTail = l1;
  24. // else {
  25. // newTail->next = l1;
  26. // newTail = newTail->next;
  27. // }
  28. // l1 = l1->next;
  29. //代码优化
  30. newTail->next=l1;
  31. newTail=newTail->next;
  32. l1=l1->next;
  33. } else {
  34. // l2
  35. // if (newHead == NULL)
  36. // newHead = newTail = l2;
  37. // else {
  38. // newTail->next = l2;
  39. // newTail = newTail->next;
  40. // }
  41. // l2 = l2->next;
  42. //代码优化
  43. newTail->next=l2;
  44. newTail=newTail->next;
  45. l2=l2->next;
  46. }
  47. }
  48. if (l1) {
  49. newTail->next = l1;
  50. }
  51. if (l2) {
  52. newTail->next = l2;
  53. }
  54. return newHead->next;
  55. }

分割链表


给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你不需要 保留 每个分区中各节点的初始相对位置。

思路

定义2个链表:大链表和小链表,遍历原链表的节点将其放到对应的新链表中,最将大小链表首尾相连

创建大小链表都要先创建一个哨兵位

利用pcur遍历链表

代码实现

  1. /**
  2. * Definition for singly-linked list.
  3. * struct ListNode {
  4. * int val;
  5. * struct ListNode *next;
  6. * };
  7. */
  8. typedef struct ListNode ListNode;
  9. struct ListNode* partition(struct ListNode* head, int x){
  10. if(head==NULL)
  11. return head;
  12. //创建带头的大小链表
  13. ListNode* lessHead ,*lessTail;
  14. ListNode* greaterHead,*greaterTail;
  15. lessHead=lessTail=(ListNode*)malloc(sizeof(ListNode));
  16. greaterHead=greaterTail=(ListNode*)malloc(sizeof(ListNode));
  17. //遍历原链表,将节点放在对应的新链表中
  18. ListNode*pcur=head;
  19. while(pcur)
  20. {
  21. if(pcur->val < x)
  22. {
  23. //放在小链表中
  24. lessTail->next=pcur;
  25. lessTail=lessTail->next;
  26. }
  27. else
  28. {
  29. //放在大链表中
  30. greaterTail->next=pcur;
  31. greaterTail=greaterTail->next;
  32. }
  33. pcur=pcur->next;
  34. }
  35. //大链表尾要置为空
  36. greaterTail->next=NULL;
  37. //大小链表首尾相连
  38. lessTail->next=greaterHead->next;
  39. ListNode*ret=lessHead->next;
  40. free(greaterHead);
  41. free(lessHead);
  42. return ret;
  43. }

环形链表的约瑟夫问题

编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数,报到 m 的人离开。

下一个人继续从 1 开始报数。

n-1 轮结束以后,只剩下一个人,问最后留下的这个人编号是多少?、

代码实现

  1. //这道OJ题已经创建好了结构体结点,只是没有展示出来
  2. typedef struct ListNode ListNode;
  3. //申请新节点
  4. ListNode* BuyNode(int x)
  5. {
  6. ListNode* newNode=(ListNode*)malloc(sizeof(ListNode));
  7. //可写可不写,一般不会申请失败
  8. if(newNode==NULL)
  9. {
  10. exit(1);
  11. }
  12. newNode->val=x;
  13. newNode->next=NULL;
  14. return newNode;
  15. }
  16. //创建循环链表
  17. ListNode*createList(int n)
  18. {
  19. //创建头节点
  20. ListNode* phead=BuyNode(1);
  21. ListNode* ptail=phead;
  22. //2开始,共创建了n个节点
  23. for(int i=2;i<=n;i++)
  24. {
  25. ptail->next=BuyNode(i);
  26. ptail=ptail->next;
  27. }
  28. //链表要首尾相连,循环起来
  29. ptail->next=phead;
  30. return phead;
  31. }
  32. int ysf(int n, int m ) {
  33. //创建不带头单向循环链表
  34. //逢m删除节点
  35. ListNode*head=createList(n);
  36. ListNode*pcur=head;
  37. ListNode*prev=NULL;//用于记录pcur走过的位置,是pcur的前驱节点
  38. int count=1;
  39. //逢m删除节点
  40. while(pcur->next!=pcur)//表示删除节点到只剩下自己跳出循环
  41. {
  42. if(count==m)
  43. {
  44. //删除当前节点pcur
  45. prev->next=pcur->next;
  46. free(pcur);
  47. //删除pcur节点后,要让pcur走到新的位置,count置为初始值
  48. pcur=prev->next;
  49. count=1;
  50. }
  51. else
  52. {
  53. //pcur往后走
  54. prev=pcur;
  55. pcur=pcur->next;
  56. count++;
  57. }
  58. }
  59. //此时pcur节点就是幸存下来的节点
  60. return pcur->val;
  61. }

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

闽ICP备14008679号