【数据结构】面试之链表问题集锦(下)

 

本文首发于我的公众号码农之屋(id: Spider1818),专注于干货分享,包含但不限于Java编程、网络技术、Linux内核及实操、容器技术等。欢迎大家关注,二维码文末可以扫。

导读:链表问题在面试过程中是非常重要也是非常基础的一环,主要就是考查面试者的编程功底,上篇《》已对一些常见的面试题进行介绍,本篇将继续介绍其他常见的链表相关的面试题。


1. 找到环的入口点

题目描述:输入一个单向链表,判断链表是否有环。如果链表存在环,如何找到环的入口点?

解题思路:由上题可知,按照 p2 每次两步,p1 每次一步的方式走,发现 p2 和p1 重合,确定了单向链表有环路了。接下来,让p2回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当 p1 和 p2 再次相遇的时候,就是环路的入口了。

 

代码如下:

Node* findLoopPort(Node *head) {     //如果head为空,或者为单结点,则不存在环      if(head == NULL || head->next == NULL) returnNULL;      Node *slow,*fast;     slow = fast = head;      //先判断是否存在环     while(fast != NULL &&  fast->next != NULL)     {         fast = fast->next->next;         slow = slow->next;         if(fast == slow)             break;     }      if(fast != slow)  returnNULL;    //不存在环      fast = head;                //快指针从头开始走,步长变为1     while(fast !=  slow)            //两者相遇即为入口点     {         fast = fast->next;         slow = slow->next;     }      return fast; }

2. 编程判断两个链表是否相交

题目描述:给出两个单向链表的头指针(如下图所示),

 

比如h1、h2,判断这两个链表是否相交。这里为了简化问题,我们假设两个链表均不带环。

解题思路:

解法一:直接循环判断第一个链表的每个节点是否在第二个链表中。但这种方法的时间复杂度为O(Length(h1) * Length(h2))。显然,我们得找到一种更为有效的方法,至少不能是O(N^2)的复杂度。

解法二:针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个节点是否在hash表出现,如果所有的第二个链表的节点都能在hash表中找到,即说明第二个链表与第一个链表有相同的节点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?

解法三:转换为环的问题。把第二个链表接在第一个链表后面,如果得到的链表有环,则说明两个链表相交。如何判断有环的问题上面已经讨论过了,但这里有更简单的方法。因为如果有环,则第二个链表的表头一定也在环上,即第二个链表会构成一个循环链表,我们只需要遍历第二个链表,看是否会回到起始点就可以判断出来。这个方法的时间复杂度是线性的,空间是常熟。

解法四:进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断两个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。

所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比解法三更胜一筹。

解法四的代码如下:

bool isIntersect(Node *h1,Node *h2) {     if(h1 == NULL || h2 == NULL) returnfalse;    //异常判断     while(h1->next  != NULL)     {         h1 = h1->next;     }      while(h2->next != NULL)     {         h2 = h2->next;     }      if(h1 == h2)      return true;        //尾节点是否相同     else     return false; }

3. 链表有环,如何判断相交

题目描述:上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?上面的方法还同样有效么?

分析:如果有环且两个链表相交,则两个链表都有共同一个环,即环上的任意一个节点都存在于两个链表上。因此,就可以判断一链表上俩指针相遇的那个节点,在不在另一条链表上。

代码如下:

bool isIntersectWithLoop(Node *h1,Node *h2) {     Node *circleNode1,*circleNode2;     if(!hasCircle(h1,circleNode1))    //判断链表带不带环,并保存环内节点         return false;                //不带环,异常退出     if(!hasCircle(h2,circleNode2))         return false;      Node *temp = circleNode2->next;     while(temp !=  circleNode2)     {         if(temp ==  circleNode1)             return true;         temp = temp->next;     }     return false; }

4. 两链表相交的第一个公共节点

 

题目描述:如果两个无环单链表相交,怎么求出他们相交的第一个节点呢?

分析:采用对齐的思想。计算两个链表的长度 L1 ,L2,分别用两个指针 p1 , p2 指向两个链表的头,然后将较长链表的 p1(假设为 p1)向后移动L2 - L1个节点,然后再同时向后移动p1 , p2,直到 p1 = p2。相遇的点就是相交的第一个节点。

代码如下:

//求两链表相交的第一个公共节点 Node* findIntersectNode(Node *h1,Node *h2){     int len1 =  listLength(h1);          //求链表长度     int len2 =  listLength(h2);     //对齐两个链表     if(len1 >  len2)     {         for(int i=0;i<len1-len2;i++)             h1=h1->next;     }     else      {         for(int i=0;i<len2-len1;i++)             h2=h2->next;     }      while(h1 != NULL)     {         if(h1 == h2)             return h1;         h1 = h1->next;         h2 = h2->next;        }     return NULL; }

我的公众号「码农之屋」(id: Spider1818) ,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值