二叉树理论基础
二叉树的种类
- 满二叉树
如果一颗二叉树只有 度为0 和度为2 的节点,并且,度为0的结点在同一层上
满二叉树的深度为k,则它的节点数为 2^k - 1
- 完全二叉树
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
- 二叉搜索树
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树
- 平衡二叉树
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn,注意我这里没有说unordered_map、unordered_set,unordered_map、unordered_map底层实现是哈希表。
二叉树的存储方式
- 链式存储
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YvaZqbTj-1668069900559)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20221110152311809.png)]
- 顺序存储
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NGNPOizW-1668069900560)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20221110152335424.png)]
如果父节点的下标为i,那么左孩子是i * 2 + 1,右孩子是i * 2 +2;
遍历方式
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
- 深度优先遍历
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 广度优先遍历
- 层次遍历(迭代法)
这里前中后,其实指的就是中间节点的遍历顺序
二叉树的定义
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(NULL),right(NULL){}
};
三种递归遍历
递归三部曲
以 二叉树的前序遍历为例
-
确定递归函数的参数和返回值
void traversal(TreeNode* cur,vector vec)
-
确定递归的终止条件
if(cur == NUll) return
- 确定单层递归的逻辑
vec.push_back(cur ->val);
traversal(cur->left,vec);
traversal(cur->right,vec);
题目
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[1,2]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
题解
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//前序遍历:中左右
void traversal(TreeNode* cur,vector<int>& vec){ //记得要加取地址符
if(cur == nullptr) return;
vec.push_back(cur->val);
traversal(cur->left,vec);
traversal(cur->right,vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root,result);
return result;
}
};
中序/后序遍历 模仿即可;
三种迭代遍历
前序遍历
//处理顺序和访问顺序是一致的
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//中左右
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;;
if(root == nullptr) return result ;
st.push(root);
//统一在while循环里来做
//将中节点放入result,弹出并加入其右孩子、加入其左孩子
//此时top()变成了新的中节点,继续步骤1
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return result;
}
};
中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//中序遍历的出栈顺序 :左中右
//元素的访问和处理不是同时的
//迭代的思想就是当每个节点成为了中节点之后,进行处理
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
TreeNode* cur = root; //借助cur指针来访问节点
while(cur != nullptr ||!st.empty())
if(cur != nullptr) {
st.push(cur);
cur = cur -> left; //找到最左的节点[只要非空就一直入栈] //此时cur = null,处理左的方法就是将非空的左【放入栈处理】
}else{
cur = st.top(); //处理中节点
st.pop();
result.push_back(cur->val);
cur = cur -> right; //处理右节点
}
return result;
}
};
后序遍历
前序遍历:中左右----> 中右左----> 翻转,左右中
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//先写出前序遍历:中左右【右要先入栈】--中右左
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if(root == nullptr) return result;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if(node->left) st.push(node->left);
if(node->right) st.push(node->right);
//迭代法最重要的就是 把所有节点 作为中间节点遍历时处理
}
reverse(result.begin(),result.end());
return result;
}
};