常见的单链表面试题——进阶篇
=================================================================================================================================================
单链表面试题——进阶篇
单链表是否带环
问题描述:单链表是否带环,若带环问环的入口点在哪里,还有环的长度是的多少?对于链表带环我们都知道链表带环会出现死循环,进环就不可能出来了,但这个想法用代码实现起来好像不可能。但可以利用这个特性定义快慢指针,快指针每次走两步,慢指针走一步,因为若带环它们在环内的距离总是一步一步在逼近,最终相遇。从相遇点开始再走一圈记录节点数量就求出了环的长度。具体思路如图:
如图就由2*slow=fast得:2*(L+X)=L+N*C+X
解得:L=N*C-X
所以设置两个指针让一个从头走,一个从快慢指针相遇点走,这两个指针相遇时就是环的入口点。
实现代码:
int IsHaveRing(ListNode* list)//这里设计一个返回值为int的变量可以直接返回环长度
{
int length = 1;
ListNode* slow = list;
ListNode* fast = list;
if (list == NULL)
return 0;//传来空链表
while (fast && (fast->next))//找尾
{
slow = slow->next;
fast = (fast->next)->next;
if (slow == fast)
break;//说明已经在环里相遇了就直接跳出循环
}
if ((fast == NULL) || (fast->next == NULL))//判断有没有找到尾
return -1;//返回-1代表没有环
while (fast->next != slow)//这里的判断条件默认少一个节点所以我将长度初值设为1
{
length++;
fast = fast->next;
}
return length;
}
判断两个链表是否相交(若不带环)
问题描述:两个链表都没有带环,也就是说它们都可以找到尾节点。相交就是其中一个链表的尾链到了另一个链表的任意一个节点上。怎么判断是否相交呢?很简单,只需判断它们是否有相同的尾就可以了。那么还有一个问题就是在哪个节点相交呢?可以用快慢指针的思路来解决,具体思路如下图所示:
实现代码:
ListNode* IsIntersect(ListNode* list1, ListNode* list2)
{
ListNode* tmp1 = list1;
ListNode* tmp2 = list2;
int count1 = 0;
int count2 = 0;
if (list1 == NULL && list2 == NULL)
return NULL;
while (tmp1->next)//遍历链表记录其节点数
{
count1++;
tmp1 = tmp1->next;
}
while (tmp2->next)
{
count2++;
tmp2 = tmp2->next;
}
if (tmp1 == tmp2)//判断尾若相等则相交
{
if (count1 > count2)//谁长决定谁先走差出来的步数
{
int gap = count1 - count2;
while (gap--)//先走gap步
list1 = list1->next;
while (list1 != list2)
{
list1 = list1->next;
list2 = list2->next;
}
list1->next = NULL;
return list1;
}
else
{
int gap = count2 - count1;//与上面那段代码是一个逻辑
while (gap--)
list2 = list2->next;
while (list1 != list2)
{
list1 = list1->next;
list2 = list2->next;
}
list1->next = NULL;
return list1;
}
}
return NULL;
}
链表相交问题的升级版
问题描述:若链表带环该如何解决上述问题?
一共有这几种情况:
实现代码:
int IsHaveRing(ListNode* list)//判断是否带环,带环则返回环长度,不带则返回-1
{
int length = 1;
ListNode* slow = list;
ListNode* fast = list;
if (list == NULL)
return 0;
while (fast && (fast->next))
{
slow = slow->next;
fast = (fast->next)->next;
if (slow == fast)
break;
}
if ((fast == NULL) || (fast->next == NULL))
return -1;//返回-1代表没有环
while (fast->next != slow)
{
length++;
fast = fast->next;
}
return length;
}
static ListNode* EnterRingNode(ListNode* list)//找入口点
{
ListNode* slow = list;
ListNode* fast = list;
if (list == NULL)
return 0;
while (fast)
{
slow = slow->next;
fast = (fast->next)->next;
if (slow == fast)
break;
}
while (slow != list)
{
slow = slow->next;
list = list->next;
}
return list;
}
static ListNode* IsIntersect(ListNode* list1, ListNode* list2)//不带环链表相交。 复用了不带环的判断代码
{
ListNode* tmp1 = list1;
ListNode* tmp2 = list2;
int count1 = 0;
int count2 = 0;
if (list1 == NULL && list2 == NULL)
return NULL;
while (tmp1->next)
{
count1++;
tmp1 = tmp1->next;
}
while (tmp2->next)
{
count2++;
tmp2 = tmp2->next;
}
if (tmp1 == tmp2)
{
if (count1 > count2)
{
int i = count1 - count2;
while (--i)
list1 = list1->next;
while (list1 != list2)
{
list1 = list1->next;
list2 = list2->next;
}
list1->next = NULL;
return list1;
}
else
{
int i = count2 - count1;
while (i--)
{
list2 = list2->next;
}
while (list1 != list2)
{
list1 = list1->next;
list2 = list2->next;
}
list1->next = NULL;
return list1;
}
}
else
return NULL;
}
//以上函数都是为下面这个函数服务的
ListNode* AdIsIntersect(ListNode* list1, ListNode* list2)//带环或不带环链表相交
{
int len1 = 0;
int len2 = 0;
if (list1 == NULL && list2 == NULL)
return NULL;
len1 = IsHaveRing(list1);
len2 = IsHaveRing(list2);
if ((len1 < 0) && (len2 < 0))//都不带环且相交情况处理
return IsIntersect(list1, list2);
else if (((len1 > 0) && (len2 < 0))||((len1 < 0) && (len2 > 0)))//一个带环一个不带环情况的处理
return NULL;
else
{
ListNode* tmp1 = EnterRingNode(list1);
ListNode* tmp2 = EnterRingNode(list2);
//判断一个环内是否有另一个的节点
while (tmp2->next != EnterRingNode(list2))
{
if (tmp2 == tmp1->next)
{
if (EnterRingNode(list1) != EnterRingNode(list2))
{//若入口点相同视为一种情况
ListNode* list3 = list1;
while (list3->next != EnterRingNode(list1))
{
list3 = list3->next;
}
list3->next = NULL;
return IsIntersect(list1, list2);
}
return EnterRingNode(list1);//入口点不同就返回其中一个
}
tmp2 = tmp2->next;
}
return NULL;//一个环内没有另一个的节点则返回空
}
}
复杂链表的复制问题
问题描述:有链表里有两个指针一个指向下一个节点,另一个随机指针指向其他节点(有可能指向空),现在要复制这个链表。一般思路先创建一个链表,先复制它的next对应的节点,然后在通过遍历的方式去给它的random赋值,这样做太麻烦,而且效率还很低。于是乎就有了这样的方法:
实现代码:
ListNode* CPList(ListNode* list)
{
ListNode* tmp = list;
while (tmp)
{
ListNode* insert = BuyNode(tmp->data);
insert->next = tmp->next;
tmp->next = insert;
tmp = tmp->next->next;
}
ListNode* CPNode = list;
while (CPNode)
{
if (CPNode->random != NULL)
CPNode->next->random = CPNode->random->next;
CPNode = CPNode->next->next;
}
ListNode* div = list;
ListNode* cphead = list->next;
ListNode* cptail = cphead;
while (cptail->next)
{
div->next = div->next->next;
div = div->next;
cptail->next = div->next;
cptail = cptail->next;
if (div->next->next == NULL)//断开原链表的最后一个节点,以防止俩链表相交
div->next = NULL;
}
return cphead;
}
通过三遍遍历的方式就完成了复杂链表的复制问题
测试结果:
上一篇: /*****/单链表常见面试题
下一篇: 常见链表面试题解题思路