劍指 Offer 36. 二叉搜尋樹與雙向連結串列
輸入一棵二叉搜尋樹,將該二叉搜尋樹轉換成一個排序的迴圈雙向連結串列。要求不能建立任何新的節點,只能調整樹中節點指標的指向。
為了讓您更好地理解問題,以下面的二叉搜尋樹為例:
我們希望將這個二叉搜尋樹轉化為雙向迴圈連結串列。連結串列中的每個節點都有一個前驅和後繼指標。對於雙向迴圈連結串列,第一個節點的前驅是最後一個節點,最後一個節點的後繼是第一個節點。
下圖展示了上面的二叉搜尋樹轉化成的連結串列。“head” 表示指向連結串列中有最小元素的節點。
特別地,我們希望可以就地完成轉換操作。當轉化完成以後,樹中節點的左指標需要指向前驅,樹中節點的右指標需要指向後繼。還需要返回連結串列中的第一個節點的指標。
解題思路:
做這一道題之前,先理解一下,中序遍歷的思路。
中序遍歷是對二叉樹作“左、根、右”進行順序遍歷。
void dfs(Node root) {
//列印中序遍歷
dfs(root.left);//列印左邊
System.out.println(root.val);//列印根
dfs(root.right);//列印右邊
}
演算法流程:
dfs(cur):
遞迴法中序遍歷;
- 終止條件,當cur等於null的時候,直接return
- 遞迴cur.left,即遞迴左子樹
- 構建連結串列:
- pre為空的時候,head節點賦值為cur
- pre不為空的時候,則pre.right=cur,cur.left=pre
- 最後pre=cur,即節點cur是pre的後繼節點
- 遞迴cur.right,即遞迴右子樹
treeToDoublyList(root):
- 特殊情況: 如果根節點root為空,則直接返回null
- 轉化為雙鏈表:遞迴root
- 尾結點pre.right=head,頭節點head.left=pre。即head指向頭節點,pre指向尾結點,修改其引用即可。
- 最後返回head節點
註釋版程式碼如下:
class Solution {
Node head, pre;
public Node treeToDoublyList(Node root) {
if(root==null) return null;
dfs(root);
pre.right = head;
head.left =pre;//進行頭節點和尾節點的相互指向,這兩句的順序也是可以顛倒的
return head;
}
public void dfs(Node cur){
if(cur==null) return;
dfs(cur.left);
//pre用於記錄雙向連結串列中位於cur左側的節點,即上一次迭代中的cur,當pre==null時,cur左側沒有節點,即此時cur為雙向連結串列中的頭節點
if(pre==null) head = cur;
//反之,pre!=null時,cur左側存在節點pre,需要進行pre.right=cur的操作。
else pre.right = cur;
cur.left = pre;//pre是否為null對這句沒有影響,且這句放在上面兩句if else之前也是可以的。
pre = cur;//pre指向當前的cur
dfs(cur.right);//全部迭代完成後,pre指向雙向連結串列中的尾節點
}
}
k神簡潔程式碼如下:
class Solution {
Node pre, head;
public Node treeToDoublyList(Node root) {
if (root == null) return null;
dfs(root);
head.left = pre;
pre.right = head;
return head;
}
void dfs(Node cur) {
if (cur == null) return;
dfs(cur.left);
if (pre == null) head = cur;
else pre.right = cur;
cur.left = pre;
pre = cur;
dfs(cur.right);
}
}
力友程式碼可參考,是線上索化二叉樹的基礎上,對head節點加上前驅,對最後一個節點加上後繼的程式碼:
class Solution {
private Node pre = null;
private Node head = null;
public Node treeToDoublyList(Node root) {
if (root==null){
return null;
}
/*獲得head節點,此時最後一個節點並沒有和head節點連上
* ,但是head.left=最後一個節點*/
Node node = treadTree(root);
/*獲得最後一個節點*/
Node left = node.left;
/*最後一個節點的後繼等於head節點*/
left.right = node;
return node;
}
public Node treadTree(Node root){
if (root == null) {
return null;
}
/*將左子樹的節點化為雙向連結串列*/
treadTree(root.left);
/*將節點的左節點置為前驅節點*/
root.left = pre;
/*當前驅節點不為空時,將前驅節點的右子樹置為當前節點(即設定上一個節點的後繼節點)*/
if (pre != null) {
pre.right = root;
}else {
/*當前驅節點為空時,那麼就是第一個節點,讓head指向第一個節點*/
head=root;
}
/*讓head節點的左子樹(前驅節點)指向當前節點,遞迴到最後指向最後一個節點*/
head.left=root;
/*將前驅節點置為當前節點*/
pre = root;
/*將右子樹的節點化為雙向連結串列*/
treadTree(root.right);
return head;
}
}
參考連結:
劍指 Offer 36. 二叉搜尋樹與雙向連結串列(中序遍歷,清晰圖解) - 二叉搜尋樹與雙向連結串列 - 力扣(LeetCode) (leetcode-cn.com)