1、树是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。

2、树里的每一个节点有一个值和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有N个节点和N-1条便的一个有向无环图。

《算法总结》——二叉树_子树

把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

树的特点

(01) 每个节点有零个或多个子节点;

(02) 没有父节点的节点称为根节点;

(03) 每一个非根节点有且只有一个父节点;

(04) 除了根节点外,每个子节点可以分为多个不相交的子树。

树的基本术语

若一个结点有子树,那么该结点称为子树根的"双亲",子树的根是该结点的"孩子"。有相同双亲的结点互为"兄弟"。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。

结点的度:结点拥有的子树的数目。

叶子:度为零的结点。

分支结点:度不为零的结点。

树的度:树中结点的最大的度。

层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。

树的高度:树中结点的最大层次。

无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。

有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。

森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。

二叉树

二叉树是一种经典的树状结构。二叉树的每个节点最多有两个子树,通常子树被称为“左子树”和“右子树”。

二叉树的基本形态

它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空

《算法总结》——二叉树_子树_02

二叉树的性质

性质1:二叉树第i层上的结点数目最多为 2{i-1} (i≥1)。

性质2:深度为k的二叉树至多有2{k}-1个结点(k≥1)。

性质3:包含n个结点的二叉树的高度至少为log2 (n+1)。

性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1。

二叉树的分类

满二叉树

定义:高度为h,并且由2{h} –1个结点的二叉树,被称为满二叉树。

《算法总结》——二叉树_结点_03

完全二叉树

定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。

特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。

《算法总结》——二叉树_结点_04

二叉查找树

定义:二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。

《算法总结》——二叉树_结点_05

在二叉查找树中:

(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(03) 任意节点的左、右子树也分别为二叉查找树。

(04) 没有键值相等的节点(no duplicate nodes)

¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥华丽的分界线¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

树的遍历(常用递归、迭代)


递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。
循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。


1、前序遍历

根节点==》左子树==》右子树

《算法总结》——二叉树_结点_06

递归:

class Solution {
public:
void helper(TreeNode* root, vector<int>& ans){
//若空
if(root == nullptr) return;
//输入一个节点,保存当前节点数据,指向左子树,右子树
ans.push_back(root->val);
helper(root->left, ans);
helper(root->right, ans);
}
vector<int> preorderTraversal(TreeNode *root) {
//创建返回数组
vector<int> ans;
//递归
helper(root, ans);
//返回数组
return ans;
}
};

迭代:

class Solution {
public:
vector<int> preorderTraversal(TreeNode *root) {
//创建vector变量
vector<int> ans;
//创建一个栈
stack<TreeNode*> stk;
//创建一个临时节点
TreeNode* node = root;
while(!stk.empty() || node != nullptr){
while(node != nullptr){
ans.push_back(node->val);
stk.push(node);
node = node->left;
}
node = stk.top();
stk.pop();
node = node->right;
}
return ans;
}
};

2、中序遍历

左子树==》根节点==》右子树

《算法总结》——二叉树_结点_07

递归:

class Solution {
public:
void helper(TreeNode* root, vector<int>& ans){
//若空
if(root == nullptr) return;
//
helper(root->left, ans);
ans.push_back(root->val);
helper(root->right, ans);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ans;
helper(root, ans);
return ans;
}
};

迭代:

//迭代
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
//创建一个vector数组
vector<int> ans;
//若空
if(root == nullptr) return ans;
//创建一个栈
stack<TreeNode*> stk;

//根据终止条件进行循环
while(!stk.empty() || root != nullptr){
while(root != nullptr){
//压入栈
stk.emplace(root);
//指向当前节点的左孩子
root = root->left;
}
//若当前节点为空
root = stk.top();
stk.pop();
//当前节点的值保存在ans;
ans.emplace_back(root->val);
root = root->right;
}
//返回一个vector数组
return ans;
}
};

3、后序遍历

左子树==》右子树==》根节点

《算法总结》——二叉树_二叉树_08

递归:

//递归
class Solution {
public:
void postorder(TreeNode* root, vector<int>& ans){
//若空
if(root == nullptr) return;
//调用递归函数
postorder(root->left, ans);
postorder(root->right, ans);
ans.emplace_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
//创建返回vector变量
vector<int> ans;
//调用递归函数
postorder(root, ans);
//返回vector变量
return ans;
}
};

迭代:

//迭代
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
//创建返回向量
vector<int> ans;
//若空
if(root == nullptr) return ans;
//创建临时节点
TreeNode* pre = nullptr;
//创建栈
stack<TreeNode*> stk;
//搞定循环初始条件,并循环
while(!stk.empty() || root != nullptr){
while(root != nullptr){
//后序遍历:左->右->根
//压入栈,先入后出
stk.emplace(root);
root = root->left;
}
root = stk.top();
stk.pop();
if(root->right == nullptr || root->right == pre){
ans.emplace_back(root->val);
pre = root;
root = nullptr;
}
else{
stk.emplace(root);
root = root->right;
}
}
return ans;
}
};

4、层序遍历

逐层遍历树结构。(广度优先搜索)。通常,我们使用一个叫做队列的数据结构来帮助我们做广度优先搜索。

《算法总结》——二叉树_结点_09

广度优先搜索:

class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> ans;
if(root == nullptr) return ans;
que.push(root);
while(!que.empty()){
int size = que.size();
ans.push_back(vector<int>());
for(int i = 0; i < size; ++i){
auto node = que.front(); que.pop();
ans.back().push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return ans;
}
};

未完待续…