有关链表的一些常见面试题

在我们面试的时候链表是考官们比较喜欢考的问题之一,链表总的来说比较简单,但是却比较容易出错,我刚开始接触链表的时候,一不小心就出现了各种各样的问题,因此在这里总结了一些链表有关的面试题。

1.删除一个无头单链表的非尾节点

这里无头单链表并不是真正的无头,只是不告诉我们而已,在做这个题的时候,根据我们的思路,如果要删除一个节点的简单方法就是,把这个节点的前一个节点的next指向后一个节点,然后把这个节点释放掉。这样就删除了一个节点。
但是我们的问题中我们找不到前一个节点,因此我们需要另辟蹊径,我们从题目里面看出删除的是非尾节点,为什么要删除非尾节点?我们从这里入手,画出了这样一个图去解决这个问题:

这里写图片描述
通过这样的思路就可以解决我们的问题了。有了这样的思路之后我们写代码就变得异常简单了

//删除一个无头单链表的非尾节点
void DelNotTail(ListNode* pos)
{
    ListNode* tmp = pos->next;
    assert(pos && pos->next);
    pos->data = tmp->data;
    pos->next = tmp->next;
    free(tmp);
    tmp = NULL;
}

2.在无头单链表的一个节点前插入一个节点

有了第一个题的思路,我们是不是可以举一反三呢?既然没办法直接在前面插入一个节点,那么我们在后面插入一个节点,然后交换两个数是不是就可以了。简单吧,想到了这一点,然后我们就可以写出代码了。
//在无头单链表的一个节点前插入一个节点
void InsertFront(ListNode* pos, DataType x)
{
    ListNode* tmp = BuyNode(pos->data);
    ListNode* next = pos->next;
    assert(pos && pos->next);

    pos->next = tmp;
    tmp->next = next;

    pos->data = x;

}

3.单链表实现约瑟夫环

做这个题首先就要知道什么是约瑟夫环,可以在网上百度一下什么是约瑟夫环(有一个关于约瑟夫环的故事),简单的来讲就是有一个圆环,每次每隔一定间隔释放一个节点,直到最后剩下唯一的一个节点,刚开始拿到这个题,我就被吓住了,让我在纸上画一画还行,让我写代码,感觉好难,但是当我认真的分析了之后发现并不难,因为它是一个环,每次释放掉一个节点之后,可以把前一个节点和后一个节点连接起来,那么**终止条件**是什么?只需要考虑它是一个环,而且最后剩下唯一一个节点,那么终止条件不就是他的下一个节点就是它本身了么?图如下,假设每隔k个节点释放一个节点,最后剩下了一个n

这里写图片描述
对于每个问题只要我们认真分析之后发现都不难

//在函数外不是一个环
ListNode* JosephRing(ListNode* pList, DataType k)
{
    ListNode* next = pList;
    ListNode* cur = NULL;
    while(next->next != NULL)  //让单链表成为一个环
    {
        next = next->next;
    }
    next->next = pList;
    next = pList;
    while(next->next != next)  //结束条件是下一个节点是本身
    {
        int count = k;
        while(--count)
        {
            cur = next;
            next = next->next;
        }
        cur->next = next->next;
        next = next->next;
    }
    return cur;
}

4.链表逆置

他弄个洋拿到一个问题先分析,对这个题依旧如此,即使第一次我们思考了很久才得出答案,或者都没有得出答案,但是我们思考过了,下次遇到到的时候我们就会有一个印象了。对于这个题,我们可以设置三个指针依次去逆置它,但由于逻辑性要很强,我也不很了解,所以我们采用了另一种方法,通过依次采摘节点的方法;
这里写图片描述
在这样分析了之后我们写出代码

//链表逆置
void Reverse(ListNode** pList)
{
    ListNode* NewList = NULL;
    ListNode* cur = *pList;
    if(*pList == NULL || (*pList)->next == NULL)
        return;
    while(cur)
    {
        ListNode* tmp = cur; //摘节点
        cur = cur->next;

        tmp->next = NewList;
        NewList = tmp;
    }
    PrintList(NewList);
}

5.合并有序链表

通过遍历两个链表,每次将较小的结点插入到合并后的新链表。

ListNode* MergeList(ListNode* pList1, ListNode* pList2)
{
    ListNode* list = pList1;
    ListNode* newlist = NULL;
    //如果有链表为空,直接返回另外一个链表即可
    if(pList1 == NULL)
        PrintList(pList2);
    else if(pList2 == NULL)
        PrintList(pList1);
    //摘一个头节点,选取较小的那个作为新链表的头
    if(pList2->data < list->data)
    {
        list = pList2;
        pList2 = pList2->next;
    }
    else
    {
        list = pList1;
        pList1 = pList1->next;
    }
    newlist = list;
    //有其中一个链表结束则结束循环
    while(pList1 !=NULL && pList2 != NULL)
    {
        if(pList1->data <pList2->data)
        {
            newlist->next = pList1;
            newlist = pList1;
            pList1 = pList1->next;
        }
        else
        {
            newlist->next = pList2;
            newlist = pList2;
            pList2 = pList2->next;
        }

    }
    //直接将剩下的链表链到新链表后
    if(pList1 == NULL)
        newlist->next = pList2;
    else
        newlist->next = pList1;
    return list;

}

6.寻找中间节点

设置一个快慢指针,快指针每次走两步,慢指针每次走一步,注意结束条件。

ListNode* MidNode(ListNode* pList)
{
    ListNode* slow = pList;
    ListNode* fast = pList;
    //链表为空,直接返回空
    if(pList == NULL)
        return NULL;
    while(fast != NULL &&fast->next != NULL)
    {
        fast = (fast->next)->next;
        slow = slow->next;
    }
    return slow;
}

7.查找单链表的倒数第k个节点

同样设置一个快慢指针,快指针先走K步,然后同时走,同样注意考虑边界条件

ListNode* FindKNode(ListNode* pList, DataType k)
{
    ListNode* slow = pList;
    ListNode* fast = pList;
    //链表为空,直接返回空
    if(pList == NULL)
        return NULL;
    while(k--)
    {
        if(fast == NULL)//链表节点少于k个,返回空
            return NULL;
        fast = fast->next;
    }
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

8.这里留下几个稍微难一点的题目,可以思考下

1.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度。
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】
4.复杂链表的复制。一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。

//ps: 复杂链表的结构
struct ComplexNode
{
int _data ; // 数据
struct ComplexNode * _next; // 指向下一个节点的指针
struct ComplexNode * _random; // 指向随机节点(可以是链表中的任意节点 or 空)
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值