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

本文深入讲解了链表的基本操作,包括链表逆序、求环、寻找环起点及判断链表相交等内容,并提供了详细的算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表是常用的数据结构。

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;
//此处增加了对fast的下一个结点的判断,如果fast->link不存在,fast->link->link语句则是非法的;
        while (fast && fast->link) 
       {
           slow = slow->link;
           fast = fast->link->link;
            if (slow == fast)
           {
              hasCircle = ture;
              break;
            }
         }
       return hasCircle;
}

3、求有环链表环的起点p

如下图:

链表逆序、环、环的起点、相交、交点 - oyjh1986 - oyjh1986的博客

 

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

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

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

又有fast = 2*slow

故 L = C - S

所以,如果一个指针从头开始走L步长,那么在p点的指针走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、无环链表情况:

    

链表逆序、环、环的起点、相交、交点 - oyjh1986 - oyjh1986的博客
方法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;
}


 

b.有环链表相交

链表逆序、环、环的起点、相交、交点 - oyjh1986 - oyjh1986的博客
解法:有环链表相交,两必然有同一个环;可设一快指针,一慢指针,在绕环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、求交点

从上面的图像和推导可以看出,求交点都可以转化为求环的起点问题。
无环->链表首尾相接;
有环->交点一个或者两个;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值