链表逆序、判断是否有环、求环的起点;两个链表是否相交、交点

链表是常用的数据结构。

typedef struct Node{

ElemType data;

Node* link;

}Node, *List;

常规链表操作,诸如遍历,插入,删除等就不再给出代码。需要注意的是,对链表的合法性进行检验;删除时,勿忘记释放其结点所占用的空间。



1、链表逆序:

链表逆序需要维护三个指针,一个指向前一个*pre,一个指向当前节点*cur,一个指向下一个节点*next以使当前节点的link域改变时,还能访问到其一下个节点。代码如下:

List reverseList(List head)

{

assert(head);

List next, pre = head, cur = head->link;

while(cur) {

next = cur->link; //保存当前结点的下一个结点

cur->link = pre; //改变当前结点的link域,指向它的前一个结点;



pre = cur; //指针移到一下个结点

cur = next;

}

head = pre; //恢复头结点

return head;

}

2、链表求环:

利用两一个指针,一个速度是另外一个的两倍。即一个指针慢指针:每次移动一个结点;一个快指针,每次移动两结点。若有环:则两指针会在某一点相遇;若无环:则快指针会先走到链表末尾。代码如下:

bool hasCircle(List head)

{

assert(head);

bool hasCircle = false;

List slow = head, fast = head;

while (fast && fast->link) //此处增加了对fast的下一个结点的判断,如果fast->link不存在,fast->link->link语句则是非法的;

{

slow = slow->link;

fast = fast->link->link;

if (slow == fast)

{

hasCircle = ture;

break;

}

}

return hasCircle;

}

3、求有环链表环的起点p,如下图:





假设快指针和慢指针在蓝色圆圈所表示结点p处相遇,则它们走过的路径长度分别为:

快指针fast = 非环段链表长度L + 环上链表结点个数C + 从p到交点的弧长S

慢指针slow = 非环段链表长度L + 从p到交点的弧长S

又有fast = 2*slow

故 L = C - S

因此:设两个指针,一个从表头开始遍历,一个从p开始遍历,相遇的结点就是环的起点。代码如下:

List beginOfCircle(List head)

{

assert(head);

List slow = head, fast = head;

while (fast && fast->link)

{

slow = slow->link;

fast = fast->link->link;

if (slow == fast)

break;

}

if (fast == NULL || fast->next == NULL) //此时链表无环;

return NULL;

fast = head;

while (slow != fast)
{

slow = slow->link;

fast = fast->link;
}

return fast;

}

4、判断链表是否相交

a、无环链表情况:




方法1:把链表的尾结点和另外一个链表的起点链接起来,然后判断是否有环(见编程之美)。代码如下:
bool isCross(List head1, List head2)
{
assert(head1 && head2);
List p1 = head1;
while (p1->link)
p1 = p1->link;
p1->link = head2;
if (hasCircle(head1))
return true;
return false;
}
方法2:如果两个无环链表相交,其尾节点必然相等!代码如下:
bool isCross(List head1, List head2)
{
assert(head1 && head2);
while (head1->link)
head1 = head1->link;
while (head2->link)
head2 = head2->link;
if (head1 == head2)
return true;
return false;
}


3 判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)。

比较好的方法有两个:

一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。

二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。

这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。


b.有环链表相交



解法:有环链表相交,两必然有同一个环;可设一快指针,一慢指针,在绕环N圈后,快指针必然遇到慢指针。代码如下:
bool isCross_(List head1, List head2)
{
bool isCross = false;
assert(head1 && head2);
List fast = head1, slow = head2;
while (fast && fast->link)
{
fast = fast->link->link;
slow = slow->link;
if (slow && slow == fast)
{
isCross = true;
break;
}
}
return isCross;
}

5、求交点:
从上面的图像和推导可以看出,求交点都可以转化为求环的起点问题。
无环->链表首尾相接;
有环->交点一个或者两个;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值