學習筆記之使用前序遍歷和中序遍歷構造二叉樹
總結一下學習筆記:
一、提出問題
給出一棵樹的前序遍歷和中序遍歷,構造二叉樹,你可以假設這棵樹中不存在重複的數。
例如:給出前序和中序遍歷序列:preorder = [3,9,20,15,7],inorder=[9,3,15,20,7] 得到如下所示的二叉樹:
二、解決方法
如何去遍歷這棵樹? 通常有兩種方式去遍歷,即Breadth First Search(BFS):廣度優先搜尋、Depth First Search(DFS):深度優先搜尋。
三、概念區分
在解釋BFS和DFS概念之前,先來區分一下圖和樹的區別。 1、什麼是圖? 答: 圖由頂點(vertex,node)和邊(edge)組成。頂點就是代表物件,因為有時候題不會直接給頂點,而是某種物件,直接看成頂點即可。我們使用連結兩頂點之間的線段來表示。頂點的集合V,邊的集合是E,所以圖記為G = (V,E), 連線兩點u和v的邊用e=(u,v)表示。
2、什麼是樹? 答:樹也是由一組頂點(vertex)以及若干條邊(edge)組成。類似於一般的圖,區別在於:樹是連通的無環圖。
3、圖的遍歷和樹的遍歷 圖的遍歷:包括廣度優先遍歷(BFS)、深度優先遍歷DFS。 樹的遍歷:包括前序、中序、後序、層次遍歷等。
圖的遍歷:從圖的某一點出發訪問其餘頂點,且使得每一個頂點僅僅被訪問一次,這過程叫圖的遍歷。
樹的遍歷: 1)、前序、中序、後序遍歷,其實都採用的是DFS思想,DFS思想常用遞迴實現。 2)、層次遍歷,採用的是BFS思想,採用佇列這種資料結構實現。
四、演算法思想
1、BFS演算法思想 類似於樹的層次遍歷,遍歷順序從樹的頂端一直遍歷到底部,頂部的節點比底部的節點要優先被訪問到。
2、DFS演算法思想 類似於樹的前、中、後序遍歷,這個策略中,我們採用深度優先作為優先順序,即沿著根節點一直遍歷到某個葉節點,然後再返回根節點,訪問其他的分支。根據根節點、左節點、右節點的順序,DFS可以分為前序、中序、後序遍歷。
下面四幅圖表示了DFS和BFS的訪問順序。 從左到右依次解釋: 下圖一:DFS的後序遍歷,節點訪問順序(L—>R—>V),即先訪問左孩子,再訪問右孩子、最後訪問父節點,訪問順序:(1,2,3,4,5)。 下圖二:DFS的前序遍歷,節點訪問順序(V—>L—>R),即先訪問父節點,再訪問左孩子,最後訪問右孩子,訪問順序(1,2,3,4,5)。 下圖三:DFS的中序遍歷,節點訪問順序(L—>V—>R),即先訪問左孩子,再訪問父節點,最後訪問右孩子,訪問順序(1,2,3,4,5)。 下圖四:BFS的層次遍歷,訪問順序:從左到右,從上到下依次訪問,訪問順序(1,2,3,4,5)。
下面介紹由前序遍歷和中序遍歷構造一棵二叉樹的實現方式。
五、程式碼實現
1、動畫展示實現思想:
2、程式碼實現: 1)、Java版本
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}
import javafx.util.Pair;
class Solution {
public Pair<TreeNode, int []> helper(int[] preorder, int[] inorder) {
if (inorder.length == 0) {
return new Pair(null, preorder);
}
// pick up the first element as a root
int root_val = preorder[0];
TreeNode root = new TreeNode(root_val);
// find index of root in the inorder list
int index = 0;
for (; (index < inorder.length) && (inorder[index] != root_val); index++){}
preorder = Arrays.copyOfRange(preorder, 1, preorder.length);
// root splits inorder list
// into left and right subtrees
int [] left_inorder = Arrays.copyOfRange(inorder, 0, index);
int [] right_inorder = index + 1 <= inorder.length ?
Arrays.copyOfRange(inorder, index + 1, inorder.length) : new int [0];
// recursion
Pair<TreeNode, int []> p = helper(preorder, left_inorder);
root.left = p.getKey();
preorder = p.getValue();
p = helper(preorder, right_inorder);
root.right = p.getKey();
preorder = p.getValue();
return new Pair(root, preorder);
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
Pair<TreeNode, int []> result = helper(preorder, inorder);
return result.getKey();
}
}
2)、Python版本
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
from collections import deque
class Solution:
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
def helper(preorder, inorder):
if not inorder:
return None
# pick up the first element as a root
root_val = preorder.popleft()
root = TreeNode(root_val)
# root splits inorder list
# into left and right subtrees
index = inorder.index(root_val)
# recursion
root.left= helper(preorder, inorder[:index])
root.right = helper(preorder, inorder[index + 1:])
return root
return helper(deque(preorder), inorder)
3)、C++實現版本
#include "stdafx.h"
#include<vector>
#include<iostream>
#include<string>
using namespace std;
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) :val(x), left(NULL), right(NULL) {}
};
class Solution
{
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
return builTreeHelper(preorder, 0, preorder.size(), inorder, 0, inorder.size());
}
TreeNode* builTreeHelper(vector<int>& preorder, int sp, int ep, vector<int>& inorder, int si, int ei) {
if (sp == ep) return nullptr;
TreeNode* root = new TreeNode(preorder[sp]);
int dis = find(inorder.begin() + si, inorder.begin() + ei, preorder[sp]) - inorder.begin() - si;
root->left = builTreeHelper(preorder, sp + 1, sp + 1 + dis, inorder, si, si + dis);
root->right = builTreeHelper(preorder, sp + 1 + dis, ep, inorder, si + dis + 1, ei);
return root;
}
};
int main()
{
//可自行新增測試程式碼
return 0;
}
六、演算法複雜度分析
1、時間複雜度:O(N) 2、空間複雜度:O(N)