【劍指Offer學習】【面試題27:二叉搜尋樹與雙向連結串列】
阿新 • • 發佈:2019-01-07
題目:輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。
比如輸入圖4.12 中左邊的二叉搜尋樹,則輸出轉換之後的排序現向連結串列。
結點定義:
public static class BinaryTreeNode {
int value;
BinaryTreeNode left;
BinaryTreeNode right;
}
解題思路:
在二叉樹中,每個結點都有兩個指向子結點的指標。在雙向連結串列中,每個結點也有兩個指標,它們分別指向前一個結點和後一個結點。由於這兩種結點的結構相似,同時二叉搜尋樹也是一種排序的資料結構,因此在理論上有可能實現二叉搜尋樹和排序的雙向連結串列的轉換。在搜尋二叉樹中,左子結點的值總是小於父結點的值,右子結點的值總是大於父結點的值。因此我們在轉換成排序雙向連結串列時,原先指向左子結點的指標調整為連結串列中指向前一個結點的指標,原先指向右子結點的指標調整為連結串列中指向後一個結點指標。接下來我們考慮該如何轉換。
由於要求轉換之後的連結串列是排好序的,我們可以中序遍歷樹中的每一個結點, 這是因為中序遍歷演算法的特點是按照從小到大的順序遍歷二叉樹的每一個結點。當遍歷到根結點的時候,我們把樹看成三部分:值為10 的結點、根結點值為6 的左子樹、根結點值為1 4 的右子樹。根據排序連結串列的定義,值為10 的結點將和它的左子樹的最大一個結點(即值為8 的結點)連結起來,同時它還將和右子樹最小的結點(即值為12 的結點)連結起來,如圖4.13 所示。
按照中序遍歷的順序, 當我們遍歷轉換到根結點(值為10 的結點)時,它的左子樹已經轉換成一個排序的連結串列了, 並且處在連結串列中的最後一個結點是當前值最大的結點。我們把值為8 的結點和根結點連結起來,此時連結串列中的最後一個結點就是10 了。接著我們去地歷轉換右子樹, 並把根結點和右子樹中最小的結點連結起來。至於怎麼去轉換它的左子樹和右子樹,由於遍歷和轉換過程是一樣的,我們很自然地想到可以用遞迴。
程式碼實現:
public class Test27 {
/**
* 二叉樹的樹結點
*/
public static class BinaryTreeNode {
int value;
BinaryTreeNode left;
BinaryTreeNode right;
}
/**
* 題目:輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。
* 要求不能建立任何新的結點,只能調整樹中結點指標的指向。
*
* @param root 二叉樹的根結點
* @return 雙向連結串列的頭結點
*/
public static BinaryTreeNode convert(BinaryTreeNode root) {
// 用於儲存處理過程中的雙向連結串列的尾結點
BinaryTreeNode[] lastNode = new BinaryTreeNode[1];
convertNode(root, lastNode);
// 找到雙向連結串列的頭結點
BinaryTreeNode head = 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];
// 如果左子樹轉換成的雙向連結串列不為空,設定尾結點的後繼
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();
}
}