題目:輸入一顆二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能建立新的結點,只能調整樹中結點指針的指向。node
好比以下圖中的二叉搜索樹,則輸出轉換以後的排序雙向鏈表爲:算法
在二叉樹中,每一個結點都有兩個指向子節點的指針。在雙向鏈表中,每一個 結點也有兩個指針,他們分別指向前一個結點和後一個結點。因爲這兩種結點的結構類似,同時二叉搜索樹也是一種排序的數據結構,所以在理論上有可能實現二叉 搜索樹和排序的雙向鏈表的轉換。在搜索二叉樹中,左子結點的值老是小於父節點的值,右子節點的值老是大於父節點的值。所以咱們在轉換稱排序的雙向鏈表時, 原先指向的左子結點的指針調整爲鏈表中指向前一個結點的指針,原先指向右子節點的指針調整爲倆表爲指向後一個結點的指針。接下來咱們考慮如何轉化。數組
因爲要求轉換以後的鏈表是排好序的,咱們可疑中序遍歷樹中的每個結 點,這是由於中序遍歷算法的特色是按照從小到達的順序遍歷二叉樹的每個結點。當遍歷到根節點的時候,咱們把樹堪稱三部分:值爲10的結點,根節點爲6的 左子樹、根節點爲14的右子樹。根據排序鏈表的定義,值爲10的結點將和它的左子樹的最大的一個結點(即值爲8的結點)連接起來,同時它還將和右子樹最小 的結點(即值爲12的結點)連接起來,如圖:數據結構
按照中序遍歷的順序,當咱們遍歷轉換到根節點(值爲10的結點)時, 它的左子樹已經轉換成一個排序的鏈表了,而且處在鏈表中的最後一個結點是當前值的最大的結點。咱們把值爲8的結點根節點連接起來,此時鏈表中的最後一個結 點是10了。接着咱們去遍歷轉換右子樹,並把根節點和右子樹最小的結點連接起來。至於怎麼去轉換它的左子樹和右子樹,因爲遍歷和轉換過程是同樣的,咱們自 然的想到了遞歸。spa
Java代碼實現:.net
package cglib;指針
class BinaryTreeNode
{排序
int value;遞歸
BinaryTreeNode left ;get
BinaryTreeNode right ;
}
public class DeleteNode{
/**
* 題目:輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。
* 要求不能建立任何新的結點,只能調整樹中結點指針的指向。
*
* @param root 二叉樹的根結點
* @return 雙向鏈表的頭結點
*/
public static BinaryTreeNode convert(BinaryTreeNode root) {
// 用於保存處理過程當中的雙向鏈表的尾結點
BinaryTreeNode[] lastNode = new BinaryTreeNode[1];
convertNode(root, lastNode);
// 找到雙向鏈表的頭結點
BinaryTreeNode head = lastNode[0];
System.out.println("lastNode[0]="+lastNode[0]);
while (head != null && head.left != null) {
head = head.left;
}
return head;
}
/**
* 鏈表轉換操做
*
* @param node 當前的根結點
* @param lastNode 已經處理好的雙向鏈表的尾結點,使用一個長度爲1的數組,相似C++中的二級指針
*/
public static void convertNode(BinaryTreeNode node, BinaryTreeNode[] lastNode) {
// 結點不爲空
if (node != null) {
// 若是有左子樹就先處理左子樹
if (node.left != null) {
convertNode(node.left, lastNode);
}
//原先指向左子結點的指針調整爲鏈表中指向前一個結點的指針,原先指向右子結點的指針調整爲鏈表中指向下一個結點的指針
// 將當前結點的前驅指向已經處理好的雙向鏈表(由當前結點的左子樹構成)的尾結點
node.left = lastNode[0];//4<->6<->8<->10<->12<->14<->16,10的前指針是6,後指針是10,6的前指針是4,後指針是8
// 若是左子樹轉換成的雙向鏈表不爲空,設置尾結點的後繼
if (lastNode[0] != null) {
lastNode[0].right = node;// 將根節點和左子樹轉換後的雙向有序鏈表的最後一個節點鏈接
}
// 記錄當前結點爲尾結點
lastNode[0] = node;
// 處理右子樹
if (node.right != null) {
convertNode(node.right, lastNode);
}
}
}
public static void main(String[] args) {
test01();
test02();
test03();
test04();
test05();
}
private static void printList(BinaryTreeNode head) {
while (head != null) {
System.out.print(head.value + "->");
head = head.right;
}
System.out.println("null");
}
private static void printTree(BinaryTreeNode root) {
if (root != null) {//遞歸中序遍歷先左子樹,而後根節點,最後右子樹
printTree(root.left);
System.out.print(root.value + "->");
printTree(root.right);
}
}
// 10
// / \
// 6 14
// /\ /\
// 4 8 12 16
private static void test01() {
BinaryTreeNode node10 = new BinaryTreeNode();
node10.value = 10;
BinaryTreeNode node6 = new BinaryTreeNode();
node6.value = 6;
BinaryTreeNode node14 = new BinaryTreeNode();
node14.value = 14;
BinaryTreeNode node4 = new BinaryTreeNode();
node4.value = 4;
BinaryTreeNode node8 = new BinaryTreeNode();
node8.value = 8;
BinaryTreeNode node12 = new BinaryTreeNode();
node12.value = 12;
BinaryTreeNode node16 = new BinaryTreeNode();
node16.value = 16;
node10.left = node6;
node10.right = node14;
node6.left = node4;
node6.right = node8;
node14.left = node12;
node14.right = node16;
System.out.print("Before convert中序遍歷: ");
printTree(node10);
System.out.println("null");
BinaryTreeNode head = convert(node10);
System.out.print("After convert : ");
printList(head);
System.out.println();
}
// 5
// /
// 4
// /
// 3
// /
// 2
// /
// 1
private static void test02() {
BinaryTreeNode node1 = new BinaryTreeNode();
node1.value = 1;
BinaryTreeNode node2 = new BinaryTreeNode();
node2.value = 2;
BinaryTreeNode node3 = new BinaryTreeNode();
node3.value = 3;
BinaryTreeNode node4 = new BinaryTreeNode();
node4.value = 4;
BinaryTreeNode node5 = new BinaryTreeNode();
node5.value = 5;
node5.left = node4;
node4.left = node3;
node3.left = node2;
node2.left = node1;
System.out.print("Before convert: ");
printTree(node5);
System.out.println("null");
BinaryTreeNode head = convert(node5);
System.out.print("After convert : ");
printList(head);
System.out.println();
}
// 1
// \
// 2
// \
// 3
// \
// 4
// \
// 5
private static void test03() {
BinaryTreeNode node1 = new BinaryTreeNode();
node1.value = 1;
BinaryTreeNode node2 = new BinaryTreeNode();
node2.value = 2;
BinaryTreeNode node3 = new BinaryTreeNode();
node3.value = 3;
BinaryTreeNode node4 = new BinaryTreeNode();
node4.value = 4;
BinaryTreeNode node5 = new BinaryTreeNode();
node5.value = 5;
node1.right = node2;
node2.right = node3;
node3.right = node4;
node4.right = node5;
System.out.print("Before convert: ");
printTree(node1);
System.out.println("null");
BinaryTreeNode head = convert(node1);
System.out.print("After convert : ");
printList(head);
System.out.println();
}
// 只有一個結點
private static void test04() {
BinaryTreeNode node1 = new BinaryTreeNode();
node1.value = 1;
System.out.print("Before convert: ");
printTree(node1);
System.out.println("null");
BinaryTreeNode head = convert(node1);
System.out.print("After convert : ");
printList(head);
System.out.println();
}
// 沒有結點
private static void test05() {
System.out.print("Before convert: ");
printTree(null);
System.out.println("null");
BinaryTreeNode head = convert(null);
System.out.print("After convert : ");
printList(head);
System.out.println();
}
}
輸出:
Before convert中序遍歷: 4->6->8->10->12->14->16->null
lastNode[0]=cglib.BinaryTreeNode@139a55
After convert : 4->6->8->10->12->14->16->null
Before convert: 1->2->3->4->5->null
lastNode[0]=cglib.BinaryTreeNode@1db9742
After convert : 1->2->3->4->5->null
Before convert: 1->2->3->4->5->null
lastNode[0]=cglib.BinaryTreeNode@106d69c
After convert : 1->2->3->4->5->null
Before convert: 1->null
lastNode[0]=cglib.BinaryTreeNode@52e922
After convert : 1->null
Before convert: null
lastNode[0]=null
After convert : null
--------------------------------------------------------
思路更清晰的:
若是左子樹爲空,對應雙向有序鏈表的第一個節點是根節點,左邊不須要其餘操做;
若是左子樹不爲空,轉換左子樹,二叉查找樹對應雙向有序鏈表的第一個節點就是左子樹轉換後雙向有序鏈表的第一個節點,同時將根節點和左子樹轉換後的雙向有序鏈 表的最後一個節點鏈接;
若是右子樹爲空,對應雙向有序鏈表的最後一個節點是根節點,右邊不須要其餘操做;
若是右子樹不爲空,對應雙向有序鏈表的最後一個節點就是右子樹轉換後雙向有序鏈表的最後一個節點,同時將根節點和右子樹轉換後的雙向有序鏈表的第一個節點連 接。
/****************************************************************************** 參數: pRoot: 二叉查找樹根節點指針 pFirstNode: 轉換後雙向有序鏈表的第一個節點指針 pLastNode: 轉換後雙向有序鏈表的最後一個節點指針 ******************************************************************************/ void Convert(BinaryTreeNode * pRoot, BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode) { BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight; if(pRoot == NULL) { pFirstNode = NULL; pLastNode = NULL; return; } if(pRoot->m_pLeft == NULL) { // 若是左子樹爲空,對應雙向有序鏈表的第一個節點是根節點 pFirstNode = pRoot; } else { Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft); // 二叉查找樹對應雙向有序鏈表的第一個節點就是左子樹轉換後雙向有序鏈表的第一個節點 pFirstNode = pFirstLeft; // 將根節點和左子樹轉換後的雙向有序鏈表的最後一個節點鏈接 pRoot->m_pLeft = pLastLeft; pLastLeft->m_pRight = pRoot; } if(pRoot->m_pRight == NULL) { // 對應雙向有序鏈表的最後一個節點是根節點 pLastNode = pRoot; } else { Convert(pRoot->m_pRight, pFirstRight, pLastRight); // 對應雙向有序鏈表的最後一個節點就是右子樹轉換後雙向有序鏈表的最後一個節點 pLastNode = pLastRight; // 將根節點和右子樹轉換後的雙向有序鏈表的第一個節點鏈接 pRoot->m_pRight = pFirstRight; pFirstRight->m_pRight = pRoot; } return; }