Love is the one thing that transcends time and space. node
只有愛能夠穿越時空。數組
返回與給定先序遍歷相匹配的二叉搜索樹的根結點。數據結構
示例:ide
輸入:[8,5,1,7,10,12]ui
輸出:[8,5,10,1,7,null,12]spa
問題分析3d
咱們知道先序遍歷的順序是:根節點→左子樹→右子樹。二叉搜索樹的特色是當前節點左子樹的值都小於當前節點,當前節點右子樹的值都大於當前節點。好比咱們在下面的搜索二叉樹中插入節點4blog
原理很簡單,咱們來看下若是插入一個結點的時候代碼該怎麼寫遞歸
1//data是插入的結點
2private static void addTreeNode(TreeNode root, int data) {
3 TreeNode node = new TreeNode(data);
4 TreeNode p = root;
5 while (true) {
6 //若是要插入的結點data比結點p的值小,就往p結點的左
7 //子節點找,不然往p的右子節點找
8 if (p.val > data) {
9 //若是p的左子節點等於空,直接放進去
10 if (p.left == null) {
11 p.left = node;
12 break;
13 } else {
14 p = p.left;
15 }
16 } else {
17 //若是p的右子節點等於空,直接放進去
18 if (p.right == null) {
19 p.right = node;
20 break;
21 } else {
22 p = p.right;
23 }
24 }
25 }
26}
上面代碼很簡單,插入一個結點的代碼寫出來了,咱們只須要把數組中的元素所有遍歷一遍而後再一個個插入便可,代碼以下get
1public TreeNode bstFromPreorder(int[] preorder) {
2 TreeNode root = new TreeNode();
3 root.val = preorder[0];
4 for (int i = 1; i < preorder.length; i++)
5 addTreeNode(root, preorder[i]);
6 return root;
7}
遞歸方式
上面節點插入的時候咱們使用的是while循環的方式,這種比較容易理解,但代碼量相對比較多,咱們還能夠改成遞歸的方式
1private TreeNode addTreeNode(TreeNode root, int val) {
2 if (root == null)
3 return new TreeNode(val);
4 else if (root.val > val)
5 root.left = addTreeNode(root.left, val);
6 else
7 root.right = addTreeNode(root.right, val);
8 return root;
9}
這種遞歸的方式代碼會更簡潔一些。若是root爲空的話會新建一個節點。不然會一直走下去,他會根據root節點的大小判斷往左走仍是往右走,注意這裏的root節點不必定是根節點,在遞歸的時候他是一直變的。
二分法構造
咱們知道輸入的數據是二叉樹的先序遍歷,那麼第一個節點確定是頭結點,比他小的是他左子樹的節點值,比他大的是他右子樹的節點值,咱們就拿上面的[8,5,1,7,10,12]來講,8是根節點,比8小的[5,1,7]是他左子樹上的值,比他大的[10,12]是他右子樹上的值。因此能夠參照二分法查找的方式,把數組分爲兩部分,他是這樣的
而後左邊的[5,1,7]咱們再按照上面的方式拆分,5是根節點,比5小的1是左子節點,比5大的7是右子節點。同理右邊的[10,12]中10是根節點,比10大的12是右子節點,這樣咱們一直拆分下去,直到不能拆分爲止,因此結果是下面這樣
咱們再來看下代碼
1public TreeNode bstFromPreorder(int[] preorder) {
2 return buildBST(preorder, 0, preorder.length - 1);
3}
4
5//數組的範圍從left到right
6private TreeNode buildBST(int[] preorder, int left, int right) {
7 if (left > right)
8 return null;
9 TreeNode root = new TreeNode(preorder[left]);
10 //若是left==right說明只有一個元素,無法再拆分了
11 if (left == right)
12 return root;
13 int i = left;
14 //拆分爲兩部分,一部分是比preorder[left]大的,一部分是比preorder[left]小的
15 while (i + 1 <= right && preorder[i + 1] < preorder[left])
16 i++;
17 //區間[left + 1,i]全部元素都在root節點的左子樹
18 //區間[i + 1,right]全部元素都在root節點的右子樹
19 root.left = buildBST(preorder, left + 1, i);
20 root.right = buildBST(preorder, i + 1, right);
21 return root;
22}
先序遍歷
咱們還能夠參照先序遍歷的方式把數組元素一個個取出來,也很好理解,直接上代碼。
1int index = 0;
2
3public TreeNode bstFromPreorder(int[] preorder) {
4 return bstFromPreorder(preorder, Integer.MAX_VALUE);
5}
6
7public TreeNode bstFromPreorder(int[] preorder, int max) {
8 if (index == preorder.length || preorder[index] > max)
9 return null;
10 //把數組中的元素一個個取出來建立節點
11 TreeNode root = new TreeNode(preorder[index++]);
12 //左子樹的最大值不能超過root.val
13 root.left = bstFromPreorder(preorder, root.val);
14 //右子樹的最大值不能超過max
15 root.right = bstFromPreorder(preorder, max);
16 return root;
17}
使用棧來解決
這題解法比較多,再來看最後一種解題思路。咱們還可使用一個棧來維護二叉搜索樹中的節點,棧中存放的是已經構建好的二叉搜索樹的結點(但不是所有,有些可能已經出棧了),其中棧中元素從棧底到棧頂是遞減的,咱們遍歷數組的時候若是當前值小於棧頂元素的值,咱們直接讓當前值成爲棧頂元素節點的左子節點,而後壓棧。
1 if (preorder[i] < stack.peek().val) {
2 stack.peek().left = node;
3 }
若是當前元素的值大於棧頂元素的值,咱們就讓棧頂元素出棧,直到當前元素的值小於棧頂元素的值爲止(或者棧爲空爲止)。而前一個比當前元素值小的節點就是當前元素的父節點。而當前元素是他父節點的右子節點。
1 TreeNode parent = stack.peek();
2 //棧從棧底到棧頂是遞減的
3 while (!stack.isEmpty() && preorder[i] > stack.peek().val) {
4 parent = stack.pop();
5 }
6 parent.right = node;
解惑:
這裏若是思路不是很清晰的可能會有點疑問,出棧的時候把小於當前元素的值出棧了,若是再遇到比出棧的元素還要小的值那不是完蛋了,由於那個值已經出棧了,找不到了。其實有這個想法是正確的,但這種想法有點多餘了,咱們就拿下面的圖來講吧[8,5,1,7,10,12]
好比當咱們插入節點7的時候,節點1,5都已經所有出棧,但7後面不管如何都不會再出現比1或者5還小的值了,由於他是二叉搜索樹,5的右節點的全部值都是比5大的。咱們來畫個簡單的圖看下
因此咱們看到後面不管走到哪一步都不可能在遇到比出棧元素更小的值了,最後咱們再來看下完整代碼
1public TreeNode bstFromPreorder(int[] preorder) {
2 Stack<TreeNode> stack = new Stack<>();
3 TreeNode root = new TreeNode(preorder[0]);
4 stack.push(root);
5 for (int i = 1; i < preorder.length; i++) {
6 TreeNode node = new TreeNode(preorder[i]);
7 //小於棧頂元素的值,說明應該在棧頂元素的左子樹
8 if (preorder[i] < stack.peek().val) {
9 stack.peek().left = node;
10 } else {//大於棧頂元素的值,咱們要找到當前元素的父節點
11 TreeNode parent = stack.peek();
12 //棧從棧底到棧頂是遞減的
13 while (!stack.isEmpty() && preorder[i] > stack.peek().val) {
14 parent = stack.pop();
15 }
16 parent.right = node;
17 }
18 //節點壓棧
19 stack.push(node);
20 }
21 return root;
22}
長按上圖,識別圖中二維碼以後便可關注。
若是喜歡這篇文章就點個"在看"吧