总结一下学习笔记:
一、提出问题
给出一棵树的前序遍历和中序遍历,构造二叉树,你可以假设这棵树中不存在重复的数。
例如:给出前序和中序遍历序列: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)