1. 程式人生 > >劍指Offer面試題:24.複雜連結串列的複製

劍指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);
    }

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

Offer試題24.複雜連結串列複製

一、題目:複雜連結串列的複製 題目:請實現函式ComplexListNode Clone(ComplexListNode head),複製一個複雜連結串列。在複雜連結串列中,每個結點除了有一個Next指標指向下一個結點外,還有一個Sibling指向連結串列中的任意結點或者NULL。   結點的定義

Offer試題5 從頭到尾列印連結串列

題目:輸入一個連結串列的頭結點,從尾到頭反過來打印出每個結點的值。 連結串列結點定義如下: struct ListNode { int m_nKey; ListNode* m_pNext; }; 思路一:將連結串列中的連線指標反轉過來,改變連結串列的方

offer試題5 從頭到尾列印連結串列(java)

注:(1)這裡體現了java資料結構與C語言的不同之處 (2)棧的操作直接利用stack進行 1 package com.xsf.SordForOffer; 2 3 import java.u

offer 試題重建二叉樹

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。 思路:二叉樹先序是根左右,中序 是左根右。所以先找到

offer 試題從尾到頭列印連結串列

題目:輸入一個連結串列,按連結串列值從尾到頭的順序返回一個ArrayList。 思路:有多種放法。(1)先反轉連結串列,再列印連結串列。(2)使用棧。 /** * struct ListNode { * int val; * struct ListNode *n

Offer試題17.樹的子結構

一、題目:樹的子結構 題目:輸入兩棵二叉樹A和B,判斷B是不是A的子結構。例如下圖中的兩棵二叉樹,由於A中有一部分子樹的結構和B是一樣的,因此B是A的子結構。   該二叉樹的節點定義如下,這裡使用C#語言描述: public class BinaryTreeNode {

Offer試題31.兩個連結串列的第一個公共節點

一、題目:兩個連結串列的第一個公共節點 題目:輸入兩個連結串列,找出它們的第一個公共結點。   連結串列結點定義如下,這裡使用C#語言描述: public class Node { public int key; public Node

Offer試題2.二維陣列中的查詢

一、題目:二維陣列中的查詢 題目:在一個二維陣列中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函式,輸入這樣的一個二維陣列和一個整數,判斷陣列中是否含有該整數。     例如下面的二維陣列就是每行、每列都遞增排序。如果在這個陣列中查詢數字7,則返回true;

Offer試題22.二叉搜尋樹的後序遍歷序列

一、題目:二叉搜尋樹的後序遍歷序列 題目:輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則返回true,否則返回false。假設輸入的陣列的任意兩個數字都互不相同。   例如在下面的一顆二叉搜尋樹中,輸入陣列{5,7,6,9,11,10,8},則返回true,因為這個整數序列是

Offer試題14.連結串列的倒數第k個節點

PS:這是一道出境率極高的題目,記得去年參加校園招聘時我看到了3次,但是每次寫的都不完善。 一、題目:連結串列的倒數第k個節點 題目:輸入一個連結串列,輸出該連結串列中倒數第k個結點。為了符合大多數人的習慣,本題從1開始計數,即連結串列的尾結點是倒數第1個結點。例如一個連結串列有6個結點,從頭結點開始

Offer試題28.連續子陣列的最大和

一、題目:連續子陣列的最大和 題目:輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。例如輸入的陣列為{1,-2,3,10,-4,7,2,-5},和最大的子陣列為{3,10,-4,7,2},因此輸出為該子陣列的和18。

Offer試題4.從尾到頭列印連結串列

一、題目:從尾到頭列印連結串列 題目:輸入一個連結串列的頭結點,從尾到頭反過來打印出每個結點的值。   到解決這個問題肯定要遍歷連結串列。遍歷的順序是從頭到尾的順序,可輸出的順序卻是從尾到頭。也就是說第一個遍歷到的結點最後一個輸出,而最後一個遍歷到的結點第一個輸出。這就是典型的“後進先出”,我

Offer試題10.數值的整數次方

一、題目:數值的整數次方 題目:實現函式double Power(doublebase, int exponent),求base的exponent次方。不得使用庫函式,同時不需要考慮大數問題。   在.NET Framework提供的BCL中,Math類實現了一個Pow方法,例如要求2的三次方,可

Offer試題6.用兩個棧實現佇列

一、題目:用兩個棧實現佇列 題目:用兩個棧實現一個佇列。佇列的宣告如下,請實現它的兩個函式appendTail和deleteHead,分別完成在佇列尾部插入結點和在佇列頭部刪除結點的功能。   原文是使用C++結合模板實現的定義,這裡我們採用C#結合泛型來實現這個佇列的定義,我們要實現的就是兩

Offer試題20.棧的壓入、彈出序列

一、題目:棧的壓入、彈出序列 題目:輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否為該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1、2、3、4、5是某棧的壓棧序列,序列4、5、3、2、1是該壓棧序列對應的一個彈出序列,但4、3、5、1、2就不可能是該壓棧序列的彈出序列。

Offer試題30.第一個只出現一次的字元

一、題目:第一個只出現一次的字元 題目:在字串中找出第一個只出現一次的字元。如輸入"abaccdeff",則輸出'b'。要求時間複雜度為O(n)。   最直觀的想法是從頭開始掃描這個字串中的每個字元。當訪問到某字元時拿這個字元和後面的每個字元相比較,如果在後面沒有發現重複的字元,則該字元就是隻出現

Offer試題34.翻轉單詞順序VS左旋轉字串

一、題目一:翻轉單詞順序 1.1 題目說明 題目一:輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字元的順序不變。為簡單起見,標點符號和普通字母一樣處理。例如輸入字串"I am a student.",則輸出"student. a am I"。 1.2 解題思路   第一步翻轉句子中所有的字

Offer試題23.二叉樹中和為某一值的路徑

一、題目:二叉樹中和為某一值的路徑 題目:輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。例如輸入下圖中二叉樹和整數22,則打印出兩條路徑,第一條路徑包含結點10、12,第二條路徑包含結點10、5和7。   二叉

Offer試題11.列印1到最大的n位數

一、題目:列印1到最大的n位數 題目:輸入數字n,按順序打印出從1最大的n位十進位制數。比如輸入3,則打印出1、2、3一直到最大的3位數即999。 二、不同的解法 2.1 不假思索的解法   最容易想到的辦法是先求出最大的n位數,然後用一個迴圈從1開始逐個列印: static v

Offer試題13.調整陣列順序使奇數位於偶數前面

一、題目:調整陣列順序使奇數位於偶數前面 題目:輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有奇數位於陣列的前半部分,所有偶數位於陣列的後半部分。   例如有以下一個整數陣列:12345,經過調整後可以為:15342、13542、13524等等。 二、解題思路 2.1 基本解