单链表的相关面试题(一)

以下内容接《C语言实现单链表的增删查改》有需要者自行戳链接查看
linklist.h文件新增内容

//冒泡排序
void linklistBubbleSort(LinkNode *head);
//找出中间节点
LinkNode *FindMidNode(LinkNode *head);
//找倒数第K个节点
LinkNode *FindLastKNode(LinkNode *head,size_t k);
//删除倒数第K个节点
void EraseLastKNode(LinkNode **head,size_t k);
//合并两个有序链表,合并后依然有序,返回合并后链表
LinkNode *linklistMerge(LinkNode *head1,LinkNode *head2);
//判断链表是否带环
LinkNode *HasCycle(LinkNode *head);
//求带环链表的环的长度
size_t GetCycleLen(LinkNode *head);
//求环的入口点
LinkNode *GetCycleEntry(LinkNode *head);
///判断两个链表是否相交(假设链表不带环)
int HasCross(LinkNode *head1,LinkNode *head2);
//判断两个链表是否相交(链表可能带环)
int HasCrossWithCycle(LinkNode *head1,LinkNode *head2);

linklist.c文件新增内容

//冒泡排序函数
void linklistBubbleSort(LinkNode *head)
{
    if(head == NULL)
    {
        //非法输入
        return;
    }
    if(head->next == NULL)
    {
        //空链表不需要排序
        return;
    }
    //定义两个指针分别指向链表的头和尾
    LinkNode *cur = head;
    LinkNode *tail = NULL;
    //遍历链表
    for(;cur != NULL;cur = cur->next)
    {
        LinkNode *pre = head;
        for(;pre->next != tail;pre = pre->next)
        {
            //升序排序
            if(pre->data > pre->next->data)
            {
                //满足条件则交换两个节点中的数据
                LinkType tmp = pre->data;
                pre->data = pre->next->data;
                pre->next->data = tmp;
            }
        }
        //每一轮循环结束都会有一个最大的数被排到最后
        //这个数在下一轮排序中不需要在进行排序
        //所以将tail指针指向这个最大的数
        //下一轮排序就不会与这个数进行比较
        tail = pre;
    }
}
//以下为该函数的测试函数
void TestBubbleSort()
{
    Test_Header;
    linklistBubbleSort(head);
    linklistPrint(head);
}

测试结果如下图:(截图中加上了上一次最后一个测试的结果,便于观察)
单链表的相关面试题(一)_第1张图片

//找中间节点函数
LinkNode *FindMidNode(LinkNode *head)
{
    if(head == NULL)
    {
        //非法输入
        return NULL;
    }
    //定义一个快指针和一个慢指针
    LinkNode *fast = head;
    LinkNode *slow = head;
    while(fast != NULL && fast->next != NULL)
    {
        //只要条件满足
        //fast(快指针)往后走两步,slow(慢指针)往后走一步
        fast = fast->next->next;
        slow = slow->next;
    }
    //当fast不能再向前走时
    //由于快慢指针的移动步数刚好为2倍关系
    //fast走完链表,则slow刚好走完链表的一半
    //所以此时的slow刚好停留在中间节点处
    //返回slow就可以了
    return slow;
}
//以下为该函数的测试函数
void TestFindMidNode()
{
    Test_Header;
    linklistPrint(head);
    LinkNode *ret = FindMidNode(head);
    printf("MidNode:%c\n\n",ret->data);
}

测试结果如下图:
单链表的相关面试题(一)_第2张图片

//找到倒数第K个节点的函数
LinkNode *FindLastKNode(LinkNode *head,size_t k)
{
    if(head == NULL)
    {
        //非法输入
        return NULL;
    }
    if(k > linklistSize(head))
    {
        //链表中不存在该K值
        return NULL;
    }
    //定义两个指针
    //front指针在back指针前面
    LinkNode *front = head;
    LinkNode *back = head;
    int i = 0;
    for(;i < k-1;i++)
    {
        //先让front指针向后移动k-1步
        //front指针移动完成以后
        //back到倒数第K个节点的距离就等于front到链表结尾的距离
        front = front->next;
    }
    //然后让front和back指针每次移动一步往后移动
    //直到front指针走到链表的结尾
    //此时back指针就刚好指向倒数第K个节点
    for(;front->next != NULL;front = front->next,back = back->next)
        ;
    //返回back即返回倒数第K个节点
    return back;
}
//以下为该函数的测试函数
void TestFindLastKNode()
{
    Test_Header;
    linklistPrint(head);
    LinkNode *ret = FindLastKNode(head,2);
    printf("LastK(2)Node:%c\n\n",ret->data);
}

测试结果如下图:
单链表的相关面试题(一)_第3张图片

//删除倒数第K个节点函数
void EraseLastKNode(LinkNode **head,size_t k)
{
    if(head == NULL)
    {
        //非法输入
        return;
    }
    if((*head) == NULL)
    {
        //空链表没有元素可删
        return;
    }
    if(k > linklistSize(*head))
    {
        //链表中不存在该K值
        return;
    }
    //用之前写的函数找到倒数第K个节点
    //定义一个新的节点保存要删除的节点
    LinkNode *to_delete = FindLastKNode(*head,k);
    //定义一个指针指向链表的第一个节点
    LinkNode *cur = *head;
    //从头遍历链表,该遍历结束以后,cur指向的是待删除元素的前一个元素
    for(;cur->next != to_delete;cur = cur->next)
        ;
    //将cur的next指向待删除元素的下一个节点即可
    cur->next = to_delete->next;
    //释放待删除的节点
    DestroyNode(to_delete);
}
//以下为该函数的测试函数
void TestEraseLastKNode()
{
    Test_Header;
    linklistPrint(head);
    EraseLastKNode(&head,2);
    linklistPrint(head);
}

测试结果如下图:
单链表的相关面试题(一)_第4张图片

//合并两个有序链表,合并后依然有序,返回合并后的链表
LinkNode *linklistMerge(LinkNode *head1,LinkNode *head2)
{
    //定义两个指针分别指向新链表的头和尾
    LinkNode *new_head = NULL;
    LinkNode *new_tail = NULL;
    //定义两个指针分别指向待合并的两个链表的第一个节点
    LinkNode *cur = head1;
    LinkNode *pre = head2;
    if(head1 == NULL)
    {
        //链表1不存在则返回连表2
        return head2;
    }
    if(head2 == NULL)
    {
        //链表2不存在则返回连表1
        return head1;
    }
    //两个链表都不存在的情况已经包含在上面两种情况

    //此次合并完成以后的新链表是升序的

    //如果链表1的第一个元素比链表2 的第一个元素小
    if(cur->data < pre->data)
    {
        //则将新链表的第一个节点指向链表1 的第一个节点
        new_head = cur;
        //将cur指向链表1 的下一个元素
        cur = cur->next;
        //更改新链表的尾指针的指向,使其指向新链表的最后一个元素
        new_tail = new_head;
    }
    //如果链表1的第一个元素大于等于链表2 的第一个元素
    else
    {
        //则将新链表的第一个节点指向链表2 的第一个节点
        new_head = pre;
        //将pre指向链表2的下一个元素
        pre = pre->next;
        //更改新链表的尾指针的指向,使其指向新链表的最后一个元素
        new_tail = new_head;
    }
    //此时新链表已经有了第一个节点
    //在之后如果有元素需要加入到新链表中
    //就只需要修改新链表的尾指针就可以了
    while(cur != NULL && pre != NULL)
    {
        //如果链表1的第一个元素比链表2 的第一个元素小
        if(cur->data < pre->data)
        {
            //将新链表的尾指针的next指向待插入新链表的元素
            new_tail->next = cur;
            //cur移动到链表1的下一个元素
            cur = cur->next;
            //更改新链表的尾指针的指向,使其指向新链表的最后一个元素
            new_tail = new_tail->next;
        }
        else
        {
            //将新链表的尾指针的next指向待插入新链表的元素
            new_tail->next = pre;
            //pre移动到链表2 的下一个元素
            pre = pre->next;
            //更改新链表的尾指针的指向,使其指向新链表的最后一个元素
            new_tail = new_tail->next;
        }
    }
    //若退出了while循环,有以下两种情况
    //1:链表1走完了
    if(cur == NULL)
    {
        //此时需要把链表2 剩下的所有元素加到新链表的后面
        new_tail->next = pre;
    }
    //2:链表2走完了
    else
    {
        //此时需要把链表1 剩下的所有元素加到新链表的后面
        new_tail->next = cur;
    }
    //返回新链表
    return new_head;
}
//以下为该函数的测试函数
void TestMerge()
{
    Test_Header;
    //该测试函数创建了两个新的链表(head1 和 head2)
    LinkNode *head1;
    LinkNode *head2;

    linklistInit(&head1);
    linklistPushBack(&head1,'a');
    linklistPushBack(&head1,'c');
    linklistPushBack(&head1,'e');
    linklistPrint(head1);

    linklistInit(&head2);
    linklistPushBack(&head2,'b');
    linklistPushBack(&head2,'d');
    linklistPushBack(&head2,'f');
    linklistPushBack(&head2,'h');
    linklistPrint(head2);

    LinkNode *ret = linklistMerge(head1,head2);
    linklistPrint(ret);
}

测试结果如下图:
单链表的相关面试题(一)_第5张图片

//判断链表是否带环
LinkNode *HasCycle(LinkNode *head)
{
    if(head == NULL)
    {
        //非法输入
        return NULL;
    }
    //定义快慢指针
    LinkNode *fast = head;
    LinkNode *slow = head;

    while(fast != NULL && fast->next->next != NULL)
    {
        //快指针一次走两步,慢指针一次走一步
        fast = fast->next->next;
        slow = slow->next;
        //每次移动后判断两指针是否相等
        if(slow == fast)
        {
            //如果相等,说明二者相遇
            //如果链表不带环,那么由于快指针和慢指针的移动步数不同
            //所以快指针一定先与慢指针到达链表的结尾,二者肯定不会相遇
            //相遇则说明链表带环,返回相遇点即可
            return slow;
        }
    }
    return NULL;
}
//以下为该函数的测试函数
void TestHasCycle()
    {
        Test_Header;
        linklistPrint(head);
        LinkNode *ret1 = HasCycle(head);
        if(ret1 != NULL)
            printf("ret1: = [%c|%p]\n\n",ret1->data,ret1);
        //创建一个新的带环的链表(new_head)
        printf("new_head:a->b->c->d->e->c\n");
        LinkNode *new_head;
        linklistInit(&new_head);
        LinkNode *a = CreateNode('a');
        LinkNode *b = CreateNode('b');
        LinkNode *c = CreateNode('c');
        LinkNode *d = CreateNode('d');
        LinkNode *e = CreateNode('e');
        new_head = a;
        a->next = b;
        b->next = c;
        c->next = d;
        d->next = e;
        e->next = c;
        LinkNode *ret2 = HasCycle(new_head);
        if(ret2 != NULL)
            printf("ret2: = [%c|%p]\n\n",ret2->data,ret2);
    }

测试结果如下图:
单链表的相关面试题(一)_第6张图片

//求环的长度
size_t GetCycleLen(LinkNode *head)
{
    if(head == NULL)
    {
        return 0;
    }
    //由前一个判断链表是否带环的函数可以得到一个相遇点
    LinkNode *meet_node = HasCycle(head);
    if(meet_node ==NULL)
    {
        //如果相遇点返回的是一个空指针,则说明链表不带环
        //所以返回0即可
        return 0;
    }
    //定义一个指针指向相遇点
    LinkNode *cur = meet_node;
    //定义count用于计数
    size_t count = 1;
    //从相遇点的下一个节点(不包括相遇点)开始计数
    //到相遇点的前一个节点(包括前一个节点)计数结束
    //因为相遇点没有被计数在内
    //所以count计数从1开始
    for(cur = cur->next;cur != meet_node;cur = cur->next)
    {
        count++;
    }
    //返回count
    return count;
}
//以下为该函数的测试函数
void TestGetCycleLen()
    {
        Test_Header;
        size_t ret1 = GetCycleLen(head);
        printf("CycleLen = %lu\n",ret1);
        printf("new_head:a->b->c->d->e->c\n");
        LinkNode *new_head;
        linklistInit(&new_head);
        LinkNode *a = CreateNode('a');
        LinkNode *b = CreateNode('b');
        LinkNode *c = CreateNode('c');
        LinkNode *d = CreateNode('d');
        LinkNode *e = CreateNode('e');
        new_head = a;
        a->next = b;
        b->next = c;
        c->next = d;
        d->next = e;
        e->next = c;
        size_t ret2 = GetCycleLen(new_head);
        printf("CycleLen = %lu\n",ret2);
    }

测试结果如下图:
单链表的相关面试题(一)_第7张图片

//求环的入口点
LinkNode *GetCycleEntry(LinkNode *head)
{
    if(head == NULL)
    {
        return NULL;
    }
    //找到相遇点
    LinkNode *meet_node = HasCycle(head);
    if(meet_node == NULL)
    {
        //如果相遇点返回的是一个空指针,则说明链表不带环
        return NULL;
    }
    //定义一个指针指向相遇点
    LinkNode *cur = meet_node;
    //定义一个指针指向链表的第一个节点
    LinkNode *start = head;
    //详细解释看下面的图
    //所以start指针从链表的第一个节点往后走
    //同时cur指针从相遇点往后走
    //两指针相遇时 的节点就是环的入口点
    //相遇的情况有两种
    //1:cur指针从相遇点到入口点的距离刚好就等于链表第一个元素到入口点的距离
    //2:链表第一个元素到入口点的距离等于cur指针从相遇点到入口点的距离的N倍
    while(start != cur)
    {
        start = start->next;
        cur = cur->next;
    }
    return cur;
}
//以下为该函数的测试函数
void TestGetCycleEntry()
    {
        Test_Header;
        LinkNode *ret1 = GetCycleEntry(head);
        if(ret1 != NULL)
            printf("ret1:[%c|%p]\n",ret1->data,ret1);
        printf("new_head:a->b->c->d->e->c\n");
        LinkNode *new_head;
        linklistInit(&new_head);
        LinkNode *a = CreateNode('a');
        LinkNode *b = CreateNode('b');
        LinkNode *c = CreateNode('c');
        LinkNode *d = CreateNode('d');
        LinkNode *e = CreateNode('e');
        new_head = a;
        a->next = b;
        b->next = c;
        c->next = d;
        d->next = e;
        e->next = c;
        LinkNode *ret2 = GetCycleEntry(new_head);
        if(ret2 != NULL)
            printf("ret2:[%c|%p]\n",ret2->data,ret2);
    }

单链表的相关面试题(一)_第8张图片
测试结果如下图:
这里写图片描述

//判断两链表是否相交(假设链表不带环),相交返回1否则返回0
int HasCross(LinkNode *head1,LinkNode *head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return 0;
    }
    //定义两个指针分别指向两个链表的第一个元素
    LinkNode *cur1 = head1;
    LinkNode *cur2 = head2;
    //从头遍历链表
    for(;cur1->next != NULL;cur1 = cur1->next)
        ;
    for(;cur2->next != NULL;cur2 = cur2->next)
        ;
    //遍历结束以后,cur1和cur2 分别指向对应链表的最后一个元素
    //如果两个链表相交,那么从交点往后的所有元素都是共有的
    //所以如果相交,两个链表的最后一个节点肯定是同一个
    if(cur1 == cur2)
    {
        //如果相等说名两个链表的最后一个节点是同一个节点
        //返回1表示两个链表相交
        return 1;
    }
    return 0;
}
//以下为该函数的测试函数
void TestHasCross()
    {
        Test_Header;
        //创建一个新链表最后的一个节点指向head链表的某一个节点
        //即两个链表相交
        printf("new_head:a->b->c->d->(head->next)\n");
        LinkNode *new_head;
        linklistInit(&new_head);
        LinkNode *a = CreateNode('a');
        LinkNode *b = CreateNode('b');
        LinkNode *c = CreateNode('c');
        LinkNode *d = CreateNode('d');
        new_head = a;
        a->next = b;
        b->next = c;
        c->next = d;
        d->next = head->next;
        int ret1 = HasCross(head,new_head); 
        printf("ret1 = %d\n",ret1);
        //将新链表的最后一个节点指向空,不知想head链表的某一个节点
        //即两个链表不相交
        printf("new_head:a->b->c->d->NULL\n");
        d->next = NULL;
        int ret2 = HasCross(head,new_head); 
        printf("ret2 = %d\n",ret2);
    }

测试结果如下图:
单链表的相关面试题(一)_第9张图片

//判断两个链表是否相交(链表可能带环),相交返回1否则返回0
int HasCrossWithCycle(LinkNode *head1,LinkNode *head2)
{
    if(head1 == NULL || head2 == NULL)
    {
        return 0;
    }
    //调用判断链表是否带环的函数的到相遇点
    LinkNode *meet_node1 = HasCycle(head1);
    LinkNode *meet_node2 = HasCycle(head2);
    //如果相遇点都为空,说明两个链表都不带环
    if(meet_node1 == NULL && meet_node2 == NULL)
    {
        //此时调用判断链表是否相交(链表不带环)的函数即可
        int ret = HasCross(head1,head2);
        return ret;
    }
    //两个链表都带环
    else if(meet_node1 != NULL && meet_node2 != NULL)
    {
        //定义快慢指针同时指向链表1 的相遇点
        LinkNode *fast = meet_node1;
        LinkNode *slow = meet_node1;

        while(fast != NULL && fast->next->next != NULL)
        {
            //fast每次走两步
            fast = fast->next->next;
            //slow每次走一步
            slow = slow->next;
            //判断fast是否等于链表2 的相遇点
            if(fast == meet_node2)
            {
                //如果相等的话
                //说明走到了链表2 的环内
                //证明两个链表相交
                return 1;
            }
            if(fast == slow)
            {
                //如果fast等于slow说明只是在链表1 的环移动
                //因此链表不相交
                return 0;
            }
        }
    }
    //一个链表带环一个链表不带环
    else 
        //此时两个链表根本不可能相交
        //如果这种情况下两个链表相交
        //那么从交点往后的部分是两个链表共用的
        //因此在判断链表是否带环时应该同时都带环
        //而不是一个带环一个不带环
        return 0;
    return 0;
}
//以下为该函数的测试函数
void TestHasCrossWithCycle()
    {
        Test_Header;
        //创建一个带环的新链表(new_head1)
        printf("new_head1:a->b->c->b\n");
        LinkNode *new_head1;
        linklistInit(&new_head1);
        LinkNode *a = CreateNode('a');
        LinkNode *b = CreateNode('b');
        LinkNode *c = CreateNode('c');
        new_head1 = a;
        a->next = b;
        b->next = c;
        c->next = b;
        //创建一个带环的新链表(new_head2)
        printf("new_head2:x->y->z->y\n");
        LinkNode *new_head2;
        linklistInit(&new_head2);
        LinkNode *x = CreateNode('x');
        LinkNode *y = CreateNode('y');
        LinkNode *z = CreateNode('z');
        new_head2 = x;
        x->next = y;
        y->next = z;
        z->next = y;
        //判断新链表1和新链表2是否相交
        int ret1 = HasCrossWithCycle(new_head1,new_head2);
        printf("expected 0,actual %d\n",ret1);
        //创建一个新链表(new_head3)
        //但是其最后一个节点指向新链表1 的某一个节点
        printf("new_head3:d->e->f->(new_head1->next)\n");
        LinkNode *new_head3;
        linklistInit(&new_head3);
        LinkNode *d = CreateNode('d');
        LinkNode *e = CreateNode('e');
        LinkNode *f = CreateNode('f');
        new_head3 = d;
        d->next = e;
        e->next = f;
        f->next = new_head1->next;

        int ret2 = HasCrossWithCycle(new_head1,new_head3);
        printf("expected 1,actual %d\n",ret2);
    }

测试结果如下图:
单链表的相关面试题(一)_第10张图片

你可能感兴趣的:(c语言,数据结构,单链表,线性表,数据结构)