参考:1.b站https://www.bilibili.com/video/BV1Mt4y197kC?t=814&p=119
2.柳神代码https://liuchuo.blog.csdn.net/article/details/52505179
3.1119. Pre- and Post-order Traversals (30)-PAT甲级真题(前序后序转中序)_eqopp的博客-CSDN博客
树的构建方法
树的构建方法有一下几种:
1.用二维数组来存储树(类似于图)(多用于树,不是二叉树?)
多用于这种输入模式
20 9 24 //节点数 非叶节点个数 要查找的权重
10 2 4 3 5 10 2 18 9 7 2 2 1 3 12 1 8 6 2 2 //输入所有节点的权重
00 4 01 02 03 04 //节点 节点的孩子个数 其他节点
02 1 05
04 2 06 07
03 3 11 12 13
06 1 09
07 2 08 10
16 1 15
13 3 14 16 17
17 2 18 19
例:1053 Path of Equal Weight
缺点:开的数据大了容易内存超限
2.用邻接表存储树(二维vector数组)(多用于树,不是二叉树)
多用于这种输入模式
9 1.80 1.00
1 5 4 4 -1 4 5 3 6
/*下标代表这个节点的下标,数值代表这个节点的父亲节点的下标,为-1代表根节点*/
例:1090 Highest Price in Supply Chain、1106 Lowest Price in Supply Chain
3.用结构体数组存储(多用于只有左右孩子的二叉树)
struct Node{
int l,r,index,id,level; //分别为左孩子下标,右孩子下标,下标,节点的值,节点的层数
}a[15]; //有几个节点数组就开多大
多用于这种输入模式
8 //节点个数
1 - //第i个节点的左右孩子,‘-’代表没有
- -
0 -
2 7
- -
- -
5 -
4 6
例:1102 Invert a Binary Tree
注意:1.这种输入方式的根节点寻找可以借助一个vector数组,唯一没有出现的那个节点就是根节点
2.输入数据应该用string接受,因为输入可能是两位数
4.用结构体链表存储(多用于构建BST树)
BST树:二叉搜索树,所有节点的左子树小于该节点,所有节点的右子树大于该节点
struct Node{
int v; //节点的值
struct Node *left; //节点左孩子的指针
struct Node *right; //节点右孩子的指针
};
多用于这种输入模式
9
25 30 42 16 20 20 35 -5 28 //告诉你节点的信息,让你构建BST树
构建BST树的函数
List Build(List root,int v){ //递归函数
if(root==NULL){ //如果节点是空的,代表改数据已经找到了自己的位置
root=new Node();
root->left=NULL;
root->right=NULL;
root->v=v;
}
else if(root->v>=v){ //不是空本来节点的值大于等于给的值时的递归调用左子树
root->left=Build(root->left,v);
/*一定是左子树等于递归函数,不是return递归函数*/
}
else
root->right=Build(root->right,v); //小于时调用左子树
return root; //返回根节点
}
build函数注意事项
1.递归时是让左右子树等于递归的返回值,不是return这个值
2.最后一定记得返回根节点
例:1115 Counting Nodes in a BST
树的遍历
一、先序遍历
void dfs(root){
cout<<root.v;
dfs(root->left);
dfs(root->right);
}
二、中序遍历
void dfs(root){
dfs(root->left);
cout<<root.v;
dfs(root->right);
}
三、后序遍历
void dfs(root){
dfs(root->left);
dfs(root->right);
cout<<root.v;
}
四、层序遍历
层序遍历的两种方法
第一种:只用下标表示
void dfs(root,int index){ //引入一个index函数,初始值为零
/*假设这棵树是一颗平衡二叉树,
那么一个节点的左孩子节点为父节点index*2+1
右孩子节点为父节点的index*2+2*/
root->index=index;
dfs(root->left,index*2+1);
dfs(root->right,index*2+2);
}
//输出
int main(){
//将结构体数组按index的值从大到小排序,按index的值从小到大输出
}
优点:
对于结构体数组构建的树,这种方法代码量较少,适用于树高度不高的树(小于20层)
缺点:
当树高度大于20层,或者只有单边的二叉树,资源利用率极小,每一层的index是2^n-1层数过高index的值会超限
第二种:用层数level表示
void dfs(int root,int level){
maxlevel=max(maxlevel,level);
//a[root].index=index;
if(a[root].l!=-1)
dfs(a[root].l,level+1);
a[root]={a[root].r,a[root].l,b[cnt++]};
if(a[root].r!=-1)
dfs(a[root].r,level+1);
}
/*DFS函数中求出这棵搜索树的最大层数*/
vector<Node> v[maxlevel+1]
/*建立结构体树的二维数组,其中行代表树的深度level列代表这行有几个数据*/
int main(){
v[0].push_back(root); //把根节点放到第一层
for(int i=0;i<=maxlevel;i++){
/*一定是i<=maxlevel,当等于maxlevel时是叶子结点,不是叶子结点的下一层节点*/
for(int j=0;j<v[i].size();j++){
cout<<a[v[i][j]].id; //输出这个节点的值
if(a[v[i][j]].l!=-1) v[i+1].push_back(a[v[i][j]].l);
if(a[v[i][j]].r!=-1) v[i+1].push_back(a[v[i][j]].r);
/*这个节点如果有左孩子或者右孩子,将左右孩子放入下一层的动态数组中*/
}
}
}
二维动态数组的行是层数,列是这一行有几个数据
开始之前一定记得把根节点放在第0层,否则for循环无法开始
遍历相互之间的转化
1.由先序序列和中序序列转化为后序序列
#include<bits/stdc++.h>
using namespace std;
int in[8]={3,1,2,6,7,5,8};
int pro[8]={6,1,3,2,5,7,8};
void post(int root,int start,int end){
/*root是先序序列根节点的下标,
start这层递归树先序序列开始的下标,
end是这层递归树先序序列结束的下标*/
if(start>end) //循环结束的判断条件
return ;
int i=start;
while(pro[root]!=in[i]&&i<end) i++; //在中序序列中寻找先序的根节点
post(root+1,start,i-1);
/*递归左子树,根节点等于原先子树加一
start不变,end编程i-1*/
post(root+(i-start)+1,i+1,end);
/*递归右子树,i-start相当于原先的树减去左子树,
还剩原来的根结点和右子树,所以应该再加1*/
printf("%d",pre[root]); //因为是后序序列,应该在递归结束后再输出
}
int main(){
post(0,0,7);
}
递归思想
2.由中序序列和后序序列转化为先序序列
#include<bits/stdc++.h>
using namespace std;
int in[8]={3,1,2,6,7,5,8};
int post[8]={3,2,1,7,8,5,6};
void pro(int root,int start,int end){
if(start>end){
return ;
}
int i=start;
while(pro[root]!=in[i];i<end) i++;
cout<<post[root];
pro(root-(end-i)-1,start,i-1);
pro(root-1,i+1,end)
}
int main(){
pro(8-1,0,7);
}
与上同理
3.由先序序列和后序序列转化为中序序列(结果可能不唯一)
void getIn(int preLeft, int preRight, int postLeft, int postRight) {
if(preLeft == preRight) { //递归结束
in.push_back(pre[preLeft]);
return;
}
if (pre[preLeft] == post[postRight]) {
int i = preLeft + 1; //先序序列除了根的第一个节点
while (i <= preRight && pre[i] != post[postRight-1]) i++;
/*找pre中等于post除了根的最后一个节点*/
if (i - preLeft > 1)
getIn(preLeft + 1, i - 1, postLeft, postLeft + (i - preLeft - 1) - 1);
//递归左子树
else
unique = false; //当左右子树只有一个时,说明情况不唯一
in.push_back(post[postRight]);
getIn(i, preRight, postLeft + (i - preLeft - 1), postRight - 1); //递归右子树
}
}
4.中序遍历转层序遍历(完全BST?)
例题:
1064 Complete Binary Search Tree
int level[1005];
int in[1005];
int t=0;
void inOrder(int root){
if(root>=n) return ; //大于等于,因为下标为n就没有值了
inOrder(root*2+1);
level[root]=in[t++];
inOrder(root*2+2);
}
int mian(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>in[i];
}
inOrder(0);
}
由遍历建树
模板(后序遍历)
int in[8]={12,11,20,17,1,15,8,5};
int post[8]={12,20,17,11,15,8,5,1};
List dfs(List Root,int root,int start,int end){
if(start>end)
return Root;
if(Root==NULL){
Root=new Node();
Root->val=post[root];
Root->left=NULL;
Root->right=NULL;
}
int i=start;
while(in[i]!=post[root]&&i<end) i++;
Root->left=dfs(Root->left,root-(end-i)-1,start,i-1);
Root->right=dfs(Root->right,root-1,i+1,end);
return Root;
}
int main(){
List Root;
Root=dfs(Root,n-1,0,n-1);
}
AVL树模板
模板
#include<bits/stdc++.h>
using namespace std;
typedef struct Node *List;
struct Node{
int val;
List left;
List right;
};
List root=NULL;
int getHeight(List root){ //获得高度
if(root==NULL) return 0;
int l=getHeight(root->left);
int r=getHeight(root->right);
return max(l,r)+1; //一定记得加1
}
List rightRotation(List root){ //当左边高于右边是右单旋
List temp=root->left;
root->left=temp->right;
temp->right=root;
return temp;
}
List leftRotation(List root){ //当右边高于左边是左单旋
List temp=root->right;
root->right=temp->left;
temp->left=root;
return temp;
}
List rightLeftRotation(List root){ //右左双旋
root->right=rightRotation(root->right);
return leftRotation(root);
}
List leftRightRotation(List root){ //左右双旋
root->left=leftRotation(root->left);
return rightRotation(root);
}
List insert(List root,int val){
if(root==NULL){
root=new Node();
root->val=val;
root->left=NULL;
root->right=NULL;
}
else if(val<root->val){
root->left=insert(root->left,val);
int l=getHeight(root->left); //插入完成后判断树的高度
int r=getHeight(root->right);
if(l-r>=2){
if(val<root->left->val){
root=rightRotation(root); //左边高于右边是右单旋
}
else{
root=leftRightRotation(root); //左边高于右边是左右双旋
}
}
}
else{
root->right=insert(root->right,val);
int l=getHeight(root->left);
int r=getHeight(root->right);
if(r-l>=2){
if(val>root->right->val){
root=leftRotation(root); //右边高于左边是左单旋
}
else{
root=rightLeftRotation(root); //右边高于左边是右左双旋
}
}
}
return root; //一定记得return!!!!!!
}
void dfs(List root){ //深搜验证
if(root==NULL)
return ;
cout<<root->val<<" ";
dfs(root->left);
dfs(root->right);
}
int main(){
int n;
cin>>n;
int data;
for(int i=0;i<n;i++){
cin>>data;
root=insert(root,data);
}
dfs(root);
}
其他注意事项
1.翻转二叉树就是每一个节点的左右孩子相互交换,递归时先递归左子树在递归右子树是原先的数,先递归右子树再递归左子树就是原本树的翻转二叉树
2.由层序遍历遍历二叉树判断是不是完全二叉树,只要有一个节点为空,后面必须没有节点才能满足完全二叉树的条件,否则则不是完全二叉树。