zoukankan      html  css  js  c++  java
  • 有环单链表的结点个数的统计方法

    对于无环单链表,计算其结点个数是相当简单的。C代码如下:

    int get_length(list_t *head)
    {
            int len = 0;
            for (list_t *p = head; p != NULL; p = p->next)
                    len++;
            return len;
    }

    那么对于有环的单链表,统计其总的结点个数,就变得复杂一些。

    • 首先,统计在环上的总的结点个数,不妨记为N1;
    • 其次,找到环的入口结点,不妨记为Joint;
    • 然后,从单链表的头结点开始到Joint结束,统计这一线性链表上的总的结点个数(不包括Joint),不妨记为N2;
    • 最后,N1+N2就是有环单链表的总的结点个数。

    对应的C代码如下:

     1 int get_total_length(list_t *head)
     2 {
     3         int len = get_loop_length(head);
     4 
     5         list_t *joint = (len != 0) ? get_loop_joint(head) : NULL;
     6 
     7         for (list_t *p = head; p != joint; p = p->next)
     8                 len++;
     9 
    10         return len;
    11 }

    其中,

    • L3的get_loop_length()是获取环上的总的结点个数。
    • L5的get_loop_joint()是获取环的入口结点。

    在给出get_loop_length()和get_loop_joint()的实现之前,我们不妨先看看如何判断一个单链表有环。

    1. 判断一个单链表有环

    通常的做法就是使用快慢两个指针,把探索单链表是否有环的过程演绎成一个追击问题。快指针和慢指针都从链表头开始移动,慢指针每次移动一步,而快指针每次移动两步,如果快指针能够追上慢指针,则说明有环。C代码如下:

     1 /**
     2  * Detect a singly linked list has a loop
     3  */
     4 bool is_loop(list_t *head)
     5 {
     6         list_t *fast = head;
     7         list_t *slow = head;
     8 
     9         /*
    10          * If loop does not exist, fast should firstly reach the end
    11          * a) if the length of list is even, fast       will be NULL
    12          * b) if the length of list is odd,  fast->next will be NULL
    13          */
    14         while (fast != NULL && fast->next != NULL) {
    15                 fast = fast->next->next;
    16                 slow = slow->next;
    17 
    18                 /*
    19                  * Well, loop is found as the fast catches up with the slow
    20                  */
    21                 if (fast == slow)
    22                         return true;
    23         }
    24 
    25         return false;
    26 }

    2. 统计有环单链表的环上结点个数

    方法还是使用快慢指针。首先使用快慢指针定位一个环上的结点,然后从此结点的下一个结点开始,单步移动指针完成统计。C代码如下:

     1 /**
     2  * Get the length of the loop if a singly linked list has a loop
     3  */
     4 int get_loop_length(list_t *head)
     5 {
     6         list_t *fast = head;
     7         list_t *slow = head;
     8         list_t *node = NULL;
     9 
    10         /* get a node in the loop */
    11         while (fast != NULL && fast->next != NULL) {
    12                 fast = fast->next->next;
    13                 slow = slow->next;
    14 
    15                 if (fast == slow) {
    16                         node = slow;
    17                         break;
    18                 }
    19         }
    20 
    21         /* no loop found hence the length should be zero */
    22         if (node == NULL)
    23                 return 0;
    24 
    25         /* now walk again to get the length of the loop */
    26         int len = 1;
    27         for (list_t *p = node->next; p != node; p = p->next)
    28                 len++;
    29         return len;
    30 }

    3. 获取有环单链表的入口结点

    这个算法稍微复杂一点,当然还是使用快慢指针。首先使用快慢指针定位环上的一个结点,不妨记为Node; 然后让快指针从链表头Head开始单步移动,同时慢指针从Node开始单步移动。一旦快慢指针相遇,我们就找到了入口结点Joint。为帮助理解,首先假设:

    • Head到Joint的长度记为X
    • Joint到Node的长度记为Y1
    • Node到Joint的长度记为Y2

    然后用一个图给出X=Y2的证明。(图是本人用LibreOffice画的,转载请注明出处

    C代码如下: (注释中也给出了严格的数学证明)

     1 /**
     2  * Get the joint if a singly linked list has a loop
     3  */
     4 list_t *
     5 get_loop_joint(list_t *head)
     6 {
     7         list_t *fast = head;
     8         list_t *slow = head;
     9         list_t *node = NULL;
    10 
    11         /* get a node in the loop */
    12         while (fast != NULL && fast->next != NULL) {
    13                 fast = fast->next->next;
    14                 slow = slow->next;
    15 
    16                 if (fast == slow) {
    17                         node = slow;
    18                         break;
    19                 }
    20         }
    21 
    22         /* no loop found hence the joint should be NULL */
    23         if (node == NULL)
    24                 return NULL;
    25 
    26         /*
    27          * The slow walks the loop from the node, and let the fast walk the
    28          * list from its head. They should meet at the joint.
    29          *
    30          *     Head        Joint
    31          *     |           |
    32          *     O-->O-->O-->O<--O<--O
    33          *                 |       ^
    34          *                 V       |
    35          *                 O-->O-->O
    36          *                          
    37          *                           Node
    38          *
    39          *     x : The length from Head  to Joint
    40          *     y1: The length from Joint to Node
    41          *     y2: The length from Node  to Joint
    42          *
    43          *     Total steps of the slow walked: x + y1
    44          *     Total steps of the fast walked: x + y1 + y2 + y1
    45          *     Note the fast and the slow have the same times to move,
    46          *     So                              x + y1 == (x + y1 + y2 + y1) / 2
    47          *                                 ==> 2x + 2y1 == x + 2y1 + y2
    48          *                                 ==> x == y2
    49          */
    50         fast = head;
    51         slow = node;
    52         while (fast != slow) {
    53                 fast = fast->next;
    54                 slow = slow->next;
    55         }
    56 
    57         return slow;
    58 }

    到此为止,我们就用Top-down的方法把有环单链表的结点个数的统计方法解释清楚了,完整代码请参见这里。也许你会好奇为什么我要研究诸如此类的单链表问题,答案之一就是那些装13的公司(比如BAT)特别喜欢考算法,吼吼:-)

  • 相关阅读:
    base64和Blob的相互转换
    限制文件上传的大小和尺寸
    git将本地项目提交到github
    vue-cli3创建项目时报错
    运行项目是node-sass报错的解决方法
    classList的使用
    将数组扁平化并去除其中重复数据,最终得到一个升序且不重复的数组
    移动端的图片放大
    js获取url中的参数
    HTML5-canvas
  • 原文地址:https://www.cnblogs.com/idorax/p/7874197.html
Copyright © 2011-2022 走看看