zoukankan      html  css  js  c++  java
  • 复杂链表的复制

    题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个next指向下一个节点,另一个特殊指针random指向一个随机节点), 请对此链表进行深拷贝,并返回拷贝后的头结点。

    最近有人跟我提到这个问题,网上一搜原来是一个经典面试题。我的笨脑袋想不到最优解法,但是想到了两个常规解和一个带限制的解法。下面把这四种解法都列出来。

    解法一:

    先做正常拷贝,即next正确。然后同时遍历两个链表,对于原链表的每个结点,再同时遍历两个链表找到原random所在位置,也就有了新random所在位置。

    时间复杂度为O(n^2)。

    解法二:

    先做正常拷贝,即next正确,同时建立一个哈希表<原结点,复制结点>。同时遍历两个链表,对于每一个复制结点,其random等于对应的原结点的random,在表中的映射。

    时间复杂度大致为O(n),因为哈希表通常很快。空间复杂度稍高。但是对于实际工程,这个哈希表通常可以复用,而且实现较有条理,容易理解,因此我认为在实际项目中这是最合适的解法。

    解法三:
    先做正常拷贝,即next正确,对于每个原结点,将其random指向对应的复制结点,而复制结点的random指向原结点的random。相当于是让复制结点充当一个桥接,复用原内存建立两个映射,并且没有丢失信息。

    最后同时遍历两个链表,每一步都重建原结点和复制结点的random。

    但是这个方法有个限制,必须前提保证random向后指,不能向前指,否则遍历的过程中也同时破坏映射,算法就会失效。

    解法四:

    这应该是最优解了:每次复制原结点,将复制结点插入其后,这样形成了一个长度2倍的复合链表。对于每一个复制结点,它的random为原结点的random的next。最后再把这个复合链表拆散还原。网上有详尽图文解释,这里不再赘述。

    下面是C++代码实现:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <unordered_map>
    
    typedef int Data;
    
    struct Node
    {
        Data data;
        Node *next;
        Node *random;
    
        Node()
        {
            random = next = NULL;
            data = rand();
        }
    };
    
    Node *copy_method1(Node *list)
    {
        Node *listCopy = NULL;
    
        Node *curr = list;
        Node *lastCopy = NULL;
        while (curr)
        {
            Node *currCopy = new Node;
            currCopy->data = curr->data;
            lastCopy ? lastCopy->next = currCopy : listCopy = currCopy;
            lastCopy = currCopy;
            curr = curr->next;
        }
    
        for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next)
        {
            if (curr->random)
            {
                for (Node *curr1 = list, *currCopy1 = listCopy; curr1; curr1 = curr1->next, currCopy1 = currCopy1->next)
                    if (curr1 == curr->random)
                        currCopy->random = currCopy1;
            }
        }
    
        return listCopy;
    }
    
    Node *copy_method2(Node *list)
    {
        // 1. Copy regular & make mapping
        Node *listCopy = NULL;
        Node *lastCopy = NULL;
        std::unordered_map<Node *, Node *> raw2Copy;
        for (Node *curr = list; curr; curr = curr->next)
        {
            Node *currCopy = new Node;
            currCopy->data = curr->data;
            raw2Copy.insert(std::make_pair(curr, currCopy));
            lastCopy ? lastCopy->next = currCopy : listCopy = currCopy;
            lastCopy = currCopy;
        }
    
        // 2. Eval random
        for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next)
            if (curr->random)
                currCopy->random = raw2Copy[curr->random];
        return listCopy;
    }
    
    Node *copy_method3(Node *list)
    {
        Node *listCopy = NULL;
        Node *lastCopy = NULL;
        for (Node *curr = list; curr; curr = curr->next)
        {
            // 1. Copy regular
            Node *currCopy = new Node;
            currCopy->data = curr->data;
            lastCopy ? lastCopy->next = currCopy : listCopy = currCopy;
    
            // 2. Make bridge
            currCopy->random = curr->random;
            curr->random = currCopy;
    
            lastCopy = currCopy;
        }
    
        for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next)
        {
            // 3. Get copy's random
            curr->random = currCopy->random;
            if (currCopy->random)
                currCopy->random = currCopy->random->random;
        }
    
        return listCopy;
    }
    
    Node *copy_method4(Node *list)
    {
        Node *listCopy = NULL;
        for (Node *curr = list; curr; curr = curr->next->next)
        {
            // 1. Insert
            Node *currCopy = new Node;
            currCopy->data = curr->data;
            currCopy->next = curr->next;
            curr->next = currCopy;
    
            if (listCopy == NULL)
                listCopy = currCopy;
        }
    
        for (Node *curr = list; curr; curr = curr->next->next)
        {
            // 2. Assign copy's random
            if (curr->random)
                curr->next->random = curr->random->next;
        }
    
        for (Node *curr = list; curr; curr = curr->next)
        {
            // 3. Split two lists
            Node *actualNext = curr->next->next;
            if (curr->next->next)
                curr->next->next = curr->next->next->next;
            curr->next = actualNext;
        }
    
        return listCopy;
    }
    
    Node *helper_genRandList()
    {
        // Generate nodes
        static const int N = 10;
        Node *nodes[N];
        srand(time(NULL));
        for (int i = 0; i < N; i++)
            nodes[i] = new Node;
    
        // Make regular list
        for (int i = 0; i < N - 1; i++)
            nodes[i]->next = nodes[i + 1];
    
        // Assign random random
        for (int i = 0; i < N - 1; i++)
        {
            // nodes[i]->random = nodes[std::max(i + 1, rand() % N)];
            nodes[i]->random = nodes[rand() % N];
        }
    
        return nodes[0];
    }
    
    int helper_validate(Node *listA, Node *listB)
    {
        while (listA && listB)
        {
            if (listA->data != listB->data)
                return 1;
            if (listA->random || listB->random)
            {
                if (listA->random->data != listB->random->data)
                    return 2;
                if (listA->random == listB->random) // Copy is incomplete
                    return 3;
            }
            listA = listA->next;
            listB = listB->next;
        }
        return 0;
    }
    
    void helper_printList(Node *list)
    {
        printf("---------------
    ");
        while (list)
        {
            printf("%d
    ", list->data);
            if (list->random)
                printf("(random)%d
    ", list->random->data);
            list = list->next;
        }
        printf("---------------
    ");
    }
    
    int main()
    {
        Node *list = helper_genRandList();
        printf("%d
    ", helper_validate(list, copy_method1(list)));
        printf("%d
    ", helper_validate(list, copy_method2(list)));
        printf("%d
    ", helper_validate(list, copy_method3(list)));
        printf("%d
    ", helper_validate(list, copy_method4(list)));
        return 0;
    }

     

  • 相关阅读:
    好久没来园子里转了,最近在学ssh,有个小问题提出来
    ClearType使用的问题
    Metro中访问特定设备的方法
    UMDF驱动程序快速上手
    关于GPS使用上的一个怪异问题
    一个不能创建WINCE6.0工程的问题
    Metro开发小记
    在WINPE中添加驱动
    DOS命令活用
    METRO开发中的多语言处理
  • 原文地址:https://www.cnblogs.com/xrst/p/13296772.html
Copyright © 2011-2022 走看看