当前位置:   article > 正文

常见的链表面试问题详解(双指针思维)_pa=pa=nullptr

pa=pa=nullptr

面试的时候经常碰见诸如获取倒数第k个元素,获取中间位置的元素,判断链表是否存在环,判断环的长度等和长度与位置有关的问题。这些问题都可以通过灵活运用双指针来解决。

53b9e4a0ad674dbabfa37bdd75e61671.png

问题一:剑指Offer.链表中倒数第k个节点

问题描述:

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

思路解读:

设有两个指针 p 和 q,初始时均指向头结点。首先,先让 p 沿着 next 移动 k 次。此时,p 指向第 k+1个结点,q 指向头节点,两个指针的距离为 k 。然后,同时移动 p 和 q,直到 p 指向空,此时 q 即指向倒数第 k 个结点。可以参考下图来理解:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

 代码示例:

  1. class Solution {
  2. public:
  3. ListNode* getKthFromEnd(ListNode* head, int k) {
  4. ListNode *p = head, *q = head; //初始化
  5. while(k--) { //将 p指针移动 k 次
  6. p = p->next;
  7. }
  8. while(p != nullptr) {//同时移动,直到 p == nullptr
  9. p = p->next;
  10. q = q->next;
  11. }
  12. return q;
  13. }
  14. };

26d957f9c70f4c16b764aa694303d952.png

问题二:LeetCode.链表的中间节点

问题描述:

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

思路解读:

设有两个指针 fast 和 slow,初始时指向头节点。每次移动时,fast向后走两次,slow向后走一次,直到 fast 无法向后走两次。这使得在每轮移动之后。fast 和 slow 的距离就会增加一。设链表有 n 个元素,那么最多移动 n/2 轮。当 n 为奇数时,slow 恰好指向中间结点,当 n 为 偶数时,slow 恰好指向中间两个结点的靠前一个(可以考虑下如何使其指向后一个结点呢)(只需要加判断fast的下一个不为空,即不清楚能否走两次只要下一个位置不空,就往下走,直到遇到空走不了为止)

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

下述代码实现了慢指针指向靠后结点,即题目要求

代码示例:

  1. class Solution {
  2. public:
  3. ListNode* middleNode(ListNode* head) {
  4. //快慢指针
  5. ListNode *fast=head;
  6. ListNode *slow=head;
  7. //快的一次走两下,直到一次走不了两次
  8. while(fast!=nullptr&&fast->next!=nullptr)
  9. {
  10. slow=slow->next;
  11. fast=fast->next->next;
  12. }
  13. return slow;
  14. }
  15. };

9b5e6730af6a46cfbb0c0b25a42fe03b.png

问题三:LeetCode.环形链表

首先明白是否存在环的问题。如果将尾结点的 next 指针指向其他任意一个结点,那么链表就存在了一个环。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

问题描述:

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

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

思路解读:

当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。

具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

fe5acac4213e3fc0d50c6660e2cf42e4.gif

代码示例:

  1. class Solution {
  2. public:
  3. bool hasCycle(ListNode *head) {
  4. //快慢指针
  5. if(head==nullptr||head->next==nullptr)
  6. return false;
  7. ListNode* fast=head->next;
  8. ListNode* slow=head;
  9. while(slow!=fast)
  10. {
  11. if(fast==nullptr||fast->next==nullptr)
  12. return false;
  13. else
  14. slow=slow->next; //慢的走一步
  15. fast=fast->next->next; //快的走两步
  16. }
  17. return true;
  18. }
  19. };

最后一个问题,如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。

76fba0a344b44500997e7614fa684311.png

问题四:LeetCode.相交链表

问题描述:

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lmd5Lmd5Li4aW8=,size_20,color_FFFFFF,t_70,g_se,x_16

思路解读:

设「第一个公共节点」为 node ,「链表 headA」的节点数量为 a ,「链表 headB」的节点数量为 b ,「两链表的公共尾部」的节点数量为 c ,则有:

头节点 headA 到 node 前,共有 a - c 个节点;
头节点 headB 到 node 前,共有 b - c个节点;

考虑构建两个节点指针 A​ , B 分别指向两链表头节点 headA , headB ,做如下操作:

指针 A 先遍历完链表 headA ,再开始遍历链表 headB ,当走到 node 时,共走步数为:
a + (b - c)

指针 B 先遍历完链表 headB ,再开始遍历链表 headA ,当走到 node 时,共走步数为:
b + (a - c)

如下式所示,此时指针 A , B 重合,并有两种情况:

a + (b - c) = b + (a - c)

若两链表 有 公共尾部 (即 c > 0 ) :指针 A , B 同时指向「第一个公共节点」node 。
若两链表 无 公共尾部 (即 c = 0 ) :指针 A , B 同时指向 null 。

97261160dacbc7b39c9f1ab653e0b4da.png

代码示例:

  1. class Solution {
  2. public:
  3. ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
  4. if(headA==nullptr||headB==nullptr)
  5. {
  6. return nullptr;
  7. }
  8. //双指针
  9. ListNode *pa=headA;
  10. ListNode *pb=headB;
  11. while(pa!=pb)
  12. {
  13. pa=pa==nullptr?headB:pa->next;
  14. pb=pb==nullptr?headA:pb->next;
  15. }
  16. return pa;
  17. }
  18. };

如果觉得文章写得还不错,麻烦点个赞支持一下,欢迎评论,互相学习!

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号