树
1、树是一种经常用到的数据结构,用来模拟具有树状结构性质的数据集合。
2、树里的每一个节点有一个值和一个包含所有子节点的列表。从图的观点来看,树也可视为一个拥有N个节点和N-1条便的一个有向无环图。
把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
树的特点
(01) 每个节点有零个或多个子节点;
(02) 没有父节点的节点称为根节点;
(03) 每一个非根节点有且只有一个父节点;
(04) 除了根节点外,每个子节点可以分为多个不相交的子树。
树的基本术语
若一个结点有子树,那么该结点称为子树根的"双亲",子树的根是该结点的"孩子"。有相同双亲的结点互为"兄弟"。一个结点的所有子树上的任何结点都是该结点的后裔。从根结点到某个结点的路径上的所有结点都是该结点的祖先。
结点的度:结点拥有的子树的数目。
叶子:度为零的结点。
分支结点:度不为零的结点。
树的度:树中结点的最大的度。
层次:根结点的层次为1,其余结点的层次等于该结点的双亲结点的层次加1。
树的高度:树中结点的最大层次。
无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。
有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去根,树即成为森林。
二叉树
二叉树是一种经典的树状结构。二叉树的每个节点最多有两个子树,通常子树被称为“左子树”和“右子树”。
二叉树的基本形态
它有五种基本形态:二叉树可以是空集;根可以有空的左子树或右子树;或者左、右子树皆为空
二叉树的性质
性质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个结点的二叉树,被称为满二叉树。
完全二叉树
定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
二叉查找树
定义:二叉查找树(Binary Search Tree),又被称为二叉搜索树。设x为二叉查找树中的一个结点,x节点包含关键字key,节点x的key值记为key[x]。如果y是x的左子树中的一个结点,则key[y] <= key[x];如果y是x的右子树的一个结点,则key[y] >= key[x]。
在二叉查找树中:
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树。
(04) 没有键值相等的节点(no duplicate nodes)
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥华丽的分界线¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
树的遍历(常用递归、迭代)
递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。
循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。
1、前序遍历
根节点==》左子树==》右子树
递归:
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、中序遍历
左子树==》根节点==》右子树
递归:
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、后序遍历
左子树==》右子树==》根节点
递归:
//递归
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、层序遍历
逐层遍历树结构。(广度优先搜索)。通常,我们使用一个叫做队列的数据结构来帮助我们做广度优先搜索。
广度优先搜索:
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;
}
};
未完待续…