• C# 判断一个单链表是否有环及环长和环的入口点


    1.为什么写这个随笔?

    前几天参加一个电面,被问到这个问题,想总结一下。

    2.为什么标题强调C#?

    想在网上看看代码,却没找到C#版的,于是自己用C#实现一下。

    一、解决问题的思路

    1.一种比较耗空间的做法是,从头开始遍历链表,把每次访问到的结点存入一个集合(hashset)或字典(dictionary),如果发现某个结点已经被访问过了,就表示这个链表存在环,并且这个结点就是环的入口点。

    空间复杂度为O(n),时间复杂度为O(n)

    2.追赶法:使用两个slow, fast指针从头开始扫描链表。指针slow 每次走1步,指针fast每次走2步。如果存在环,则指针slow、fast会相遇;如果不存在环,指针fast遇到NULL退出。

    空间复杂度为O(1),时间复杂度为O(n)

    仅仅考虑代码实现方面第一种方法相对简单,所以下面用第二种方法来实现。

    二、解析(追击相遇)

    给出一个存在环的单链表:1,2,3,4,5,3,4,5...

    用图表示出来:

    然后按照追赶法的定义一步一步走:

    从上图可以看出第四次和第七次快慢指针在节点4处相遇,7-4=3,正好是环的长度;

    求出环的长度后,我们便可以求出入口点:

    链表总长=5;

    头结点到入口点的距离=链表总长-环长;即=5-3=2;

    然后从头结点开始向下遍历,得到入口点3;

    三、代码

     1.单链表节点

     1 //单链表节点 
     2 class LinkNode<T>
     3     {
     4         public LinkNode<T> Next
     5         { get; set; }
     6         public T Data
     7         { get; set; }
     8         public LinkNode()
     9         {
    10             Data = default(T);
    11             Next = null;
    12         }
    13         public LinkNode(T val)
    14         {
    15             Data = val;
    16             Next = null;
    17         }
    18         public LinkNode(T val,LinkNode<T> node)
    19         {
    20             Data = val;
    21             Next = node;
    22         }
    23     }

    2.单链表

      1     class DLinkedList<T>
      2     {
      3         public LinkNode<T> Head
      4         { get; set; }
      5         public int Length
      6         { get; set; }
      7         public DLinkedList()
      8         {
      9             Length = 0;
     10             Head = null;
     11         }
     12         public void Add(LinkNode<T> node)//插入节点
     13         {
     14             if (IsEmpty())
     15             {
     16                 Head = node;
     17                 Length++;
     18                 return;
     19             }
     20             else
     21             {
     22                 LinkNode<T> currentNode = Head;
     23                 while (currentNode.Next != null)
     24                 {
     25                     currentNode = currentNode.Next;
     26                 }
     27                 currentNode.Next = node;
     28                 Length++;
     29             }
     30         }
     31         public void Display()//显示链表
     32         {
     33             LinkNode<T> currentNode = Head;
     34             while (currentNode != null)
     35             {
     36                 Console.WriteLine(currentNode.Data);
     37                 currentNode = currentNode.Next;
     38             }
     39         }
     40         public int GetLength()//获取链表长度
     41         {
     42             return Length;
     43         }
     44         public bool IsEmpty()//判空
     45         {
     46             return Head == null ? true : false;
     47         }
     48         public bool HasCircle()//判断是否有环
     49         {
     50             LinkNode<T> slowNode = Head;
     51             LinkNode<T> fastNode = Head;//定义快慢节点,开始指向头结点
     52             bool result = false;
     53             int count = 0;
     54             int step = 0;
     55             while (slowNode.Next != null && fastNode.Next.Next != null)
     56             {
     57                 try
     58                 {
     59                     slowNode = slowNode.Next;//慢节点指向下一个
     60                     fastNode = fastNode.Next.Next;//快节点指向下一个节点的下一个节点
     61                 }
     62                 catch (NullReferenceException)
     63                 {
     64                     result = false;
     65                     break;
     66                 }
     67                 if (slowNode == fastNode && slowNode != Head && slowNode != null)
     68                 {
     69                     result = true;
     70                     Console.WriteLine($"The currentNode's Data is {slowNode.Data}");
     71                     count++;
     72                 }
     73                 if (count == 1)//第一次相遇
     74                     step++;//计步,获取环长
     75                 if (count == 2)//第二次相遇
     76                 {
     77                     Console.WriteLine($"Circle's length is {step}");
     78                     break;
     79                 }
     80             }
     81             GetPoint(step);
     82             return result;
     83         }
     84         public LinkNode<T> GetPoint(int circleLength)//获取相交点
     85         {
     86             int lineLength = Length -circleLength;
     87             int num = 1;
     88             LinkNode<T> currentNode = Head;
     89             while(currentNode.Next!=null)
     90             {
     91                 currentNode = currentNode.Next;
     92                 num++;
     93                 if(num==lineLength)
     94                 {
     95                     break;
     96                 }
     97             }
     98             Console.WriteLine($"The meet node's Data is {currentNode.Data}");
     99             return currentNode;
    100         }
    101     }

    3.测试

     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             DLinkedList<int> list = new DLinkedList<int>();
     6             LinkNode<int> tmp=null;
     7             for(int i =1;i<=10;i++)
     8             {
     9                 LinkNode<int> node = new LinkNode<int>(i);
    10                 if (i == 4)
    11                     tmp = node;
    12                 list.Add(node);
    13             }
    14             list.Add(tmp);
    15             Console.WriteLine(list.HasCircle());
    16             Console.ReadKey();
    17         }
    18     }

    测试结果如图:

  • 相关阅读:
    数据结构与算法
    android vitamio的坑
    Fragment的坑
    scrollview里面嵌套listview的坑
    关于微信小程序的tabbar
    Intent初级学习
    HDU 4562 守护雅典娜
    SGU 542 Gena vs Petya
    [二] EditText and Button
    A Data Structure for Dynamic Trees
  • 原文地址:https://www.cnblogs.com/yh2015/p/7504702.html
Copyright © 2020-2023  润新知