劍指Offer面試題:15.反轉鏈表


一、題目:反轉鏈表

題目:定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉后鏈表的頭結點。

  鏈表結點定義如下,這里使用的是C#描述:

    public class Node
    {
        public int Data { get; set; }
        // 指向后一個節點
        public Node Next { get; set; }

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

        public Node(int data, Node next)
        {
            this.Data = data;
            this.Next = next;
        }
    }

二、解題思路

2.1 借助外部空間的解法一

  由於題目並沒有要求必須原地反轉,因此可以借助外部空間實現。這里可以將單鏈表儲存為數組,然后按照數組的索引逆序進行反轉。但是,此方式比較浪費空間,而且需要兩次遍歷,效率不占優勢

  依據上面的思路,我們可以寫出以下代碼:

    public static Node ReverseList1(Node head)
    {
        if(head == null)
        {
            return null;
        }

        List<Node> nodeList = new List<Node>();
        while (head != null)
        {
            nodeList.Add(head);
            head = head.Next;
        }

        int startIndex = nodeList.Count - 1;
        for (int i = startIndex; i >= 0; i--)
        {
            Node node = nodeList[i];
            if (i == 0)
            {
                node.Next = null;
            }
            else
            {
                node.Next = nodeList[i - 1];
            }
        }
        // 現在頭結點是原來的尾節點
        head = nodeList[startIndex];
        return head;
    }

2.2 使用三個指針的高效解法二

  定義3個指針,分別指向當前遍歷到的結點、它的前一個結點及后一個結點。在遍歷過程中,首先記錄當前節點的后一個節點,然后將當前節點的后一個節點指向前一個節點,其次前一個節點再指向當前節點,最后再將當前節點指向最初記錄的后一個節點,如此反復,直到當前節點的后一個節點為NULL時,則代表當前節點時反轉后的頭結點了。

  整個過程只需遍歷鏈表一次,效率提高不少,且需要的外部空間也較第一種方法要少很多,實現代碼如下:

    public static Node ReverseList2(Node head)
    {
        if (head == null)
        {
            return null;
        }

        Node reverseHead = null;
        // 指針1:當前節點
        Node currentNode = head;
        // 指針2:當前節點的前一個節點
        Node prevNode = null;

        while(currentNode != null)
        {
            // 指針3:當前節點的后一個節點
            Node nextNode = currentNode.Next;
            if(nextNode == null)
            {
                reverseHead = currentNode;
            }
            // 將當前節點的后一個節點指向前一個節點
            currentNode.Next = prevNode;
            // 將前一個節點指向當前節點
            prevNode = currentNode;
            // 將當前節點指向后一個節點
            currentNode = nextNode;
        }

        return reverseHead;
    }

三、單元測試

3.1 測試用例

  (1)為了方便對比,封裝了一個用於將鏈表所有元素輸出為字符串的方法GetNodeString()

    // 輔助方法:生成鏈表元素的字符串用於對比
    public string GetNodeString(Node head)
    {
        if (head == null)
        {
            return null;
        }

        StringBuilder sbResult = new StringBuilder();
        Node temp = head;
        while (temp != null)
        {
            sbResult.Append(temp.Data.ToString());
            temp = temp.Next;
        }

        return sbResult.ToString();
    } 
View Code

  (2)功能測試、特殊輸入測試

    // 01.輸入的鏈表有多個結點
    [TestMethod]
    public void ReverseTest1()
    {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);

        node1.Next = node2;
        node2.Next = node3;
        node3.Next = node4;
        node4.Next = node5;

        Node newHead = ListHelper.ReverseList2(node1);
        Assert.AreEqual(GetNodeString(newHead), "54321");
    }

    // 02.輸入的鏈表只有一個結點
    [TestMethod]
    public void ReverseTest2()
    {
        Node node1 = new Node(1);

        Node newHead = ListHelper.ReverseList2(node1);
        Assert.AreEqual(GetNodeString(newHead), "1");
    }

    // 03.輸入NULL
    [TestMethod]
    public void ReverseTest3()
    {
        Node newHead = ListHelper.ReverseList2(null);
        Assert.AreEqual(GetNodeString(newHead), null);
    } 

3.2 測試結果

  (1)測試通過情況

  (2)代碼覆蓋率

 


免責聲明!

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



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