劍指Offer面試題:24.復雜鏈表的復制


一、題目:復雜鏈表的復制

題目:請實現函數ComplexListNode Clone(ComplexListNode head),復制一個復雜鏈表。在復雜鏈表中,每個結點除了有一個Next指針指向下一個結點外,還有一個Sibling指向鏈表中的任意結點或者NULL。

  結點的定義如下,采用C#語言描述:

    public class ComplexListNode
    {
        public int Data { get; set; }
        public ComplexListNode Next { get; set; }
        public ComplexListNode Sibling { get; set; }

        public ComplexListNode(int data)
        {
            this.Data = data;
        }

        public ComplexListNode(int data, ComplexListNode next, ComplexListNode sibling = null)
        {
            this.Data = data;
            this.Next = next;
            this.Sibling = sibling;
        }
    }

  下圖是一個含有5個結點的復雜鏈表。圖中實線箭頭表示m_pNext指針,虛線箭頭表示m_pSibling指針。為簡單起見,指向NULL的指針沒有畫出。

二、解題思路

2.1 O(n2)的普通解法

  第一步是復制原始鏈表上的每一個結點,並用Next節點鏈接起來;

  第二步是設置每個結點的Sibling節點指針。

對於一個含有n個結點的鏈表,由於定位每個結點的Sibling都需要從鏈表頭結點開始經過O(n)步才能找到,因此這種方法的總時間復雜度是O(n2)

2.2 借助輔助空間的O(n)解法

  第一步仍然是復制原始鏈表上的每個結點N創建N',然后把這些創建出來的結點用Next鏈接起來。同時我們把<N,N'>的配對信息放到一個哈希表中。

  第二步還是設置復制鏈表上每個結點的m_pSibling。由於有了哈希表,我們可以用O(1)的時間根據S找到S'

此方法使用空間換時間。對於有n個結點的鏈表我們需要一個大小為O(n)的哈希表,也就是說我們以O(n)的空間消耗把時間復雜度由O(n2)降低到O(n)

2.3 不借助輔助空間的O(n)解法

  第一步仍然是根據原始鏈表的每個結點N創建對應的N'。(把N'鏈接在N的后面)

    private static void CloneNodes(ComplexListNode head)
    {
        ComplexListNode node = head;
        while (node != null)
        {
            ComplexListNode cloned = new ComplexListNode();
            cloned.Data = node.Data;
            cloned.Next = node.Next;
            cloned.Sibling = null;

            node.Next = cloned;
            node = cloned.Next;
        }
    }

  第二步設置復制出來的結點的Sibling。(把N'的Sibling指向N的Sibling)

    private static void ConnectSiblingNodes(ComplexListNode head)
    {
        ComplexListNode node = head;
        while (node != null)
        {
            ComplexListNode cloned = node.Next;
            if (node.Sibling != null)
            {
                cloned.Sibling = node.Sibling;
            }
            node = cloned.Next;
        }
    }

  第三步把這個長鏈表拆分成兩個鏈表:把奇數位置的結點用Next鏈接起來就是原始鏈表,偶數數值的則是復制鏈表。

    private static ComplexListNode ReconnectNodes(ComplexListNode head)
    {
        ComplexListNode node = head;
        ComplexListNode clonedHead = null;
        ComplexListNode clonedNode = null;

        if (node != null)
        {
            clonedHead = clonedNode = node.Next;
            node.Next = clonedNode.Next;
            node = node.Next;
        }

        while (node != null)
        {
            // 復制鏈表的連接
            clonedNode.Next = node.Next;
            clonedNode = clonedNode.Next;
            // 原始鏈表的連接
            node.Next = clonedNode.Next;
            node = node.Next;
        }

        return clonedHead;
    }

  最后,將三個步驟銜接起來形成Clone方法:

    public static ComplexListNode Clone(ComplexListNode head)
    {
        CloneNodes(head);
        ConnectSiblingNodes(head);
        return ReconnectNodes(head);
    }

三、單元測試

  輔助方法的封裝

    public static void TestPortal(string testName, ComplexListNode head)
    {
        if (!string.IsNullOrEmpty(testName))
        {
            Console.WriteLine("{0} begins:", testName);
        }

        Console.WriteLine("The original list is:");
        PrintList(head);

        ComplexListNode clonedHead = Clone(head);
        Console.WriteLine("The cloned list is:");
        PrintList(clonedHead);
    }

    private static void PrintList(ComplexListNode head)
    {
        ComplexListNode node = head;
        while (node != null)
        {
            Console.WriteLine("The value of this node is: {0}.", node.Data);
            if (node.Sibling != null)
            {
                Console.WriteLine("The value of its sibling is: {0}.", node.Sibling.Data);
            }
            else
            {
                Console.WriteLine("This node does not have a sibling.");
            }

            Console.WriteLine();

            node = node.Next;
        }
    }

    private static void BuildNodes(ComplexListNode node, ComplexListNode next, ComplexListNode sibling)
    {
        if (node != null)
        {
            node.Next = next;
            node.Sibling = sibling;
        }
    }
View Code

  (1)Test1

    //          -----------------
    //         \|/              |
    //  1-------2-------3-------4-------5
    //  |       |      /|\             /|\
    //  --------+--------               |
    //          -------------------------
    public static void Test1()
    {
        ComplexListNode node1 = new ComplexListNode(1);
        ComplexListNode node2 = new ComplexListNode(2);
        ComplexListNode node3 = new ComplexListNode(3);
        ComplexListNode node4 = new ComplexListNode(4);
        ComplexListNode node5 = new ComplexListNode(5);

        BuildNodes(node1, node2, node3);
        BuildNodes(node2, node3, node5);
        BuildNodes(node3, node4, null);
        BuildNodes(node4, node5, node2);

        TestPortal("Test1", node1);
    }

  (2)Test2

    // Sibling指向結點自身
    //          -----------------
    //         \|/              |
    //  1-------2-------3-------4-------5
    //         |       | /|\           /|\
    //         |       | --             |
    //         |------------------------|
    public static void Test2()
    {
        ComplexListNode node1 = new ComplexListNode(1);
        ComplexListNode node2 = new ComplexListNode(2);
        ComplexListNode node3 = new ComplexListNode(3);
        ComplexListNode node4 = new ComplexListNode(4);
        ComplexListNode node5 = new ComplexListNode(5);

        BuildNodes(node1, node2, null);
        BuildNodes(node2, node3, node5);
        BuildNodes(node3, node4, node3);
        BuildNodes(node4, node5, node2);

        TestPortal("Test2", node1);
    }

  (3)Test3

    // Sibling形成環
    //          -----------------
    //         \|/              |
    //  1-------2-------3-------4-------5
    //          |              /|\
    //          |               |
    //          |---------------|
    public static void Test3()
    {
        ComplexListNode node1 = new ComplexListNode(1);
        ComplexListNode node2 = new ComplexListNode(2);
        ComplexListNode node3 = new ComplexListNode(3);
        ComplexListNode node4 = new ComplexListNode(4);
        ComplexListNode node5 = new ComplexListNode(5);

        BuildNodes(node1, node2, null);
        BuildNodes(node2, node3, node4);
        BuildNodes(node3, node4, null);
        BuildNodes(node4, node5, node2);

        TestPortal("Test3", node1);
    }

  (4)Test4

    // 只有一個結點
    public static void Test4()
    {
        ComplexListNode node1 = new ComplexListNode(1);
        node1.Sibling = node1;

        TestPortal("Test4", node1);
    }

  (5)Test5

    // 魯棒性測試
    public static void Test5()
    {
        TestPortal("Test5", null);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM