輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列(劍指offer)
阿新 • • 發佈:2018-12-08
題目
輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。
分析:
在二叉搜尋樹中,每個結點都有兩個分別指向其左、右子樹的指標,左子樹結點的值總是小於父結點的值,右子樹結點的值總是大於父結點的值。在雙向連結串列中,每個結點也有兩個指標,它們分別指向前一個結點和後一個結點。所以這兩種資料結構的結點是一致,二叉搜尋樹和雙向連結串列,只是因為兩個指標的指向不同而已
舉個例子,如下圖中的二叉搜尋樹,轉換後的雙向連結串列為:
為了減少指標的變換次數,並讓操作更加簡單,在轉換成排序雙向連結串列時,原先指向左子結點的指標調整為連結串列中指向前一個結點的指標,原先指向右子結點的指標調整為連結串列中指向下一個結點的指標。
由於要求連結串列是有序的,可以藉助二叉樹中序遍歷,因為中序遍歷演算法的特點就是從小到大訪問結點。當遍歷訪問到根結點時,假設根結點的左側已經處理好,只需將根結點與上次訪問的最近結點(左子樹中最大值結點)的指標連線好即可。進而更新當前連結串列的最後一個結點指標。同時中序遍歷過程正好是轉換成連結串列的過程,可採用遞迴方法處理。
法一:遞迴版本
(不理解中序遍歷的遞迴與非遞迴可以看我之前的文章)
public class Convert {
// 雙向連結串列的左邊頭結點和右邊頭節點
TreeNode leftHead = null;
TreeNode rightHead = null;
public TreeNode convert(TreeNode pRootOfTree){
// 遞迴呼叫葉子節點的左右節點返回null
if (pRootOfTree == null)
return null;
// 第一次執行時,它會使最左邊葉子節點為連結串列第一個節點
convert(pRootOfTree. left);
if (rightHead == null) {
leftHead = pRootOfTree;
rightHead = pRootOfTree;
} else {
// 把根節點插入到雙向連結串列右邊,rightHead向後移動
rightHead.right = pRootOfTree;
pRootOfTree.left = rightHead;
rightHead = pRootOfTree;
}
// 把右葉子節點也插入到雙向連結串列(rightHead已確定,直接插入)
convert(pRootOfTree.right) ;
// 返回左邊頭結點
return leftHead;
}
}
法二:非遞迴版本
1.核心是中序遍歷的非遞迴演算法。(不理解中序遍歷的遞迴與非遞迴可以看我之前的文章)
2.修改當前遍歷節點與前一遍歷節點的指標指向。
public TreeNode ConvertBSTToBiList(TreeNode root) {
if(root==null)
return null;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode p = root;
TreeNode pre = null;// 用於儲存中序遍歷序列的上一節點
boolean isFirst = true;
while(p!=null||!stack.isEmpty()){
while(p!=null){
stack.push(p);
p = p.left;
}
p = stack.pop();
if(isFirst){
root = p;// 將中序遍歷序列中的第一個節點記為root
pre = root;
isFirst = false;
}else{
pre.right = p;
p.left = pre;
pre = p;
}
p = p.right;
}
return root;
}