二叉树理论基础
p.s.请去acwing练一下自己创建二叉树
二叉树的种类
满二叉树和完全二叉树,二叉树搜索树
满二叉树
如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
节点个数2^n-1【n为树的深度】
完全二叉树
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
满二叉树一定是完全二叉树
二叉搜索树
二叉搜索树是有数值的了,二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树【AVL(Adelson-Velsky and Landis)树】
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,所以map、set的增删操作时间时间复杂度是logn【key有序】
而unordered_map、unordered_set底层实现是哈希表。
二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
那么链式存储方式就用指针, 顺序存储的方式就是用数组。
链式存储【构造二叉树:函数里面传参头指针】
数组【实际用的少】
遍历方式
DFS
中序前序后序【递归,栈;非递归,迭代】
左右中序指的是中的遍历顺序
BFS
层序遍历【迭代,队列,先进先出】
二叉树定义
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x),left(NULL),right(NULL){}//构造函数
};
构造函数也可以不写,但是new一个新的节点的时候就比较麻烦。
例如有构造函数,定义初始值为9的节点:
TreeNode* a = new TreeNode(9);
而没有构造函数还得自己定义:
TreeNode* a = new TreeNode();
a->val = 9;
a->left = NULL;
a->right = NULL;
二叉树遍历
递归三要素:
1.函数返回值和参数【函数头定义】2.确定结束条件【if(check)return】3.单层递归逻辑
不太懂这个结构体/二叉树怎么读进去的
1.中序遍历
p.s.函数名字不要起太平常的,比如try什么的,可能和系统性的函数重名,出问题
p.s.对于返回值是void的递归函数,参数里的&是重点
感觉挺神奇的,只有root的val,也就是中可以放进数组
问题:这个输入是怎么读进去的
/**
* 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) {//没看懂
//返回一个vector,必然不可以遍历
vector<int> res;
try1(root,res);
return res;
}
void try1(TreeNode* root,vector<int>&res){
if(root == nullptr){
return;
}
//左中右
try1(root->left,res);
res.push_back(root->val);
try1(root->right,res);
}
};
2.迭代遍历【递归指的是一个函数可以自己调用自己】【感觉对栈不够了解,就是理解的很别扭】
【递归的本质就是栈】
一个栈【放入弹出元素】,一个数组【输出遍历顺序】
【处理顺序和遍历顺序,
这里处理顺序定义为移除栈顶元素对其操作的顺序,遍历顺序定义为从root到left、right的操作】
栈遍历的终止条件是while(!st.empty()),不为空时进行弹出栈顶,新元素压入栈
p.s.这里要先访问左子树后访问右子树:需要先压入右节点,再压入左节点
while(!st.empty()){
now = st.top();
st.pop();//取出栈顶元素
if(now!=nullptr){
rs.push_back(now->val);//压入栈
}else{
continue;
}
st.push(now->right);
st.push(now->left);
}
/**
* 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>rs;
st.push(root);
TreeNode*now;
while(!st.empty()){
now = st.top();
st.pop();
if(now!=nullptr){
rs.push_back(now->val);
}else{
continue;
}
st.push(now->right);
st.push(now->left);
}
return rs;
}
};
还是不会基于迭代的后序:
中左右->中右左->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>rs;
st.push(root);
TreeNode*now;
while(!st.empty()){
now = st.top();
st.pop();
if(now!=nullptr){
st.push(now->left);
st.push(now->right);
rs.push_back(now->val);
cout<<now->val;
}else{
continue;
}
}
reverse(rs.begin(),rs.end());
return rs;
}
};
基于迭代的中序遍历
【直接复制代码】
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
层序遍历【BFS】5/22
BFS是队列
问题:1.queue只取了元素:front(),没pop()
2.vector<int>s;s.push_back(1);注意:push_back是指在末尾加元素,如果构造了指定大小的vector,再push_back会发现前面有好多0,
即【3】变成【0,3】
3.注意边界条件,root为null直接返回null
/**
* 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<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>rs;//
queue<TreeNode*>q;
// q.push(root);
if (root != NULL) q.push(root);//边界条件
TreeNode*now;
int size;
int i = 0;
int j =0;
while(!q.empty()){
size = q.size();
// rs[i].resize(size);
vector<int>re(size);
// cout<<re.size();
j = 0;
while(size!=0){
now = q.front();//没pop
q.pop();
re[j] = now->val;//push_back是在末尾加元素,所以导致【3】变成了[0,3],或者构建空vector,然后push_back
j++;
if(now->left){
q.push(now->left);
}
if(now->right){
q.push(now->right);
}
size--;
}
i++;
rs.push_back(re);
}
return rs;
}
};
二叉树的最大深度【还是层序遍历bfs】 【27min】
e104. 二叉树的最大深度 - 力扣(LeetCode)
/**
* 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:
int maxDepth(TreeNode* root) {//层次遍历??
int depth = 0;
queue<TreeNode*>q;
if(root==nullptr){
return 0;
}
q.push(root);
while(!q.empty()){
int size = q.size();
depth++;
while(size!=0){
TreeNode*cur = q.front();
q.pop();
if(cur->left!=nullptr){
q.push(cur->left);
}
if(cur->right!=nullptr){
q.push(cur->right);
}
size--;
}
}
return depth;
}
};
翻转二叉树【8min】5/22
有时间可细品,经典题
注意如果按中序方式遍历的话
需要
inverse1(root->left);
swap(root->left,root->right);
inverse1(root->left);【依然还是root->left】【因为翻转了,所以原来的right变成left了】
/**
* 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:
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr){
return nullptr;
}
//核心在于left,right进行swap,那么遍历每一个节点?yes
inverse1(root);
return root;
}
void inverse1(TreeNode* root){
if(root==nullptr){
return;
}
swap(root->left,root->right);
inverse1(root->left);
inverse1(root->right);
}
};
3.对称二叉树5/23【没思路】【迷茫】【1h】
这个递归和之前的不太一样,原来的是一直往深遍历,直到什么情况retrun
这里更像是从根出发就开始return了,其实也不是,还是到底才开始return,只不过根有情况直接在刚开始就return掉了
方法一、递归
这里是一个不返回void的递归!!!
注意:空树的边界情况!!!!
/**
* 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:
bool compare(TreeNode* left,TreeNode*right){//自上而下
if(left==nullptr && right==nullptr){
return true;
}else if(left ==nullptr && right!=nullptr){
return false;
}else if(left!= nullptr &&right==nullptr){
return false;
}else if(left->val != right->val){//不为空
return false;
}
bool oo = compare(left->left,right->right);
bool ii = compare(left->right,right->left);
if(oo && ii){
return true;
}else{
return false;
}
}
bool isSymmetric(TreeNode* root) {
//没啥思路,层序遍历?不保真
//没看懂的点在于如何考虑以下情况
// 8 8
// / \
//2 2
//这个的思路应该是从上向下,和一般的递归不太一样
if (root == NULL) return true;//考虑空树
bool it = compare(root->left,root->right);
return it;
}
};
另一种实现:queue迭代
【感觉queue还是要比stack舒服一点】
p,s.队列可以存储 nullptr,支持push和front等
错误:左右子树都为nullptr时应该continue,而不是return true
因为这时候queue里也许还有别的值
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if(root==nullptr){
return true;
}
queue<TreeNode*>q;
q.push(root->left);
q.push(root->right);
while(!q.empty()){
TreeNode *left = q.front();q.pop();
TreeNode *right= q.front();q.pop();
if(left == nullptr && right==nullptr){//不应该return true
// return true;
continue;
}else if(left == nullptr && right!=nullptr){
return false;
}else if(left != nullptr && right==nullptr){
return false;
}
if(left->val == right->val){
q.push(left->left);
q.push(right->right);
q.push(left->right);
q.push(right->left);
}else{
return false;
}
}
return true;
}
};