題目:輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能建立任何新的結點,只能調整樹中結點指針的指向。好比輸入下圖中左邊的二叉搜索樹,則輸出轉換以後的排序雙向鏈表。node
二叉搜索樹的節點定義以下,這裏使用C#語言描述:算法
public class BinaryTreeNode { public int Data { get; set; } public BinaryTreeNode leftChild { get; set; } public BinaryTreeNode rightChild { get; set; } public BinaryTreeNode(int data) { this.Data = data; } public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right) { this.Data = data; this.leftChild = left; this.rightChild = right; } }
首先,咱們知道:在二叉樹中,每一個結點都有兩個指向子結點的指針。在雙向鏈表中,每一個結點也有兩個指針,它們分別指向前一個結點和後一個結點。ide
其次,因爲要求轉換以後的鏈表是排好序的,咱們能夠中序遍歷樹中的每個結點,這是由於中序遍歷算法的特色是按照從小到大的順序遍歷二叉樹的每個結點。單元測試
最後,按照中序遍歷的順序,當咱們遍歷轉換到根結點(值爲10的結點)時,它的左子樹已經轉換成一個排序的鏈表了,而且處在鏈表中的最後一個結點是當前值最大的結點。咱們把值爲8的結點和根結點連接起來,此時鏈表中的最後一個結點就是10了。接着咱們去遍歷轉換右子樹,並把根結點和右子樹中最小的結點連接起來。測試
很明顯,轉換它的左子樹和右子樹,因爲遍歷和轉換過程是同樣的,很天然地想到能夠用遞歸。this
public BinaryTreeNode Convert(BinaryTreeNode root) { BinaryTreeNode lastNodeInList = null; ConvertNode(root, ref lastNodeInList); // lastNodeInList指向雙向鏈表的尾結點, // 咱們須要返回頭結點 BinaryTreeNode headInList = lastNodeInList; while (headInList != null && headInList.leftChild != null) { headInList = headInList.leftChild; } return headInList; } private void ConvertNode(BinaryTreeNode node, ref BinaryTreeNode lastNodeInList) { if (node == null) { return; } BinaryTreeNode currentNode = node; // 轉換左子樹 if (currentNode.leftChild != null) { ConvertNode(currentNode.leftChild, ref lastNodeInList); } // 與根節點的銜接 currentNode.leftChild = lastNodeInList; if (lastNodeInList != null) { lastNodeInList.rightChild = currentNode; } lastNodeInList = currentNode; // 轉換右子樹 if (currentNode.rightChild != null) { ConvertNode(currentNode.rightChild, ref lastNodeInList); } }
(1)輔助方法的封裝spa
private void SetSubTreeNode(BinaryTreeNode root, BinaryTreeNode lChild, BinaryTreeNode rChild) { if (root == null) { return; } root.leftChild = lChild; root.rightChild = rChild; } private BSTConverter converter; [TestInitialize] public void Initialize() { converter = new BSTConverter(); } [TestCleanup] public void CleanUp() { converter = null; }
(2)功能測試、特殊輸入測試指針
// 10 // / \ // 6 14 // /\ /\ // 4 8 12 16 [TestMethod] public void ConvertTest1() { BinaryTreeNode node10 = new BinaryTreeNode(10); BinaryTreeNode node6 = new BinaryTreeNode(6); BinaryTreeNode node4 = new BinaryTreeNode(4); BinaryTreeNode node8 = new BinaryTreeNode(8); BinaryTreeNode node14 = new BinaryTreeNode(14); BinaryTreeNode node12 = new BinaryTreeNode(12); BinaryTreeNode node16 = new BinaryTreeNode(16); SetSubTreeNode(node10, node6, node14); SetSubTreeNode(node6, node4, node8); SetSubTreeNode(node14, node12, node16); BinaryTreeNode result = converter.Convert(node10); Assert.AreEqual(result, node4); } // 5 // / // 4 // / // 3 // / // 2 // / // 1 [TestMethod] public void ConvertTest2() { BinaryTreeNode node5 = new BinaryTreeNode(5); BinaryTreeNode node4 = new BinaryTreeNode(4); BinaryTreeNode node3 = new BinaryTreeNode(3); BinaryTreeNode node2 = new BinaryTreeNode(2); BinaryTreeNode node1 = new BinaryTreeNode(1); node5.leftChild = node4; node4.leftChild = node3; node3.leftChild = node2; node2.leftChild = node1; BinaryTreeNode result = converter.Convert(node5); Assert.AreEqual(result, node1); } // 1 // \ // 2 // \ // 3 // \ // 4 // \ // 5 [TestMethod] public void ConvertTest3() { BinaryTreeNode node5 = new BinaryTreeNode(5); BinaryTreeNode node4 = new BinaryTreeNode(4); BinaryTreeNode node3 = new BinaryTreeNode(3); BinaryTreeNode node2 = new BinaryTreeNode(2); BinaryTreeNode node1 = new BinaryTreeNode(1); node1.rightChild = node2; node2.rightChild = node3; node3.rightChild = node4; node4.rightChild = node5; BinaryTreeNode result = converter.Convert(node1); Assert.AreEqual(result, node1); } // 樹中只有1個結點 [TestMethod] public void ConvertTest4() { BinaryTreeNode node1 = new BinaryTreeNode(1); BinaryTreeNode result = converter.Convert(node1); Assert.AreEqual(result, node1); } // 空指針 [TestMethod] public void ConvertTest5() { BinaryTreeNode result = converter.Convert(null); Assert.AreEqual(result, null); }
(1)測試經過狀況code
(2)代碼覆蓋率blog