链表面试题总结(二)

昨天总结了一部分基础的面试题,有兴趣的小伙伴可以戳链接去看看链表面试题总结(一)。今天想要总结的是链表面试题的升级部分,关于链表带环部分和相交部分。

常见的面试题目:

  • 合并两个有序链表, 合并后依然有序
  • 求两个已排序单链表中相同的数据
  • 判断单链表是否带环?若带环,求环的长度?求环的入口点?
  • 判断两个链表是否相交,若相交,求交点。(假设链表不带环)
  • 判断两个链表是否相交,若相交,求交点。(假设链表可能带环)

合并两个有序链表, 合并后依然有序

分析:首先它是一个有序链表,那么数据一定是规律的。我们创建新指针newHead用来保存合并后有序链表的头,用一个tail指针向后进行比较,指向新的节点。用tmp保存当前节点的信息,然后指针向后移动。直到其中一个链表被遍历完,直接用tail将另一个链表的后续部分链接起来。

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

SListNode* Merge(SListNode* l1, SListNode* l2)  //合并两个有序链表,合并后依然有序
{
    if (l1 == NULL) return l2;
    if (l2 == NULL) return l1;
    SListNode* newHead;
    SListNode* tail;
    SListNode* tmp;
    if (l1->data < l2->data)
        newHead = tail = l1;
    else
        newHead = tail = l2;

    while ((l1!= NULL) && (l2 != NULL))
    {
        if (l1->data < l2->data)
        {
            tmp = l1;
            l1 = l1->next;
        }
        else
        {
            tmp = l2;
            l2 = l2->next;
        }
        tail->next = tmp;
        tail = tmp;
    }
    if(l1) tail->next = l1;
    else  tail->next = l2;

    return newHead;
}

求两个已排序单链表中相同的数据

分析:题目已经告诉我们是有序链表,所以一定要把这个条件用好。和上面的题目类似,直接对两个节点的数据进行比较,如果不相等,就向后移动,谁小就移动谁;如果相等,就直接打印此出来,直到遍历整个链表。由于较简单,就不画图说明了。

void UnionSet(SListNode* l1, SListNode* l2)   //求两个已排序单链表中相同的元素
{
    assert(l1 && l2);
    while (l1 && l2)
    {
        if (l1->data < l2->data)
            l1 = l1->next;
        else if (l1->data > l2->data)
            l2 = l2->next;
        else
        {
            printf("%d  ", l1->data);
            l1 = l1->next;
            l2 = l2->next;
        }
    }
}

判断单链表是否带环?若带环,求环的长度?求环的入口点?
1、判断单链表是否带环?

分析:先来判断一个链表是否带环,如果一个链表带环,它就没有尾节点,类似下图这种:

这里写图片描述

那么判断链表带环的问题就要从这里入手了,既然是一个环,那么肯定会在里面循环,所以就可以借鉴快慢指针问题了。定义一个快指针,一个慢指针,同时出发,如果可以相遇,那么就是环;如果不能相遇,则为非环链表。图示如下:

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

SListNode* IsCircle(SListNode* pHead)  //判断一个链表是否带环
{
    SListNode* fast, *slow;
    fast = slow = pHead;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
        {
            return slow;
        }
    }
    return NULL;
}
2、若带环,求环的长度

分析:如果判断为环了,定义一个计数器count,那么环的长度就是沿着这个环在走一遍,再次走到现在的位置时,count的值。

size_t GetCircle(SListNode* meetNode)  //如果带环,求环的长度
{
    assert(meetNode);
    int count = 1;
    SListNode* cur = meetNode->next;
    while (cur != meetNode)
    {
        cur = cur->next;
        ++count;
    }
    return count;
}
3、求环的入口点

分析:既然已经能确定是环,而且在第1问中,将相遇点返回了,那么我们就可以利用数学知识来求解入口点这个问题了。在这里,要说的方法还是快慢指针,这个题换成数学题,就变成了求相遇问题。下面用图来解释一下。

这里写图片描述
这里写图片描述
这里写图片描述

(关于为什么是一个走两步,一个走一步的原因:既然是要相遇,如果快指针走三步,它每次会跳过两个节点,慢指针一次只能走一步,这就可能会错过。快指针每次走两步的话,只会跳过一个节点,无论如何后都会和慢指针相遇。)

SListNode* GetEnter(SListNode* pHead, SListNode* meetNode)  //求环的入口
{
    SListNode* cur = pHead;
    assert(cur && meetNode);
    while (cur && meetNode)
    {
        if (cur == meetNode)
            return meetNode;
        cur = cur->next;
        meetNode = meetNode->next;
    }
    return NULL;
}

判断两个链表是否相交,若相交,求交点。(假设链表不带环)
1、判断两个链表是否相交

分析:如果两个链表相交,那么它应该如下图所示。根据它的结构特点,我们可以发现,如果两个链表相交,它的尾节点一定是相同的。也就是说当两个链表的尾节点相同时,它们一定是相交的。

这里写图片描述

SListNode* IsUnion(SListNode* l1, SListNode* l2)  //判断是否相交
{
    assert(l1 && l2);
    while (l1->next)
        l1 = l1->next;
    while (l2->next)
        l2 = l2->next;
    if (l1 == l2)
        return l1;

    return NULL;
}
2、若相交,求交点

分析:在链表中的相交相遇问题,貌似比较优的解法都是和快慢指针有关的。当然求交点,也是一样的。假设,两个链表一样长,那么它们的相交的判断方法就是在某处这两个节点的信息相同。好了,现在假设我们知道这两个链表的长度,让较长的那个先走两个链表长度之差步,那么接下来的处理方式就和一样长的链表的处理方式一样了。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

SListNode* GetMeetNode(SListNode* l1, SListNode* l2) //如果相交,求交点
{
    int len1 = 1;
    int len2 = 1;
    SListNode* cur1 = l1;
    SListNode* cur2 = l2;
    while (cur1->next)
    {
        cur1 = cur1->next;
        ++len1;
    }

    while (cur2->next)
    {
        cur2 = cur2->next;
        ++len2;
    }

    size_t k = abs(len1 - len2);
    SListNode* longNode, *shortNode;
    if (len1 > len2)
    {
        longNode = l1;
        shortNode = l2;
    }
    else
    {
        longNode = l2;
        shortNode = l1;
    }

    while (k--)
    {
        longNode = longNode->next;
    }

    while (longNode != shortNode)
    {
        longNode = longNode->next;
        shortNode = shortNode->next;
    }

    return shortNode;
}

判断两个链表是否相交,若相交,求交点。(假设链表可能带环)

其实这个题目和上面的那个比较相似,甚至说是上面的延伸。在这种情况下,我们只需要理清可能出现的情况,然后根据对应的情况进行分析。

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

上面这六种是满足题设要求的情况。每一种情况对应的判断条件都会有所不同,在这里就只和大家分享一下我对这个题的理解,不做详细的分析。

常见的链表面试题就这么多了,其实现在回头看这些题,解题的思路都略相像。可能不同的是,每个题想要的考察侧重点,学会分析题目的思路很重要,对题设要求一定要尽可能考虑到所有的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值