数据结构|二叉树
完成阅读您将会了解二叉树的:
- 概念
- 构建方法
- 基本操作
- C++实现
- Rust实现
1. 概念
二叉树(Binary Tree)是最基本的树形结构,属于图论(Graph Theory)的有向无环图(Directed Acyclic Graph)范畴。一棵完整的二叉树,如图1 [1],以根节点(Root)作为整树访问入口,自上而下按层次不断分支,左分支被称作左子树(Left Sub-tree),右分支被称作右子树(Right Sub-tree)。两个拥有共同母节点的节点被称为兄弟节点(Sibling Nodes)。如一个节点没有下层子节点,则称之为叶节点(Leaf Node)。



2. 构建方法
一般情况下使用链式数据结构来存储一棵二叉树,便于操作,节约空间。通过数组表达或构建二叉树,需要带有空位标记的字符串序列或两种异序遍历序列,而对稀疏树(Sparse Tree)而言,前者会产生较大的空间浪费。但完全二叉树或满二叉树由于具有空间连续性,适合数组表达,如图4[2]。

数组表达时,若下标位置为\([i]\)的节点存在子节点,那么其左右子节点下标为\([2i+1]\)与\([2i+2]\),如非根节点,其母节点下标为\([\lfloor\frac{i-1}{2}\rfloor]\)。
3. 基本操作
| 操作 | 描述 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| CREATE(A) | 从序列构建树 | \(\Theta(n)\) | \(\Theta(n)\) |
| PREORDER() | 前序遍历 | \(\Theta(n)\) | \(\Theta(n)\) |
| INORDER() | 中序遍历 | \(\Theta(n)\) | \(\Theta(n)\) |
| POSTORDER() | 后序遍历 | \(\Theta(n)\) | \(\Theta(n)\) |
| LEVEL() | 层次遍历 | \(\Theta(n)\) | \(\Theta(n)\) |
| DEPTH() | 获取树的深度 | \(\Theta(n)\) | \(\Theta(1)\) |
二叉树的不同遍历方式对应的不同的遍历次序如下。
- 前序遍历(Pre-order Traversal):根节点\(\rightarrow\)左子树\(\rightarrow\)右子树
- 中序遍历(In-order Traversal):左子树\(\rightarrow\)根节点\(\rightarrow\)右子树
- 后序遍历(Post-order Traversal):左子树\(\rightarrow\)右子树\(\rightarrow\)根节点
- 层次遍历(Level Traversal):按层次从左到右
其中,前、中、后序为深度优先遍历,层次遍历为广度优先遍历。
伪代码:从字符串序列创建树辅助函数
变量说明:root \(\rightarrow\) 根节点;strs\(\rightarrow\)树的字符串数组表达;i\(\rightarrow\)当前节点下标;N \(\rightarrow\) 空位标记
伪代码:从字符串序列创建树
变量说明:root \(\rightarrow\) 根节点;strs\(\rightarrow\)树的字符串数组表达
伪代码:前序遍历树
变量说明:root \(\rightarrow\) 根节点
伪代码:中序遍历树
变量说明:root \(\rightarrow\) 根节点
伪代码:后序遍历树
变量说明:root \(\rightarrow\) 根节点
伪代码:层次遍历树
变量说明:root \(\rightarrow\) 根节点
伪代码:获取树深度
变量说明:root \(\rightarrow\) 根节点
4. C++实现
template <class T> struct Node {
T val;
Node *left, *right;
Node(auto val) : val(val), left(nullptr), right(nullptr) {}
Node() : Node(0) {}
};
template <class T> class Bitree {
Node<T> *root;
constexpr size_t _depth(auto *node) const noexcept {
return node ? 1 + std::max(_depth(node->left), _depth(node->right)) : 0;
}
void _Bitree(auto *node, const auto &strs, auto i) noexcept {
auto l = (i << 1) + 1, r = l + 1;
auto len = strs.size();
if (l < len && strs[l] != "N") {
node->left = new Node<T>(std::stof(strs[l]));
_Bitree(node->left, strs, l);
}
if (r < len && strs[r] != "N") {
node->right = new Node<T>(std::stof(strs[r]));
_Bitree(node->right, strs, r);
}
}
void __Bitree(auto *node) noexcept {
if (node) {
__Bitree(node->left);
__Bitree(node->right);
delete node;
}
}
public:
Bitree(const auto &strs) {
if (strs.size()) {
root = new Node<T>(std::stof(strs[0]));
_Bitree(root, strs, 0);
} else
root = nullptr;
}
~Bitree() { __Bitree(root); }
constexpr auto preorder() const noexcept {
auto iter = root;
std::vector<T> c;
std::stack<Node<T> *> s;
while (iter || !s.empty()) {
while (iter) {
s.emplace(iter);
c.emplace_back(iter->val);
iter = iter->left;
}
iter = s.top()->right;
s.pop();
}
return c;
}
constexpr auto inorder() const noexcept {
auto iter = root;
std::vector<T> c;
std::stack<Node<T> *> s;
while (iter || !s.empty()) {
while (iter) {
s.emplace(iter);
iter = iter->left;
}
iter = s.top();
c.emplace_back(iter->val);
iter = iter->right;
s.pop();
}
return c;
}
constexpr auto postorder() const noexcept {
std::vector<T> c;
if (root) {
auto iter = root;
std::stack<Node<T> *> s;
s.emplace(nullptr);
while (iter || !s.empty()) {
if (iter) {
s.emplace(iter);
s.emplace(nullptr);
if (iter->right)
s.emplace(iter->right);
if (iter->left)
s.emplace(iter->left);
} else {
c.emplace_back(s.top()->val);
s.pop();
}
iter = s.top();
s.pop();
}
}
return c;
}
constexpr auto level() const noexcept {
std::vector<T> c;
std::queue<Node<T> *> q;
auto iter = root;
while (iter) {
c.emplace_back(iter->val);
if (iter->left)
q.emplace(iter->left);
if (iter->right)
q.emplace(iter->right);
if (q.empty())
break;
iter = q.front();
q.pop();
}
return c;
}
constexpr auto depth() const noexcept { return _depth(root); }
};
5. Rust实现
type TNode<T> = Rc<RefCell<Node<T>>>;
impl<T: Clone> Bitree<T> {
pub fn new() -> Self { Self { root: None } }
pub fn from(strs: &[&str]) -> Self where T: std::str::FromStr, <T as std::str::FromStr>::Err: Debug {
fn _Bitree<T>(node: Option<&mut TNode<T>>, strs: &[&str], i: usize) where T: std::str::FromStr, <T as std::str::FromStr>::Err: Debug {
let (l, size, mut n) = ((i << 1) + 1, strs.len(), node.unwrap().borrow_mut());
let r = l + 1;
if l < size && strs[l] != "N" {
n.left = Some(Rc::new(RefCell::new(Node { val: strs[l].parse::<T>().unwrap(), left: None, right: None })));
_Bitree(n.left.as_mut(), strs, l);
}
if r < size && strs[r] != "N" {
n.right = Some(Rc::new(RefCell::new(Node { val: strs[r].parse::<T>().unwrap(), left: None, right: None })));
_Bitree(n.right.as_mut(), strs, r);
}
}
let mut tree: Self = Self { root: None };
if !strs.is_empty() {
tree.root = Some(Rc::new(RefCell::new(Node { val: strs[0].parse::<T>().unwrap(), left: None, right: None })));
_Bitree(tree.root.as_mut(), strs, 0);
}
tree
}
pub fn preorder(&self) -> Vec<T> {
let (mut s, mut c, mut iter) = (vec![], vec![], self.root.clone());
while !s.is_empty() || iter.is_some() {
while let Some(node) = iter {
c.push(node.borrow().val.clone());
iter = node.borrow().left.clone();
s.push(node);
}
iter = s.pop().unwrap().borrow().right.clone();
}
c
}
pub fn inorder(&self) -> Vec<T> {
let (mut s, mut c, mut iter) = (vec![], vec![], self.root.clone());
while !s.is_empty() || iter.is_some() {
while let Some(node) = iter {
iter = node.borrow().left.clone();
s.push(node);
}
let node = s.pop().clone().unwrap();
c.push(node.borrow().val.clone());
iter = node.borrow().right.clone();
}
c
}
pub fn postorder(&self) -> Vec<T> {
let mut c = vec![];
if self.root.is_some() {
let (mut s, mut iter) = (vec![], self.root.clone());
s.push(None);
while !s.is_empty() || iter.is_some() {
match iter {
Some(node) => {
s.push(Some(node.clone()));
s.push(None);
if let Some(right) = node.borrow().right.clone() { s.push(Some(right)); }
if let Some(left) = node.borrow().left.clone() { s.push(Some(left)); }
}
_ => c.push(s.pop().unwrap().unwrap().borrow().val.clone())
}
iter = s.pop().unwrap();
}
}
c
}
pub fn level(&self) -> Vec<T> {
let (mut c, mut q, mut iter) = (vec![], VecDeque::new(), self.root.clone());
while let Some(node) = iter {
c.push(node.borrow().val.clone());
if let Some(left) = node.borrow().left.clone() { q.push_back(left); }
if let Some(right) = node.borrow().right.clone() { q.push_back(right); }
if q.is_empty() { break; }
iter = q.pop_front();
}
c
}
#[inline]
pub fn depth(&self) -> usize {
fn helper<T>(node: Option<&TNode<T>>) -> usize {
match node {
Some(n) => 1 + max(helper(n.borrow().left.as_ref()), helper(n.borrow().right.as_ref())),
_ => 0
}
}
helper(self.root.as_ref())
}
}
struct Node<T> {
val: T,
left: Option<TNode<T>>,
right: Option<TNode<T>>,
}
struct Bitree<T> {
root: Option<TNode<T>>,
}
6. 自我测试
伪代码实践:
- 递归方式求树的深度十分简单,请用迭代方式实现。
LeetCode选荐:
- Construct Binary Tree from Inorder ans Postorder Traversal
- Serialize and Deserialize Binary Tree
- House Robber III
- Binary Tree Paths
- Lowest Common Ancestor of a Binary Tree
- Invert Binary Tree
- Binary Tree Right Side View
- Binary Tree Maximum Path Sum
- Path Sum II
让每一天足够精致,期待与您的再次相遇! ^_^
图片引自tutorialspoint,在此鸣谢。 ↩︎

浙公网安备 33010602011771号