【C语言】单链表相关面试题(二)

来源:互联网 发布:域名访问升级中 编辑:程序博客网 时间:2024/06/06 02:49

上篇博客介绍了查询链表中间结点,删除非尾结点,逆序链表三个问题。

有兴趣可点击链接查看:单链表相关面试题(一)

此篇博客将介绍:

1.删除倒数第k个结点(k>1)

2.合并两个有序链表

3.判断链表是否带环,若带环求环的长度,找出环的入口点

4.判断两条链表是否相交,相交则求出交点。

一、删除倒数第k个结点

1.算法思路:

        仍然通过设置快慢指针的方法解决,快指针每走一步,让k--,当k<0的时候,让慢指针也开始走,这样,当快指针走到链表尾部的时候,慢指针刚好指向倒数第k个结点。最后删除慢指针指向的那个结点。简单来说就是,让快指针先走k部,慢指针再开始走,这样快慢指针之间总是相差k步,所以,快指针指向空时,慢指针离空k个结点,即慢指针指向倒数第k个结点。

2.代码实现:

void DelKNode(pLinkList pList, int k){assert(pList);assert(k > 1);if (pList->pHead == NULL){return;}pLinkNode fast = pList->pHead;pLinkNode slow = pList->pHead;while (fast){k--;if (k < 0){slow = slow->next;}fast = fast->next;}if (k<=0)              //为了防止给进去的k不符合要求,超过链表的总长{pLinkNode del = NULL;del = slow->next;slow->data = del->data;slow->next = del->next;free(del);del = NULL;}}

3.测试

test9(){LinkList list;InitLinkList(&list);PushBack(&list, 1);PushBack(&list, 2);PushBack(&list, 3);PushBack(&list, 4);PushBack(&list, 5);PushBack(&list, 6);  //构建链表1->2->3->4->5->6PrintList(&list);DelKNode(&list, 5);   //删除倒数第5个结点PrintList(&list);}

4.测试结果



二、合并两个有序链表

1.算法思路:

假设顺序是由小到大,先考虑异常情况,当其中一条链表为空则返回另外一条链表。当两条链表都不为空的时候,设置新的头结点,比较两条链表第一个结点的数据域大小,让新的头结点指向数据域小的结点,且用来遍历此条链表的结点指针向后移。接下来循环比较两条链表里的数据域,让新链表的尾结点的指针域指向较小的结点。直到其中一条链表指向空,则让新的链表的尾指针的指针域指向用来另外一条链表的当前指针。可能文字描述不太能懂,我们画图说明一下。



2.代码实现:

pLinkNode merge(pLinkNode list1, pLinkNode list2){pLinkNode Newhead = NULL;pLinkNode Tail = NULL;pLinkNode cur1 = list1;pLinkNode cur2 = list2;if (list1 == NULL){return list2;}else if (list2 == NULL){return list2;}else{if (cur1->data > cur2->data){Newhead = cur2;cur2 = cur2->next;}else{Newhead = cur1;cur1 = cur1->next;}Tail = Newhead;while (cur1&&cur2){if (cur1->data > cur2->data){Tail->next = cur2;cur2 = cur2->next;Tail = Tail->next;}else{Tail->next = cur1;cur1 = cur1->next;Tail = Tail->next;}}if (cur1){Tail->next = cur1;}else if (cur2){Tail->next = cur2;}}return Newhead;}

3.测试

test10(){LinkList list;  //构建两个链表LinkList list1;LinkList list2;  pLinkNode ret = NULL;pLinkNode l1 = NULL;pLinkNode l2 = NULL;InitLinkList(&list);InitLinkList(&list1);InitLinkList(&list2);PushBack(&list, 1);PushBack(&list, 3);PushBack(&list, 5);PushBack(&list, 7);PushBack(&list1, 2);PushBack(&list1, 4);PushBack(&list1, 6);PrintList(&list);PrintList(&list1);l1 = list.pHead;l2 = list1.pHead;list2.pHead= merge(l1, l2);<span style="font-family: Arial, Helvetica, sans-serif;">//接收新的头结点</span>PrintList(&list2);DestroyList(&list);DestroyList(&list1);DestroyList(&list2);}

4.测试结果



三、判断链表是否带环,若带环求环的长度,找出环的入口点

1.算法思路:

设置快慢指针,快指针每次走两步,慢指针每次走一步,若链表带环,则快慢指针一定会在环上相遇,我们返回相遇点,若不带环,则快指针或者其指针域会为空,此时我们返回空。
如何计算环的长度:从相遇点遍历一遍,通过计数器获得环的长度。
找环的入口点:

2.代码实现:

pLinkNode CheckCycle(pLinkList pList);  //判断链表是否带环int GetCircleLength(pLinkNode meet);  //若带环求环的长度pLinkNode FindEntryNode(pLinkList pList, pLinkNode meet);//找出环的入口点
pLinkNode CheckCycle(pLinkList pList){assert(pList);pLinkNode fast = pList->pHead;pLinkNode slow = pList->pHead;while(fast&&fast->next){slow = slow->next;fast = fast->next->next;if (fast == slow){return slow;}}return NULL;}int GetCircleLength(pLinkNode meet){assert(meet);int count = 1;pLinkNode cur = meet->next;while (cur != meet){count++;cur = cur->next;}return count;}pLinkNode FindEntryNode(pLinkList pList, pLinkNode meet){assert(pList);pLinkNode cur = pList->pHead;while (cur != meet){cur = cur->next;meet = meet->next;}return cur;}

3.测试

 void test11(){LinkList list;int length=0;pLinkNode ret = NULL;pLinkNode meet = NULL;pLinkNode entry= NULL;InitLinkList(&list);PushBack(&list, 1);PushBack(&list, 2);PushBack(&list, 3);PushBack(&list, 4);PushBack(&list, 5);PushBack(&list, 6);ret = Find(&list, 6);   //构造环ret->next = Find(&list, 3);  //尾指针域指向3,则环的入口点应该为3meet=CheckCycle(&list);if (ret = NULL){printf("链表不带环");}else{length = GetCircleLength(meet);printf("环长%d\n", length);entry = FindEntryNode(&list, meet);printf("入口点为:%d", entry->data);}DestroyList(&list);}

4.测试结果



四、判断两条链表是否相交,相交则求出交点。

1.算法思路:

链表是否相交分为以下几种情况来讨论:
1>链表不带环,不带环时相交,只要遍历两条链表,判断两条链表的尾结点是否相同,相同则说明相交,不同则不相交。相交时,通过计数器的方法求出两条链表的长度,让长的那条链表先走长度的差值个结点,然后指向两条链表指针同时向后遍历,指针相等的时候就是交点。
2>一条带环一条不带环,此种情况一定不相交。
3>两条链表都带环。调用之前的判断链表是否带环函数,判断两条链表的相遇点是否在一个环上,若不在,则不相交,在一个环上则说明相交。相交时,从环上的任意一点断开。参照不带环相交的情况求出相交点。

2.代码实现:

pLinkNode CheckCross(pLinkList list1, pLinkList list2){int count1 = 1;int count2 = 1;pLinkNode tmp1 = CheckCycle(list1);//调用现有函数检测链表是否带环pLinkNode tmp2 = CheckCycle(list2);if (tmp1 == NULL && tmp2 == NULL)  //两条链表都不带环{pLinkNode cur1 = list1->pHead;pLinkNode cur2 = list2->pHead;pLinkNode pcur1 = list1->pHead;pLinkNode pcur2 = list2->pHead;while (cur1->next){cur1 = cur1->next;count1++;}while (cur2->next){cur2 = cur2->next;count2++;}if (cur1 == cur2)   //链表相交,求交点{int ret = count1 - count2;if (ret > 0){while (ret--){pcur1 = pcur1->next;}}else{ret = (int)fabs(ret);while (ret--){pcur2 = pcur2->next;}}while (pcur1 != pcur2){pcur1 = pcur1->next; //1 2 3 4 5 6  // 2 4 6 8 10 2 3 4 5 6pcur2 = pcur2->next;}return pcur1;}elsereturn NULL;}else if (tmp1 != NULL && tmp2 != NULL)  //两条链表都带环{pLinkNode cur1 = list1->pHead;pLinkNode cur2 = list2->pHead;int length1 = GetCircleLength(tmp1);int length2 = GetCircleLength(tmp2);if (length1 != length2){return NULL;}else{pLinkNode pcur1 = list1->pHead;pLinkNode pcur2 = list2->pHead;while (--length1){if (tmp1 == tmp2)    //链表相交{tmp2->next = NULL;int count1 = 0;int count2 = 0;while (cur1){cur1 = cur1->next;count1++;}while (cur2){cur2 = cur2->next;count2++;}int ret = count1 - count2;if (ret > 0){while (ret--){pcur1 = pcur1->next;}while (pcur1 != pcur2){pcur1 = pcur1->next;pcur2 = pcur2->next;}return pcur1;}else{ret = (int)fabs(ret);while (ret--){pcur2 = pcur2->next;}while (pcur1 != pcur2){pcur1 = pcur1->next;pcur2 = pcur2->next;}return pcur1;}break;}tmp1 = tmp1->next;}if (length1 == 0){return NULL;}}}    return NULL;}

3.测试

void test12(){LinkList list;LinkList list1;pLinkNode ret = NULL;pLinkNode ret1 = NULL;pLinkNode meet = NULL;InitLinkList(&list);InitLinkList(&list1);PushBack(&list, 1);PushBack(&list, 2);PushBack(&list, 3);PushBack(&list, 4);PushBack(&list, 5);PushBack(&list, 6);PushBack(&list1, 2);PushBack(&list1, 4);PushBack(&list1, 6);PushBack(&list1, 8);PushBack(&list1, 10);ret = Find(&list, 6);ret->next = Find(&list, 3);  //构建环ret1 = Find(&list1, 10);ret1->next = Find(&list, 2);//设置相交meet = CheckCross(&list, &list1);if (meet != NULL){printf("交点是%d", meet->data);}else{printf("不相交");}}

4.测试结果


本文测试用例都调用了链表基础操作里的某些函数。若对此有疑惑的亲可参考博文:单链表的基本操作

0 0