KimiZ님의 프로필SEIZE THE DAY사진블로그리스트기타 도구 도움말

블로그


    11월 24일

    特殊链表的拷贝--一道面试题

    面IBM时候被问到,死在这道题上了。标记一下。

            struct node
            {
                 int data;
                 struct node * next;
                 struct node * random;
            };

            本题来源是ms的一道面试题,要求是将一条有特殊结构的链表进行复制,链表的结构如上,除了正常的2个域之外,多了一个random,它代表一个指向链表 上某一任意节点的指针,要求将该链表复制。如图,其中实线表示next域,虚线表示random域。


            难点分析:本题最难的部分就在于如何将random域完好无损的完全拷贝复制到新的链表之上,对于普通链表一个while循环拷贝就可以搞定,但是这里似乎不可行。

            突破点:如何保存或记录random域可以作为一个思考的参考点,下面提供2种解法:
            1. hash缓存: 我们可以定义一个hash表,key为被拷贝链表中Node的地址,value为新的拷贝链表中Node的值。下面将在伪代码中进一步说明

            伪代码如下:
            struct node * old_list;
            struct node * new_list;

            hash ht;
            while( old_list != NULL )
            {
                if( ht.get(old_list) == NULL )   
                // 当前节点没有被复制过,则拷贝节点,同时将原节点的地址作为key,插入hash表
                {
                     new_node = copy(*old_list);
                     ht.set(old_list, new_node);
                }
                else    // 已经存在则直接将该节点读出
               {
                     new_node = ht.get(old_list);
               }
               // 并将新复制的节点插入链表
               insert(new_list, new_node);
       
               if( ht.get(old_list->random) == NULL )   
               // 如果ht的random不存在,那么产生一个random node,并放入hash表
               {
                    random_node = copy(*(old_list->random));
                    ht.set(old_list->ramdon,random_node);
               }
               else    //否则从hash表中读出random
               {
                    random_node = ht.get(old_list->random);
               }
               // 将random域进行填充
               new_node->random = random_node;
            }

            这样可以看出,因为地址值是唯一存在的,所以Hash函数的设计可以保证每个节点的值都会被一一映射,并且由于每一个节点都只会被复制一次,并且在代码的遍历过程中就已经完全复制好,空间复杂度为O(n),时间复杂度为O(n)

            2. 用一个图的方法看的比较清楚,首先在遍历原来链表的过程中,插入新的节点,比如A_N是copy(A_O)的,同时将A_N->next = A_O->next; A_O->next = A_N;通过一个遍历,我们就可以将链表复制成图中的样子。在完成next域的链接后,需要开始做random域的复制。

            在实现了next的结构之后,我们需要开始对new_list的random域和next域进行赋值。
            那么有: A_N->random = A_O->random->next;
            入图所示:A_O->random为C_O,C_O的next为C_N,这样A_N->random = C_N;
            对于 A_N->next = A_N->next->next;即B_N;
            该算法复杂度为O(N),空间复杂度为O(1),除了新拷贝的空间之外没有额外地址;