如何建树?
二叉树-建树-方式一
dfs使用root左右指针建立树节点关系,返回根节点root
二叉树-建树-方式二
dfs使用二维数组,int nds[n][2],如:nds[i][0]表示i节点的左子节点,nds[i][1]表示i节点的右子节点
树-建树
dfs使用邻接表保存树节点关系,vector
二叉查找树-建树
二叉查找树-建树-方式一:前序序列
如前序序列:8 6 5 7 10 8 11
8是根节点
左子树:从6开始往后找小于8的都为8的左子树节点
右子树:从最后一位11开始往前找大于8的都为8的右子树节点
继续递归过程,直到完成建树
node * create(int preL,int preR){
if(preL>preR)return NULL;
node * root = new node(pre[preL]);
int k=preL+1;
while(k<preR&&abs(pre[k])<=abs(pre[preL]))k++;//找第一个大于根节点的值
root->f=create(preL+1,k-1);
root->r=create(k,preR);
return root;
}
二叉查找树-建树-方式二:依次插入
非递归
node * root;
void insert(int n) {
if(root==NULL) {
root=new node(n,1);
return;
}
node * p=root;
while(p!=NULL) {
if(p->data<n) {
if(p->right==NULL){
p->right=new node(n,p->h+1);
return;
}
else p=p->right;
} else {
if(p->left==NULL){
p->left=new node(n,p->h+1);
return;
}
else p=p->left;
}
}
}
递归
void insert(node* &root, int data, int dep) {
if(root == NULL) { //到达空结点时,即为需要插入的位置
root = new node(data,dep);
root->data = data;
root->left = root->right = NULL; //此句不能漏
return;
}
if(data <= root->data) insert(root->left, data, root->h+1); //插在左子树
else insert(root->right, data, root->h+1); //插在右子树
}
完全二叉查找树建树
完全二叉查找树,任意节点序列建树
注:二叉查找树(不一定要完全二叉树)任意节点序列,升序排序,即为二叉查找树的中序序列
- 方式一:模拟递归打印中序序列的过程,将中序序列依次插入到数组建树
- 方式二:找root在中序序列中的位置k并将root保存到数组,递归处理startk-1左子树和k+1end右子树,完成建树
void inOrder(int root) { //root保存在1位置,index初始化为0
if(root>n) return;
inOrder(root*2);
CBT[root]=number[index++];
inOrder(root*2+1);
}
方式二
void getLevel(int start, int end, int index) {
if(start>end)return;
int n=end-start+1;
int l=log(n+1)/log(2);//最后一层的层数
int leave=n-(pow(2,l)-1); //最后一层叶子结点数
//pow(2, l - 1) - 1是除了root结点所在层和最后?层外,左?树的结点个数,pow(2, l - 1) 是l+1
//层最多拥有的属于根结点左?树的结点个数,min(pow(2, l - 1), leave)是最后?个结点真正拥有的
//属于根结点左?树上的结点个数
int root = start+(pow(2,l-1)-1)+min((int)pow(2,l-1),leave);
level[index]=in[root];
getLevel(start,root-1,2*index+1);
getLevel(root+1,end,2*index+2);
}
序列转换
前序后序转中序
后序倒数第一个节点为根root,root在前序中为第一个节点
后序倒数第二个节点为root的右子树的根节点rightTreeRoot,rightTreeRoot在前序中将序列分为左边部分(root和root的左子树),右边部分(root的右子树包含rightTreeRoot)
若rightTreeRoot前只有一个节点,那么说明该rightTreeRoot的父节点root只有一个子节点,即rightTreeRoot既可以为root的左子节点也可以为root的右子节点
node * create(int preL,int preR,int postL,int postR) {
if(preL>preR)return NULL;
node * root = new node;
root->data=post[postR];
root->left=NULL;
root->right=NULL;
if(preL==preR)return root;
int k=preL+1;
while(k<=preR&&pre[k]!=post[postR-1])k++;
if(k-preL>1) {
root->left=create(preL+1,k-1,postL,postL+(k-preL-1)-1);
root->right=create(k,preR,postL+(k-preL-1),postR-1);
} else {
unique = false;
root->right=create(k,preR,postL+(k-preL-1),postR-1);
}
return root;
}
前序中序转后序
思路
前序第一个节点为根root,root在中序中位置为k,将序列分为左边部分(root的左子树),右边部分(root的右子树)
inLk-1为下次左递归的后序序列,其对应的后序序列为preL+1preL+(k-inL);
k+1inR为下次递归的后序序列,其对应的后序序列为postL+(k-inL)+1preR;
void postFirst(int inL,int inR, int preL,int preR) {
if(inL>inR||flag) return;
int k=inL;
while(k<inR&&in[k]!=pre[preL])k++;
postFirst(inL, k-1, preL+1, preL+(k-inL));
postFirst(k+1, inR, preL+(k-inL)+1, preR);
if(flag==false) {
printf("%d", pre[preL]);
flag=true;
}
}
题目
后序中序转前序
思路
后序第一个节点为根root,root在中序中位置为k,将序列分为左边部分(root的左子树),右边部分(root的右子树)
inLk-1为下次左递归的后序序列,其对应的后序序列为postLpostL+(k-inL)-1;
k+1inR为下次递归的后序序列,其对应的后序序列为postL+(k-inL)postR-1;
node * create(int postL,int postR,int inL,int inR) {
if(postL>postR)return NULL;
node * root=new node;
root->data=post[postR];
// 找到当前根节点在中序遍历中的位置
int i;
for(i=inL; i<=inR; i++) {
if(post[postR]==in[i])
break;
}
int numLeft=i-inL;
root->left=create(postL,postL+numLeft-1,inL,inL+numLeft-1);//inL+numLeft=i
root->right=create(postL+numLeft,postR-1,inL+numLeft+1,inR);//inL+numLeft=i
return root;
}
题目
PAT A1127 ZigZagging on a Tree (30分)
二叉查找树前序转后序
二叉查找树前序序列
- pre[0]为root
- 从pre[1]开始第一个大于root的值(位置为k,pre[1]pre[k-1]即为root左子树)
- 从pre[N-1]开始第一个小于root的值(位置为k,pre[k+1]pre[N-1]即为root右子树)
void getPost(int root, int tail) {
if(root>tail)return;
int i=root+1;
int j=tail;
if(!isMirror) {
while(i<=tail&&pre[i]<pre[root])i++;
while(j>root&&pre[j]>=pre[root])j--;
} else {
while(i<=tail&&pre[i]>=pre[root])i++;
while(j>root&&pre[j]<pre[root])j--;
}
if(i-j!=1)return;
getPost(root+1,j);//左子树
getPost(i,tail); //右子树
post.push_back(pre[root]);
}
如何进行层级遍历序列?
方式一
建树,借助队列层级遍历
方式二
建树时,记录index到结点(如:节点i的左子节点index=2i+1,节点i的右子节点index=2i+2),之后节点index升序排序,依次打印即为层序序列
如何记录层级?
DFS中如何记录层级?如何记录每层叶子节点数?
记录层级-方式一-辅助数组
dfs函数参数中记录当前处理节点的层级,使用int max_h记录最大层数方便后续层级遍历,可使用int leaf[n]记录对应层级叶子节点数
void dfs(int index,int h) {
max_h=max(h,max_h);//记录最大层数
if(nds[index].size()==0) { //叶子节点
leaf[h]++;
return;
}
for(int i=0; i<nds[index].size(); i++) {
dfs(nds[index][i],h+1);
}
}
记录层级-方式二-结构体中定义层级变量
struct node {
int data;
node * left;
node * right;
int depth;
};
void dfs(node * root) {
queue<node*> q;
root->depth=0;
q.push(root);
while(!q.empty()) {
node * now = q.front();
q.pop();
// printf("%d-%d ",now->data,now->depth);
result[now->depth].push_back(now);
if(now->left!=NULL) {
now->left->depth=now->depth+1;
q.push(now->left);
}
if(now->right!=NULL) {
now->right->depth=now->depth+1;
q.push(now->right);
}
}
}
BFS中如何记录层级?如何记录每层叶子节点数?
使用int h[n]记录节点层级,如:h[i]即为节点i的层级,使用int max_h记录最大层数方便后续层级遍历,可使用int leaf[n]记录对应层级叶子节点数
void bfs(){
queue<int> q;
q.push(1);
while(!q.empty()){
int now = q.front();
q.pop();
max_h=max(max_h,h[now]);
if(nds[now].size()==0){
leaf[h[now]]++;
} else{
for(int i=0;i<nds[now].size();i++){
h[nds[now][i]]=h[now]+1;
q.push(nds[now][i]);
}
}
}
}
如何判断树是否为完全二叉树
方式一
数组存储树(节点index=i,左右子节点index分别为2i+1,2i+2),判断数组中间是否有空闲位置
方式二
数组存储树(节点index=i,左右子节点index分别为2i+1,2i+2),最大index==结点数n-1即为完全二叉树
方式二
BFS借助队列广度优先遍历,若遇到NULL则为非完全二叉树,因为完全二叉树在借助队列BFS遍历时,中间不可能出现NULL