100个小问题_每日一题_第1题

来源:互联网 发布:zoz软件下载吧 编辑:程序博客网 时间:2024/05/22 05:38

problem 1:

 

      假设一个节点集合,无空指针(每个指针都指向自身或是指向集合中的其他节点)。 
      1:编写一个代码断,已知某节点的指针,求从该节点开始,沿着链接最终可以到达不同的节点数,要求不修改任何的节点,使用的额外内存空间不多于某个常量。 
      2:在1的条件下,编写一个函数,沿着两个已知的链接(意思就是知道两个节点指针,分别沿着各自链接下去),判断他们是否最后终止于同一个循环。 

 

值得一提的是:本题最初来源于algorithms in c,是书中的一个课外思考题,后来被一些公司改成了笔试或是面试题。 


以下是对于problem1,我个人写出个一个解答吧,算是抛砖引玉。。。 

第一问的求解: 

首先,可以简单证明从集合中的任意一个已知节点开始,沿着链接最终可以形成一个环。 
证明如下(利用反证法):假设从某个节点k开始,沿着链接不能形成一个循环,那么这个链接必然是无穷的(因为没有空指针),然而节点集合却是有限的。矛盾,故必然会形成环。 

然后找到环路入口点,设置两个指针(fast, slow),初始值都指给定的开始的节点,slow每次前进一步,fast每次前进二步,经过若干步后,则fast必定先进入环,而slow后进入环。 
入环后,两个指针必定相遇。而当fast若与slow相遇时,slow肯定没有走遍历完环路,而fast已经在环内循环了n圈(1<=n)。 

简单证明如下: 

当slow与fast都进入环后,可以将这个问题比喻为2个人就是在操场当中跑步,速度快的会把速度慢的扣圈追上。 
可以证明,fast追赶上slow的时候,slow一定还没有走完一遍环路,fast也不会跨越slow多圈才追上 
我们可以从fast和slow的位置差距来证明,fast一定会赶上slow但是不会跳过slow的 
因为fast每次走2步,而slow走一步,所以他们之间的差距是一步一步的缩小(把slow看作是在fast前面,让fast去追它),x,x-1 ………… 4,3,2,1,0 到0的时候就fast就追上slow了。 

接下来是如何找到这个环路的入口呢? 

解法如下: 先让fast按照每次2步,slow每次1步的方式走,直到fast和slow重合。 
接下来,让fast回到初始的节点,重新走,每次步长不是走2了,而是走1,那么当fast和slow再次相遇的时候,就是环路的入口了。 

这点可以证明如下: 

在fast和slow第一次相遇的时候,假定slow走了n步骤,环路的入口是在p步的时候经过的,那么有 
slow走的路径: p+c = n;   c为slow和fast相交点距离环路入口的距离。 
fast走的路径: p+c+k*L = 2*n;   L为环路的周长,k是整数。 
显然,如果从p+c点开始,slow再走n步骤的话,还可以回到p+c这个点 
同时fast从头开始走的话,经过n步,也会达到p+c这点 
显然在这个步骤当中fast和slow只有前p步骤走的路径不同,而后面c步是必须相同的。 
所以当fast和slow再次重合的时候,必然是在节点的环路入口点上,此时就求得了p值,再加上环的长度L就可以求得所能走过的最多不同节点数目。 

程序如下: 
int search_most_diffrent_nodes(list  *start){ 
    list *slow = start , * fast = start; 
    int num = 0; 
    while  (1) { 
        slow  =  slow -> next; 
        fast  =  fast -> next -> next; 
        if  ( slow  ==  fast )  break ;          //第一次相遇 
    } 
    for(fast = start; fast != slow; fast = fast -> next, slow = slow -> next) 
        num++;        //求得p,即起点到环路入口的节点数目 
    do { 
        slow = slow -> next; 
        num++;                   //加上环路的长度,即环路的节点数目 
    } while(slow != fast) 
    return num; 


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

第二问2求解: 
先根据问题的思想找到两个链的各自第一次相遇点,即保证两个链都已经进入环内,然后选择其中的任意一个环,遍历该环,若是两环相交,那么必须在遍历过程中经过另外一个环的第一次相遇点。 
否则不相交,即两个链接不能最后终止于同一个循环。 
代码如下: 

bool is_intersect(list *st1, list *st2){ 
    list *slow1 = st1, fast1 = st1; 
    list *slow2 = st2, fast2 = st2; 
    do { 
        slow1  =  slow1 -> next; 
        fast1  =  fast1 -> next -> next; 
    } while ( slow1 !=  fast1 ) 
    
    do { 
        slow2  =  slow2 -> next; 
        fast2  =  fast2 -> next -> next; 
    } while ( slow2 !=  fast2 ) 
    
    if(slow1 = slow2) return true; 
    for( slow1 = slow1 -> next; slow1 != fast1; slow1 = slow1 -> next) if(slow1 == slow2) return true; 
    return false; 


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

原创粉丝点击