这里写目录标题
二叉树的基本组成
结构体和类的构造和使用方法非常像,所以,在C++中,结构体也存在无参和有参构造;可以根据自己的需求来定义。
struct BiNode{
int val;
BiNode *lchild,*rchild;
//BiNode() : val(0), lchild(nullptr), rchild(nullptr) {}
// BiNode(int x) : val(x), lchild(nullptr), rchild(nullptr) {}
// BiNode(int x, BiNode *lchild, BiNode *rchild) : val(x), lchild(lchild), rchild(rchild) {}
};
创建二叉树
这里我是通过队列的方式来创建二叉树的
每创建一个节点,就拿出一个数
void CreateTree(BiNode* &root,queue<int>& dp){ //通过队列创建二叉树
if(dp.empty()) return ;
int n=dp.front();
dp.pop();
if(n==0){ //等于0时,就是空节点
return;
}
else{
root =new BiNode();
root->val=n;
CreateTree(root->lchild,dp);
CreateTree(root->rchild,dp);
}
}
二叉树的4种常用遍历
先序遍历
void preOrder(BiNode* root){ //前序遍历
if(root){
cout<<root->val<<" ";
preOrder(root->lchild);
preOrder(root->rchild);
}
}
中序遍历
void inOrder(BiNode* root){ //中序遍历
if(root!=NULL){
inOrder(root->lchild);
cout<<root->val<<" ";
inOrder(root->rchild);
}
}
后序遍历
void postOrder(BiNode* root){ //后序遍历
if(root){
postOrder(root->lchild);
postOrder(root->rchild);
cout<<root->val<<" ";
}
}
层序遍历
前面的三种先、中、后遍历方法,很常用,也很简单,基本上学会了其中一种,另外两种自然也就会了,只要调换一下位置就行了,原理非常简单;
第四种方法:层序遍历,也是非常的常用,使用的场景也很多,我这里是使用两重vector容器,把它的每一层都单独放在一起,在一般的做题或者其他场景,不需要这么细致的话,使用一维vector也是一样的。
vector<vector<int>> levelOrder(BiNode*& root) { //层序遍历
vector<vector<int>> res;
if(root==NULL) return res;
queue<BiNode*> dp;
dp.push(root);
while(!dp.empty()){ //如果队列为空,则已经遍历完毕
int n=dp.size(); //获取队列的大小,也就是这一层的节点数
res.push_back(vector<int> ());
for(int i=1;i<=n;i++){ //每一次都把这一层的所有节点输出,遍历完这一层的,队列剩下的就是下一层的
auto p=dp.front();dp.pop();
res.back().push_back(p->val);
if(p->lchild) dp.push(p->lchild); //把非空的节点加入队列
if(p->rchild) dp.push(p->rchild);
}
}
return res;
}
在二叉树中查找值为val的节点
思路:
1、当root == NULL; return NULL;
2、当root->val ==num; return root;
3、当root->val!=num ; 则先去左子树中找,FindNode(root->lchild,num);
4、以上都不满足,则去右子树找
BiNode* FindNode(BiNode* &root,int num){
BiNode* p;
if(!root) return NULL; //节点为空
else if (root->val==num) return root;
else{
p=FindNode(root->lchild,num);
if(p){
return p;
}
else{
return FindNode(root->rchild,num);
}
}
}
二叉树的高度(深度)
直接递归左右子树,比大小
int HeightTree(BiNode* root){
if(!root) return 0;
return max(HeightTree(root->lchild),HeightTree(root->rchild))+1;
}
查找二叉树中的叶子节点
通过叶子节点性质,直接查找左右子节点都为空的节点,把它加入进vector数组
void FindLeaf(BiNode* &root,vector<BiNode*> &leafnode){
if(root){
if(!root->lchild && !root->rchild){
leafnode.push_back(root);
return;
}
if(root->lchild) FindLeaf(root->lchild,leafnode);
if(root->rchild) FindLeaf(root->rchild,leafnode);
}
}
以上就是本次介绍的关于二叉树的创建以及一些基本的使用
这是本次演示所使用的二叉树,下面附上一下演示的代码
#include<iostream>
#include<vector>
using namespace std;
#include<queue>
struct BiNode{
int val;
BiNode *lchild,*rchild;
//BiNode() : val(0), lchild(nullptr), rchild(nullptr) {}
// BiNode(int x) : val(x), lchild(nullptr), rchild(nullptr) {}
// BiNode(int x, BiNode *lchild, BiNode *rchild) : val(x), lchild(lchild), rchild(rchild) {}
};
void CreateTree(BiNode* &root,queue<int>& dp); //创建二叉树
void preOrder(BiNode* root); //先序遍历
void inOrder(BiNode* root); //中序遍历
void postOrder(BiNode* root); //后序遍历
vector<vector<int>> levelOrder(BiNode*& root); //层序遍历,一层层的输出
//template<class T>
BiNode* FindNode(BiNode* &root,int val); //查找值为val 的结点,找到了返回该节点地址,没找到则返回NULL
int HeightTree(BiNode* root); //求二叉树的高度(深度)
void FindLeaf(BiNode* &root,vector<BiNode*> &leafnode); //查找到叶子节点,并输出
//中间的函数在上面,就不再写了
int main(){
BiNode* root;
queue<int> dp;
vector<int> arr={1,2,4,0,6,0,0,0,3,0,5,0,0};
for(int i=0;i<arr.size();i++){
dp.push(arr[i]);
}
//创建二叉树
CreateTree(root,dp);
//先、中、后三中遍历
cout<<"preOrder: "<<endl;
preOrder(root);
cout<<"\ninOrder: "<<endl;
inOrder(root);
cout<<"\npostOrder: "<<endl;
postOrder(root);
cout<<"\nlevelOrder: "<<endl;
//层序遍历,一层层的输出
vector<vector<int>> res=levelOrder(root);
for(auto &ch:res){ //一层一层的输出
for(auto &num:ch){
cout<<num<<" ";
}
cout<<endl;
}
//查找指定节点,这里我是没有定义,直接给了一个固定值10,纯属是为了演示
BiNode* findnode;
findnode=FindNode(root,10);
if(findnode){
cout<<"findnode->val="<<findnode->val<<endl;
}
else{
cout<<"This node does not exist in this binary tree! "<<endl;
}
//定义了一个ans,接收该二叉树的高度
int ans=HeightTree(root);
cout<<"The height of the binary tree is "<<ans<<endl;
//查找该二叉树的叶子节点,并输出
vector<BiNode*> leafnode(dp.size());
FindLeaf(root,leafnode);
for(auto& leaf:leafnode){
cout<<leaf->val<<" ";
}
cout<<endl;
}
疑问,希望有大佬能够帮忙解惑!
以上便是我本次学习所得,不过有一个疑问,
在我用struct定义二叉树的基本结构的时候,如果我写了无参构造
BiNode() : val(0), lchild(nullptr), rchild(nullptr) {}
那么我在创建二叉树的时候,无论是
BiNode* root =new BiNode();
亦或是
BiNode* root =new BiNode;
都可以运行,且得出的答案都一样,
但如果我不写这个无参构造
那么第二种定义,在vscode中,虽然不会报错,但不会得出答案,便直接结束了。在网上找了很久,依然没有找到,希望有懂得大佬看到了能帮忙解惑,先谢谢了!
上述问题的原因
上述问题,在前一段时间困扰了我很久,一直没找到原因。
直到今天终于找到了,原来:
在自定义的数据类型中
- 如果该类没有定义构造函数(由编译器合成默认构造函数)也没有虚函数,那么BiNode *root= new BiNode ;将不调用合成的默认构造函数,而BiNode *root = new BiNode ();则会调用默认构造函数。
- 如果该类没有定义构造函数(由编译器合成默认构造函数)但有虚函数,那么BiNode *root= new BiNode ;和BiNode *root= new BiNode ();一样,都会调用默认构造函数。
- 如果该类定义了默认构造函数,那么BiNode *root= new BiNode ;和BiNode *root= new BiNode ();一样,都会调用默认构造函数。
在内置的数据类型中,也是有区别的,但是因为现在还没有搞懂,所以就不写了。等以后明白了再回来补充!