基本概念
高度和深度
高度
树的高度是从离根节点最远的那个叶节点开始一直到树的根节点的那条路径上的节点的个数;树节点的高度是这个节点可以到达的最远叶节点路径上的节点个数,可以通过如下代码计算某个节点的高度:
int get_tree_depth(tree_node* root) {
if (root == NULL) {
return 0;
}
return 1 + max(get_tree_depth(root->left), get_tree_depth(root->right));
}
深度
树的深度就是根节点到最远叶节点之间路径上所有节点的数量;树节点的深度是根节点到这个节点之间路径上所有的节点数量。
树的类型
二叉树
- 每个结点最多有两棵子树,二叉树中不存在度大于2的结点。每个节点最多存在两棵子树。
- 左子树和右子树是有顺序的,次序不能颠倒。
满二叉树
所有的分支结点都存在左子树和右子树,并且所有的叶子结点都在同一层上,这样就是满二叉树。
完全二叉树
对一棵具有n个结点的二叉树按层序排号,如果编号为i的结点与同样深度的满二叉树编号为i结点在二叉树中位置完全相同,就是完全二叉树。满二叉树必须是完全二叉树,反过来不一定成立。
二叉搜索树(BST)
- 在二叉搜索树中,左子结点小于或等于根结点,右子结点大于或等于根结点。
- 对于任意一个结点,其左子树中最大关键字都不超过根结点的值,其右子树中最小关键字都不小于根结点的值。这样结构很容易联想到二分法,二分法在一个有序序列找一个数效率是挺可观的。
- 二叉搜索树的中序遍历是有序的。
树的遍历
树的前中后序遍历是基于根节点的,这一点很重要。
前序遍历
递归形式:
void pre_order_recur(tree_node* root, vector<int>& result) {
result.push_back(root->val);
if (root->left) {
pre_order_recur(root->left, result);
}
if (root->right) {
pre_order_recur(root->right, result);
}
}
迭代形式:
/*
* 1. 入栈根节点
* 2. 弹出栈首节点
* 3. 把弹出节点的值放入vector中,
* 4. 再入栈弹出节点右子节点和左子节点(注意顺序),
* 5. 直到栈中无元素
*/
vector<int> pre_order(tree_node* root) {
stack<tree_node*> stk;
vector<int> result;
if (root != NULL) {
stk.push(root);
}
while (!stk.empty()) {
tree_node* top = stk.top();
stk.pop();
result.push_back(top->val);
if (top->right != NULL) {
stk.push(top->right);
}
if (top->left != NULL) {
stk.push(top->left);
}
}
return result;
}
中序遍历
递归形式:
void in_order_recur(tree_node* root, vector<int>& result) {
if (root->left) {
pre_order_recur(root->left, result);
}
result.push_back(root->val);
if (root->right) {
pre_order_recur(root->right, result);
}
}
迭代形式:
/*
* 1. 从根节点向左一路出发,入栈所有左子节点,
* 2. 把栈顶元素的值放入vector中,弹出栈顶元素top,
* 3. 判断top是否有右节点,如果有则把top的右节点开始一路向左,把所有左子节点压入栈,
* 4. 直到栈为空且节点为空
*/
vector<int> in_order(tree_node* root) {
stack<tree_node*> stk;
vector<int> result;
while (root || !stk.empty()) {
while (root) {
stk.push(root); // 先push,再下移,这样才能保证push到stack中的元素不是空的
root = root->left;
}
if (!stk.empty()) {
tree_node* top = stk.top();
result.push_back(top->val);
stk.pop();
root = top->right; // root被赋值为top->right,而不是root->right
}
}
return result;
}
后序遍历
递归形式:
void in_order_recur(tree_node* root, vector<int>& result) {
if (root->left) {
pre_order_recur(root->left, result);
}
if (root->right) {
pre_order_recur(root->right, result);
}
result.push_back(root->val);
}
迭代形式:
/*
* 1. 建立两个栈stk1, stk2,
* 2. 向stk1压入根节点,
* 3. 弹出stk1栈顶节点top, 并压入stk2,
* 4. 把top的左子节点和右子节点压入stk1,
* 5. 循环上述过程,直到stk1为空
* 6. stk2中从顶到底按照后序遍历顺序存放树的节点,依次把元素输出即可得到树的后序遍历
*/
vector<int> post_order(tree_node* root) {
stack<tree_node*> stk1, stk2;
vector<int> result;
if (root != NULL) {
stk1.push(root);
}
while (!stk1.empty()) {
tree_node* top = stk1.top();
stk1.pop();
stk2.push(top);
if (top->left) {
stk1.push(top->left);
}
if (top->right) {
stk1.push(top->right);
}
}
while (!stk2.empty()) {
result.push_back(stk2.top()->val);
stk2.pop();
}
return result;
}
层次遍历
层次遍历结果存放在vector中
利用队列的先进先出性质进行层次遍历。
class Solution {
public:
std::vector<TreeNode*> traverse_tree_from_top_to_bottom(TreeNode* root) {
std::vector<int> result;
if (root == nullptr) {
return result;
}
std::queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
auto& front = q.front();
result.push_back(front);
q.pop();
if (front->left) {
q.push(front->left);
}
if (front->right) {
q.push(front->right);
}
}
return result;
}
}
层次遍历过程中打印
利用to_be_printed和next_level_node两个标记位以及队列的先进先出的性质进行层次打印。
class Solution {
public:
void print_tree_from_top_to_bottom(TreeNode* root) {
if (root == nullptr) {
return;
}
std::queue<TreeNode*> q;
q.push(root);
int to_be_printed = 1;
int next_level_node = 0;
while (!q.empty()) {
auto& front = q.front();
std::cout << front->value << " ";
if (front->left) {
q.push(front->left);
++next_level_node;
}
if (front->right) {
q.push(front->right);
++next_level_node;
}
q.pop();
--to_be_printed;
if (to_be_printed == 0) {
std::cout << std::endl;
to_be_printed = next_level_node;
next_level_node = 0;
}
}
}
}