当前位置:   article > 正文

【数据结构】单链表经典面试题10道及详细思路代码_cur->next = prev->next;

cur->next = prev->next;

目录

1. 删除链表中等于给定值 val 的所有节点。

2. 反转一个单链表。

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

4. 输入一个链表,输出该链表中倒数第k个结点。

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

6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

7. 链表的回文结构。

8. 输入两个链表,找出它们的第一个公共结点。

9. 给定一个链表,判断链表中是否有环。

10. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL


1. 删除链表中等于给定值 val 的所有节点。

leetcode 203 移除链表元素

 

  1. typedef struct ListNode Node;
  2. struct ListNode* removeElements(struct ListNode* head, int val){
  3. Node* cur = head;
  4. Node *prev = NULL;//保存cur的前一个结点
  5. while(cur)
  6. {
  7. if(cur->val == val)
  8. {
  9. if(NULL == prev)
  10. {
  11. //需要删除的cur刚好是链表第一个结点,此时prev是空
  12. //需要修改head的指向
  13. head = cur->next;
  14. free(cur);
  15. cur = head;
  16. }
  17. else
  18. {
  19. //删除cur
  20. //让prev的next指向此时cur的next
  21. prev->next = cur->next;
  22. free(cur);
  23. //将prev赋值给cur
  24. cur = prev->next;
  25. }
  26. }
  27. else
  28. {
  29. prev = cur;
  30. cur = cur->next;
  31. }
  32. }
  33. return head;
  34. }

2. 反转一个单链表。

leetcode 206 反转链表

  1. typedef struct ListNode Node;
  2. struct ListNode* reverseList(struct ListNode* head){
  3. Node* prev = NULL;
  4. Node* cur = head;
  5. Node* next = NULL;
  6. //以左图为例子,此时,cur在1,其他在null
  7. while(cur)
  8. {
  9. next = cur->next; //next在2,cur在1,prev在空
  10. cur->next = prev;//改变链表指向,让cur指向空
  11. prev = cur; //prev在1
  12. cur = next;//cur在2,依次改变链表每个结点指向
  13. }
  14. return prev;
  15. }

 

3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

leetcode 876

给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路:

给两个指针fast slow,fast每次走2步,slow走一步,fast到末尾,slow在中间

  1. typedef struct ListNode Node;
  2. struct ListNode* middleNode(struct ListNode* head){
  3. Node* fast = head;
  4. Node* slow = head;
  5. //fast和接下来2步都不为空
  6. //fast不为null,可以保证fast第一步成功
  7. //fast->next 不为空,保证第二部走成功
  8. while(fast && fast->next)
  9. {
  10. fast = fast->next->next;
  11. slow = slow->next;
  12. }
  13. return slow;
  14. }

 拓展:要求假设返回中间的第一个结点

  1. Node* fast = head;
  2. Node* slow = head;
  3. Node* prev =NULL;
  4. while(fast && fast->next)
  5. {
  6. fast = fast->next->next;
  7. prev = slow;
  8. slow = slow->next;
  9. }
  10. if(fast) //总数是奇数
  11. {
  12. return slow;
  13. }
  14. else //总数是偶数
  15. {
  16. return prev;
  17. }

4. 输入一个链表,输出该链表中倒数第k个结点。

最优思路:先让front走k步,再让back和front同时走,当front到末尾,back就是倒数第k结点

OJ链接

  1. struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
  2. // write code here
  3. //先让front走k步,再让back和front同时走,当front到末尾,back就是倒数第k结点
  4. //1.对参数进行检测
  5. if(NULL ==pListHead || k <= 0)
  6. return NULL;
  7. //2.定义两个指针
  8. struct ListNode* front = pListHead;
  9. struct ListNode* back = pListHead;
  10. //注意K可能会大于链表长度
  11. while(k)
  12. {
  13. if(NULL == front)
  14. return NULL;
  15. front = front->next;
  16. k--;
  17. }
  18. while(front != NULL)
  19. {
  20. front = front->next;
  21. back = back->next;
  22. }
  23. return back;
  24. }

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

(Leetcode 21.合并两个有序链表)

 思路:

1.先创建一个新的链表

2.让cur1 和 cur2遍历两个链表,遇到的节点逐个比较,将值小的节点往新链表的末尾tail进行尾插

3.上述循环结束后,要判断有无链表还有剩余元素,把剩余的元素尾插到新链表的末尾。

  1. typedef struct ListNode Node;
  2. struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
  3. if(NULL == list1)
  4. return list2;
  5. if(NULL == list2)
  6. return list1;
  7. //走到这里,说明两个链表均不为空,需要合并
  8. Node* pNewList =NULL;
  9. Node* cur1 = list1;
  10. Node* cur2 = list2;
  11. //在pnewlist中插入第一个节点,第一个节点要修改pnewlist的指向
  12. if(cur1 -> val <= cur2->val)
  13. {
  14. pNewList = cur1;
  15. cur1 = cur1->next;
  16. }
  17. else
  18. {
  19. pNewList = cur2;
  20. cur2 = cur2->next;
  21. }
  22. Node* tail = pNewList;//新链表的尾部
  23. //让cur1 和 cur2遍历两个链表,遇到的节点逐个比较
  24. //将值小的节点往新链表的末尾tail进行尾插
  25. while(cur1 && cur2)
  26. {
  27. if(cur1->val <= cur2->val)
  28. {
  29. tail->next = cur1;
  30. cur1 = cur1->next;
  31. }
  32. else
  33. {
  34. tail->next = cur2;
  35. cur2 = cur2->next;
  36. }
  37. tail = tail->next;
  38. }
  39. if(cur1)
  40. {
  41. tail->next =cur1;
  42. }
  43. else if(cur2)
  44. {
  45. tail->next = cur2;
  46. }
  47. else{
  48. return pNewList;
  49. }
  50. return pNewList;
  51. }

6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。

OJ链接

思路:

1.创建两个链表,一个链表尾插值小于x的节点,另一个链表中插值大于等于x的节点

2.遍历原链表,将链表中的节点往两个新链表中尾插--新链表最好将其给成带头节点的单链表

3.将两个链表拼接

  1. class Partition {
  2. public:
  3. ListNode* partition(ListNode* pHead, int x) {
  4. if(NULL == pHead)
  5. return pHead;
  6. //less链表中尾插小于x的节点
  7. ListNode less(0);
  8. ListNode* lessTail = &less;
  9. //尾插大于等于x的节点
  10. ListNode greater(0);
  11. ListNode* greaterTail = &greater;
  12. ListNode* cur = pHead;
  13. //利用cur去遍历phead
  14. while(cur)
  15. {
  16. if(cur->val < x)
  17. {
  18. lessTail->next = cur;
  19. lessTail = lessTail->next;
  20. }
  21. else
  22. {
  23. greaterTail->next = cur;
  24. greaterTail = greaterTail->next;
  25. }
  26. cur = cur->next;
  27. }
  28. //拼接两个链表
  29. lessTail ->next = greater.next;
  30. greaterTail->next = NULL;
  31. return less.next;
  32. }
  33. };

7. 链表的回文结构。

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。OJ链接

思路1:

题目中条件:保证链表长度小于等于900,所以额外空间创建如下 为o(1)

1.给一个900的Int类型的数组

2.遍历链表,让每个节点的值放入数组

3.检测数组中保存的元素是否为回文结构

  1. class PalindromeList {
  2. public:
  3. bool chkPalindrome(ListNode* A) {
  4. int array[900];
  5. ListNode* cur = A;
  6. int size = 0;
  7. //遍历链表
  8. while(cur)
  9. {
  10. array[size] = cur-> val;
  11. cur = cur->next;
  12. size++;
  13. }
  14. int left = 0;
  15. int right = size-1;
  16. while(left < right)
  17. {
  18. if(array[left] != array[right])
  19. {
  20. return false;
  21. }
  22. else
  23. {
  24. left++;
  25. right--;
  26. }
  27. }
  28. return true ;
  29. }
  30. };

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。(去掉一个条件)

思路2:
1.找到链表的中间节点,从中间节点的位置将链表分开,形成2个链表---->找中间节点:采用快慢指针ok
2.对后半部分进行逆置---链表的逆置ok
3.将两个链表从前往后逐个节点进行比对,如果比对之后发现所有节点的值域都相同,则是回文链表,否则不是
4.将链表还原成原链表
注意:一般情况下不要破坏链表的结构,如果破坏了最后最好将其恢复

  1. class PalindromeList {
  2. public:
  3. ListNode* getMiddleNode(ListNode* plist)
  4. {
  5. ListNode* fast = plist;
  6. ListNode* slow = plist;
  7. while(fast && fast->next)
  8. {
  9. fast = fast->next->next;
  10. slow = slow->next;
  11. }
  12. return slow;
  13. }
  14. ListNode* reverseList(ListNode* plist)
  15. {
  16. ListNode* cur = plist;
  17. ListNode* prev = NULL;
  18. ListNode* next = NULL;
  19. while(cur)
  20. {
  21. next = cur->next;
  22. cur->next = prev;
  23. prev = cur;
  24. cur = next;
  25. }
  26. return prev;
  27. }
  28. bool chkPalindrome(ListNode* A)
  29. {
  30. if(NULL == A || NULL == A->next)
  31. return true;
  32. //找到A链表中间节点,然后从中间节点的位置将其断开
  33. ListNode* list1 = A;
  34. ListNode* midNode = getMiddleNode(A);
  35. ListNode* list2 = midNode->next;
  36. midNode->next = NULL;
  37. //将后半部分进行逆置
  38. list2 = reverseList(list2);
  39. //将list1和list2中的节点逐个比较
  40. bool ret = true;
  41. ListNode* cur1 = list1;
  42. ListNode* cur2 = list2;
  43. while(cur1 && cur2)
  44. {
  45. if(cur1->val != cur2->val)
  46. {
  47. ret = false;
  48. break;
  49. }
  50. cur1 = cur1->next;
  51. cur2 = cur2->next;
  52. }
  53. //还原链表
  54. list2 = reverseList(list2);
  55. midNode->next = list2;
  56. return ret;
  57. }
  58. };

8. 输入两个链表,找出它们的第一个公共结点。

leetcode 160.相交链表

下面是两个节点相交的各类情况:

 

 在相交的情况下,求交点的方式:

1.让较长的链表先走两个链表的差值步数

2.在让两个链表同时往后走,直到遇到地址相同的交点

  1. struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
  2. //参数检测,如果有一个链表为空,不可能相交
  3. if(NULL == headA || NULL == headB)
  4. {
  5. return NULL;
  6. }
  7. //1.检测headA和headB是否相交
  8. //分别找到两个链表最后一个节点,相同就相交,不同则不交
  9. struct ListNode* curA = headA;
  10. int sizeA = 1;
  11. int sizeB = 1;
  12. while(curA ->next)
  13. {
  14. curA = curA->next;
  15. sizeA++;
  16. }
  17. struct ListNode* curB = headB;
  18. while(curB->next)
  19. {
  20. curB = curB->next;
  21. sizeB++;
  22. }
  23. if(curB != curA)
  24. return NULL;
  25. //2.执行到此处说明相交
  26. int gap = sizeA - sizeB;
  27. curA = headA;
  28. curB = headB;
  29. if(gap > 0)
  30. {
  31. //headA长
  32. while (gap--)
  33. {
  34. curA = curA->next;
  35. }
  36. }
  37. else
  38. {
  39. while(gap++)
  40. {
  41. curB = curB->next;
  42. }
  43. }
  44. while(curA != curB)
  45. {
  46. curA = curA->next;
  47. curB = curB->next;
  48. }
  49. return curA;
  50. }

9. 给定一个链表,判断链表中是否有环。

leetcode 141,环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

思路:

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可
能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动
一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,
快指针肯定是可以追上慢指针的,即相遇
快指针如果走3、4步,可能存在永远不能遇到的可能

 

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

10. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

leetcode 142环形链表2

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。

 

 ​​​​​​​

 

 

  1. struct ListNode *detectCycle(struct ListNode *head) {
  2. struct ListNode* fast = head;
  3. struct ListNode* slow = head;
  4. int hasCycle = 0; //标记链表是否带环
  5. while(fast && fast->next)
  6. {
  7. fast= fast->next->next;
  8. slow = slow->next;
  9. if(fast == slow)
  10. {
  11. hasCycle = 1;
  12. break;
  13. }
  14. }
  15. if(!hasCycle)//链表不带环
  16. return NULL;
  17. struct ListNode* pH = head;
  18. struct ListNode* pM = fast; //相遇点
  19. while(pH != pM)
  20. {
  21. pH = pH->next;
  22. pM = pM->next;
  23. }
  24. return pM;
  25. }

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

闽ICP备14008679号