数据结构-树

前言

说是树,其实大部分都是二叉树,更多的还是二叉排序树(也叫做二叉查找树 BinarySearchTree),树的数据结构一般都要自己实现。下面给出部分自己的写法(CPP),仅供参考。注意nullptr要在C++11的标准以上,有可能xdoj编译错误,改成NULL就行。再一次验证了xdoj的复古风格。

xdoj例题的代码都是CPP,都不是裸体代码或者屎山,放心食用。

二叉树的定义以及操作集

大神可以跳过,这里只有常见的一些函数,前序中序后序层次遍历等。

定义:

/*-----------------二叉链表实现----------------*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<stack>
#include<queue>
#define TElemType int
typedef struct BiTNode{
    TElemType data;
    struct BiTNode* lchild,*rchild;
}BiTNode,*BiTree;

//这里是下面Visit函数的一个例子
int PrintElement(TElemType e){
    printf("%c",e);
    return 1;
}

//遍历部分  int(*Visit)表示 指向函数的指针,传递一个函数

//递归版本前序遍历,自己写的,比较垃圾,LDR,LRD同理
void PreOrder(BiTree T){
    if(T==nullptr){
        return;
    }else{
        cout<<T->data;//Visit();
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}

//递归版本中序遍历,自己写的
void InOrder(BiTree T){
    if(T==nullptr){
        return;
    }else{
        InOrder(T->lchild);
        cout<<T->data;//Visit();
        InOrder(T->rchild);
    }
}

//递归版本后序遍历,自己写的
void PostOrder(BiTree T){
    if(T==nullptr){
        return;
    }else{
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        cout<<T->data;
    }
}

//非递归层次遍历
int LevelTraverseNonR(BiTree T,int(*Visit)(TElemType e)){
    //队列实现 queue 总体是先入队,p=Q的头并访问,p左子树入队,p右子树入队,再循环
    BiTree p;
    queue<BiTree>Q;
    Q.push(T);
    while(!Q.empty()){
        p=Q.front();
        if(!Visit(p->data)){
            return 0;//ERROR
        }
        Q.pop();
        if(p->lchild!=nullptr){
            Q.push(p->lchild);
        }
        if(p->rchild!=nullptr){
            Q.push(p->rchild);
        }
    }
    return 1;//OK
}
//先序次序创建二叉树
BiTree CreateBiTree(){
    char ch;
    BiTree T;
    scanf("%c",&ch);
    if(ch==' '){//用空格表示空树
        T=NULL;
    }else{
        if(!(T=(BiTree)malloc(sizeof(BiTNode)))){
            exit(0);
        }
        T->data=ch;//生成根结点
        T->lchild=CreateBiTree();
        T->rchild=CreateBiTree();
    }
    return T;
}

例题:

XDOJ0273 统计二叉树中的叶子结点数

输入形式类似完全二叉树,所以可以利用数组存储。而且下标有一定规律。
在这里插入图片描述

//xdoj0273.cpp
#include<cstdio>
using namespace std;

char input[100];

int main(){
    char ch;
    int count=0,len=1;
    while(scanf("%c",&ch) && ch!='#'){
        input[len++]=ch;
    }
    for(int i=1;i<len;++i){
        if(input[i]=='@'){
            continue;
        }else{
            if(i*2>=len){
                printf("%c ",input[i]);
                count++;
            }else{
                if(input[i*2]=='@' && input[i*2+1]=='@'){
                    printf("%c ",input[i]);
                    count++;
                }
            }
        }
    }
    printf("\n%d",count);
    return 0;
}

XDOJ0314 完全二叉树的子树

完全二叉树,可以利用数组来做。这个子树有多少结点可以数学推导。而且子树的结点数与层数有关系。
大概有三种情况。
(1)n结点和m结点在同一子树
(2)n在下方
(3)n在右边
具体推导可以参考下面的图片。
在这里插入图片描述
在这里插入图片描述

//xdoj0314.cpp
#include<cstdio>
#include<cmath>
using namespace std;

void CountNodes(int m,int n){
    int delta=(int)log2(n)-(int)log2(m);
    int k=1<<delta;
    //俩个层数之差是k
    if(n>k*m+k-1){
        //说明n结点不在这个子树但是在同一层
        printf("%d\n",2*k-1);
    }else if(n<m*k){
        printf("%d\n",k-1);
    }else{
        //n和m都在同一个树
        printf("%d\n",k+n%k);
    }
    return;
}

int main(){
    int n,m;
    while(scanf("%d%d",&m,&n) && n+m!=0){
        CountNodes(m,n);
    }
    return 0;
}

XDOJ0315 二叉树扩展先序遍历转中序遍历

在这里插入图片描述

//xdoj0315.cpp
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<stack>
using namespace std;

typedef struct BTNode{
    char data;
    struct BTNode* lchild;
    struct BTNode* rchild;
}BTNode,*BiTree;

BiTree PreOrderCreateBT(){
    char ch;
    BiTree T;
    scanf("%c",&ch);
    if(ch=='#'){//用空格表示空树
        T=NULL;
    }else{
        if(!(T=(BiTree)malloc(sizeof(BTNode)))){
            exit(0);
        }
        T->data=ch;//生成根结点
        T->lchild=PreOrderCreateBT();
        T->rchild=PreOrderCreateBT();
    }
    return T;
}

void InOrder(BiTree T){
    //LDR
    if(T==NULL){
        return;
    }else{
        InOrder(T->lchild);
        cout<<T->data<<' ';
        InOrder(T->rchild);
    }
}

int main(){
    BiTree T;
    T=PreOrderCreateBT();
    InOrder(T);
    return 0;
}

XDOJ0316 完全二叉树的公共父结点

完全二叉树,编号很特殊,左右子树其实是俩倍根结点和俩倍根结点+1。
在这里插入图片描述

//xdoj0316.cpp
#include<cstdio>
using namespace std;
//观察易得左右子树除2就是根结点的值
int main(){
    int n,m;//沙比题目也不说清楚00是什么,纯属挠谈
    while(scanf("%d%d",&n,&m) && n+m!=0){
        while(n!=m){
            n>m?n/=2:m/=2;
        }
        printf("%d\n",n);
    }
    return 0;
}

XDOJ0317 输出完全二叉树的某一层

在这里插入图片描述

//xdoj0317.cpp
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;

int Node[100];//完全二叉树一定是先有左孩子的,要么满要么从左到右连续

void InitNode(){
    for(int i=0;i<100;++i){
        Node[i]=0;//0表示空
    }
}

int Pow(int x){//计算2的x次方
    return 1<<x;//左移x次,相当于2的x次方
}

//输出某一层的结点 如果有的话,一定是[2^(d-1)-1,2^d -1)这个范围 注意右边不取
void PrintLevel(int n,int d){
    //记得判定是否大于了树的深度
    if(d > (int)log2(n)+1){
        printf("EMPTY");
        return;
    }
    int start=Pow(d-1)-1;
    int end=Pow(d)-1;
    for(int i=start;i<end;++i){
        if(i==start && Node[i]==0){
            //开头就是空,说明这一层都是空
            printf("EMPTY");
            return;
        }
        if(Node[i]!=0){
            printf("%d ",Node[i]);
        }else{
            return;
        }
    }
    return;
}

int main(){
    int n,d;
    while(scanf("%d",&n) && n!=0){
        InitNode();
        for(int i=0;i<n;++i){
            scanf("%d",&Node[i]);
        }
        scanf("%d",&d);//depth
        PrintLevel(n,d);
        printf("\n");
    }
    return 0;
}

二叉排序树/二叉查找树/BinarySearchTree

定义:

一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

数据结构和操作集:

还是用的CPP

#define TElemType int
typedef struct BiTNode{
    TElemType data;
    struct BiTNode* lchild,*rchild;
}BiTNode,*BiTree;

//BinarySearchTree,结点结构体定义与二叉树相同
//定义:左子树所有结点小于根,右子树所有结点大于根,子树仍符合上述定义(递归)

//MakeEmpty,初始化
BiTree MakeEmpty(BiTree T){
    if(T!=nullptr){
        MakeEmpty(T->lchild);
        MakeEmpty(T->rchild);
        free(T);
    }
    return nullptr;
}

//Find查找 递归的方法
BiTree Find(TElemType x,BiTree T){
    if(T==nullptr){
        return nullptr;
    }
    if(x < T->data){
        return Find(x,T->lchild);
    }else if(x > T->data){
        return Find(x,T->rchild);
    }else{
        return T;
    }
}

//FindMin查找最小值 递归版本
BiTree FindMin(BiTree T){
    if(T==nullptr){//先写终止条件
        return nullptr;
    }else if(T->lchild==nullptr){
        return T;
    }else{
        return FindMin(T->lchild);
    }
}

//FindMax查找最大值 非递归版本
BiTree FindMax(BiTree T){
    if(T!=nullptr){
        while(T->rchild!=nullptr){
            T=T->rchild;
        }
    }
    return T;
}

//在二叉查找树中插入x结点
BiTree Insert(TElemType x,BiTree T){
    if(T=nullptr){
        //创建一个树
        T=(BiTree)malloc(sizeof(BiTNode));
        if(T==nullptr){
            printf("Out of Space");//溢出
        }else{
            T->data=x;
            T->lchild=nullptr;
            T->rchild=nullptr;
        }
    }else{
        if(x < T->data){
            T->lchild=Insert(x,T->lchild);//这里已经实现插入了
        }else if(x > T->data){
            T->rchild=Insert(x,T->rchild);//这里已经实现插入了
        }
    }
    return T;//这一行非常重要
}

//Delete删除操作 递归 lazy deletion
BiTree Delete(TElemType x,BiTree T){
    BiTree TmpCell;
    if(T==nullptr){
        printf("ERROR: Element not found");
    }else if(x < T->data){
        T->lchild=Delete(x, T->lchild);
    }else if(x > T->data){
        T->rchild=Delete(x, T->rchild);
    }else if(T->lchild && T->rchild){//找到要删除的元素了,不过是有俩个子树的情况
        //找右子树中的最小值
        TmpCell=FindMin(T->rchild);
        T->data=TmpCell->data;
        T->rchild=Delete(T->data,T->rchild);//此时T->data已经赋值成右子树的最小值了,接下来需要在右子树删除这个结点(因为多余了)
    }else{
        //0个子树,或者1个子树
        TmpCell = T;
        if(T->lchild==nullptr){//这里同时也处理了0个子树的情况
            T=T->rchild;
        }else if(T->rchild==nullptr){
            T=T->lchild;
        }
        free(TmpCell);
    }
    return T;
}

例题:

XDOJ0259 二叉排序树运算

在这里插入图片描述

//xdoj0259.cpp
#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

BiTree CreateBSTree(BiTree T,int x){
    if(T==NULL){
        T=(BiTree)malloc(sizeof(BiTNode));
        T->data=x;
        T->lchild=NULL;
        T->rchild=NULL;
    }else{
        if(x < T->data){
            T->lchild=CreateBSTree(T->lchild,x);
        }else if(x > T->data){
            T->rchild=CreateBSTree(T->rchild,x);
        }
    }
    return T;
}

void PreOrder(BiTree T){
    if(T==NULL)return;
    cout<<T->data<<" ";
    PreOrder(T->lchild);
    PreOrder(T->rchild);
    return;
}

void PostOrder(BiTree T){
    if(T==NULL)return;
    PostOrder(T->lchild);
    PostOrder(T->rchild);
    cout<<T->data<<" ";
    return;
}

int main(){
    BiTree T=NULL;
    int n,input[100];
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>input[i];
    }
    for(int i=0;i<n;++i){
        T=CreateBSTree(T,input[i]);
    }
    PreOrder(T);
    cout<<endl;
    PostOrder(T);
    return 0;
}

XDOJ0271 中序遍历二叉排序树(同XDOJ0274)

在这里插入图片描述

//xdoj0271.cpp
#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

BiTree CreateBSTree(BiTree T,int x){
    if(T==NULL){
        T=(BiTree)malloc(sizeof(BiTNode));
        T->data=x;
        T->lchild=NULL;
        T->rchild=NULL;
    }else{
        if(x>T->data){
            T->rchild=CreateBSTree(T->rchild,x);
        }else if(x< T->data){
            T->lchild=CreateBSTree(T->lchild,x);
        }
    }
    return T;
}

void InOrder(BiTree T){
    //LDR中序遍历
    if(T==NULL){
        return;
    }else{
        InOrder(T->lchild);
        cout<<T->data<<' ';
        InOrder(T->rchild);
    }
}

int main(){
    BiTree T=NULL;
    int n,t;
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>t;
        T=CreateBSTree(T,t);
    }

    InOrder(T);

    return 0;
}

XDOJ0329 相同二叉排序树

xdoj非要加个最后一行是0,最后一个测试样例是多次输入n然后多次判断,纯属啥比
在这里插入图片描述

//xdoj0329.cpp
#include<cstdio>
#include<cstdlib>
using namespace std;

int pos=0;//计数器

typedef struct BiTNode{
    char data;//因题而异
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

BiTree CreateBSTree(BiTree T,char x){
    if(T==NULL){
        T=(BiTree)malloc(sizeof(BiTNode));
        T->data=x;
        T->lchild=NULL;
        T->rchild=NULL;
    }else{
        if(x>T->data){
            T->rchild=CreateBSTree(T->rchild,x);
        }else if(x< T->data){
            T->lchild=CreateBSTree(T->lchild,x);
        }
    }
    return T;
}

//将T前序遍历的结果存储到数组a
void PreOrder(BiTree T,char a[]){
    if(T==NULL){
        return;
    }else{
        a[pos++]=T->data;
        PreOrder(T->lchild,a);
        PreOrder(T->rchild,a);
    }
    return;
}

//将T中序遍历的结果存储到数组a
void InOrder(BiTree T,char a[]){
    if(T==NULL){
        return;
    }else{
        InOrder(T->lchild,a);
        a[pos++]=T->data;
        InOrder(T->rchild,a);
    }
    return;
}

void CompareTree(char pre1[],char pre2[],char in1[],char in2[],int length){
    for(int i=0;i<length;++i){
        if(pre1[i]!=pre2[i]){
            printf("NO\n");
            return;
        }
    }
    for(int i=0;i<length;++i){
        if(in1[i]!=in2[i]){
            printf("NO\n");
            return;
        }
    }
    printf("YES\n");
    return;
}

void Fun(int n){
    BiTree T1=NULL;
    int i,length;
    char str1[11];//输入的字符串
    char pre1[10],pre2[10],in1[10],in2[10];//最终对比的前序和中序结果
    scanf("%s",str1);
    for(i=0;str1[i]!='\0';++i){
        T1=CreateBSTree(T1,str1[i]);
    }

    PreOrder(T1,pre1);
    pos=0;
    InOrder(T1,in1);
    pos=0;

    //此时i为str1的长度
    length=i;

    for(int j=0;j<n;++j){
        char str2[11];//避免循环过后仍然存在值
        BiTree T2=NULL;//避免循环过后仍然存在树
        scanf("%s",str2);
        for(i=0;i<length;++i){
            T2=CreateBSTree(T2,str2[i]);
        }
        PreOrder(T2,pre2);
        pos=0;
        InOrder(T2,in2);
        pos=0;
        CompareTree(pre1,pre2,in1,in2,length);
    }    
}

int main(){
    int n;
    //xdoj非要加个最后一行是0,最后一个测试样例是多次输入n判断,纯属啥比
    while(scanf("%d",&n) && n!=0){
        Fun(n);
    }
    return 0;
}

XDOJ0330 二叉排序树之父结点

在这里插入图片描述

//xdoj0330.cpp
#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

BiTree CreateBSTree(BiTree T,int x){
    if(T==NULL){
        T=(BiTree)malloc(sizeof(BiTNode));
        T->data=x;
        T->lchild=NULL;
        T->rchild=NULL;
    }else{
        if(x>T->data){
            T->rchild=CreateBSTree(T->rchild,x);
        }else if(x< T->data){
            T->lchild=CreateBSTree(T->lchild,x);
        }
    }
    return T;
}

void FindFater(BiTree T,int x){
    //在二叉排序树T中找到x的父亲
    if(T==NULL){
        return;
    }else{
        if(x==T->data){
            //是根结点,直接输出-1
            cout<<"-1"<<endl;
        }else if(x > T->data){
            if(x==T->rchild->data){
                cout<<T->data<<endl;
            }else{
                FindFater(T->rchild,x);
            }
        }else if(x < T->data){
            if(x==T->lchild->data){
                cout<<T->data<<endl;
            }else{
                FindFater(T->lchild,x);
            }
        }
    }
    return;
}

int main(){
    BiTree T=NULL;
    int n,t;
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>t;
        T=CreateBSTree(T,t);
        FindFater(T,t);
    }
    return 0;
}

XDOJ0331 二叉排序树的遍历

在这里插入图片描述

//xdoj0331.cpp
#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

BiTree CreateBSTree(BiTree T,int x){
    if(T==NULL){
        T=(BiTree)malloc(sizeof(BiTNode));
        T->data=x;
        T->lchild=NULL;
        T->rchild=NULL;
    }else{
        if(x < T->data){
            T->lchild=CreateBSTree(T->lchild,x);
        }else if(x > T->data){
            T->rchild=CreateBSTree(T->rchild,x);
        }
    }
    return T;
}

void PreOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        cout<<T->data<<' ';
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
    return;
}

void InOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        InOrder(T->lchild);
        cout<<T->data<<' ';
        InOrder(T->rchild);
    }
    return;
}

void PostOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        cout<<T->data<<' ';
    }
    return;
}
int main(){
    BiTree T=NULL;
    int n,t;
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>t;
        T=CreateBSTree(T,t);
    }
    PreOrder(T);
    cout<<endl;
    InOrder(T);
    cout<<endl;
    PostOrder(T);
    return 0;
}

XDOJ0332 二叉排序树的判定

在这里插入图片描述

//xdoj0332.cpp
#include<iostream>
#include<cstdlib>
using namespace std;

int Matrix[1000][3];//直接矩阵存储,不需要非线性结构了

int DFS(int root,int rootval){
    //从root开始DFS
    int tmp;
    if(root==0){
        //终止条件,也就是遇到空树
        return -1;//设置一个标志,表示该根结点下方无孩子
    }else{
        tmp=DFS(Matrix[root][1],Matrix[Matrix[root][1]][0]);
        if(tmp!=-1 && tmp >= rootval){
            //如果左孩子的结点大于等于根,说明不是合法的二叉排序树
            cout<<"false";
            exit(0);//直接退出
        }
        tmp=DFS(Matrix[root][2],Matrix[Matrix[root][2]][0]);
        if(tmp!=-1 && tmp <= rootval){
            //如果右孩子的结点小于等于根,说明不是合法的二叉排序树
            cout<<"false";
            exit(0);
        }
    }
    if(tmp==-1){
        //说明该根的左右子树都是空,返回的应该是根的值
        tmp=rootval;
    }
    return tmp;
}


int main(){
    //实际上是DFS深度优先搜索,左子树搜一遍,没有大于根的就回头,再根再右子树
    int n,root;
    for(int i=0;i<3;++i){
        Matrix[0][i]=0;//第0行不存储
    }

    cin>>n>>root;
    for(int i=1;i<=n;++i){
        for(int j=0;j<3;++j){
            cin>>Matrix[i][j];//行从1数起,避免混淆
        }
    }
    DFS(root,Matrix[root][0]);
    cout<<"true";

    return 0;
}

俩种遍历结果确定一颗二叉树的方法

前序遍历中序遍历可以唯一确定一颗二叉树
后序遍历中序遍历可以唯一确定一颗二叉树
层次遍历中序遍历可以唯一确定一颗二叉树

一般都是递归的思路来确定树的,先确定根结点,再由中序遍历LDR的特殊性,根结点左边的序列肯定是左子树,根结点右边的序列肯定是右子树。详见下面三种情况的例题。

根据前序遍历和中序遍历确定二叉树

XDOJ0318 二叉树遍历

在这里插入图片描述
主要难点在于如何根据前序遍历和中序遍历结果确定一颗二叉树;
其实一样的,在DLR(前序遍历)结果中按顺序来,再从LDR中挑选,LDR中的元素由于特殊性质,该元素左边的子序列一定是该结点的左子树,右边序列一定是该元素的右子树,以此类推,可以用递归实现。

下面给出一种递归的思路确定二叉树:
(1)从头按顺序取DLR数组没取过的一个结点,再从LDR数组里面找到这个点,以这个点为分隔
(2)LDR的左边部分先遍历
(3)LDR的右边部分再遍历
(4)左右遍历部分仍然符合上述规则

//xdoj0318.cpp
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef struct BiTNode{
    char data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

char DLR[27],LDR[27];
//插入x到T中,T传引用,避免传值导致返回时T实际上没有改变
void InsertTNode(BiTree& T,char x){
    T=(BiTree)malloc(sizeof(BiTNode));
    T->data=x;
    T->lchild=NULL;
    T->rchild=NULL;
}

int FindRoot(int n,int start,int end){
    //在俩个遍历结果中 找到LDR根结点的位置
    int flag=0,i,j;
    for(i=0;i<n;++i){
        for(j=start;j<=end;++j){
            if(LDR[j]==DLR[i]){
                flag=1;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    return j;//返回LDR的索引j
}

BiTree CreateBiTree(BiTree T,int n,int start,int end){
    //下面是终止条件 start和end都是LDR的索引
    if(start>end){
        //这种情况,明显不对头,直接返回
        return T;//会有这种情况,是下面递归的锅,可以先筛掉
    }else if(start==end){
        //说明LDR子数组只剩一个,直接插入结点 然后返回就行了
        InsertTNode(T,LDR[start]);
        return T;
    }

    //这里是找ROOT结点用于插入到树中 先遍历LEVEL,再从LDR子数组中找
    int j=FindRoot(n,start,end);
    InsertTNode(T,LDR[j]);
    T->lchild=CreateBiTree(T->lchild,n,start,j-1);
    T->rchild=CreateBiTree(T->rchild,n,j+1,end);
    return T;
}

void PostOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        printf("%c",T->data);
    }
}

int main(){
    BiTree T;
    char ch;
    int i=0;
    while(scanf("%c",&ch) && ch!='\n'){
        DLR[i++]=ch;
    }
    gets(LDR);//???没懂为什么这里改成gets过了
    T=CreateBiTree(T,i,0,i-1);
    PostOrder(T);
    return 0;
}

XDOJ0319 将满二叉树转换为求和树

在这里插入图片描述
主要难点在于如何根据前序遍历和中序遍历结果确定一颗二叉树;
其实一样的,在DLR(前序遍历)结果中按顺序来,再从LDR中挑选,LDR中的元素由于特殊性质,该元素左边的子序列一定是该结点的左子树,右边序列一定是该元素的右子树,以此类推,可以用递归实现。

下面给出一种递归的思路确定二叉树:
(1)从头按顺序取DLR数组没取过的一个结点,再从LDR数组里面找到这个点,以这个点为分隔
(2)LDR的左边部分先遍历
(3)LDR的右边部分再遍历
(4)左右遍历部分仍然符合上述规则

求和部分也是递归思路
(1)终止条件是左右子树是空(注意是满二叉树,没孩子一定是没有左右孩子的)
(2)否则计算左子树的和
(3)再计算右子树的和
(4)左右子树求和部分仍然符合上述规则

//xdoj0319.cpp
#include<iostream>
#include<cstdlib>
using namespace std;
typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

int DLR[100],LDR[100];

//插入x到T中,T传引用,避免传值导致返回时T实际上没有改变
void InsertTNode(BiTree& T,int x){
    T=(BiTree)malloc(sizeof(BiTNode));
    T->data=x;
    T->lchild=NULL;
    T->rchild=NULL;
}

int FindRoot(int n,int start,int end){
    //在俩个遍历结果中 找到LDR根结点的位置
    int flag=0,i,j;
    for(i=0;i<n;++i){
        for(j=start;j<=end;++j){
            if(LDR[j]==DLR[i]){
                flag=1;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    return j;//返回LDR的索引j
}

BiTree CreateBiTree(BiTree T,int n,int start,int end){
    //下面是终止条件 start和end都是LDR的索引
    if(start>end){
        //这种情况,明显不对头,直接返回
        return T;//会有这种情况,是下面递归的锅,可以先筛掉
    }else if(start==end){
        //说明LDR子数组只剩一个,直接插入结点 然后返回就行了
        InsertTNode(T,LDR[start]);
        return T;
    }

    //这里是找ROOT结点用于插入到树中 先遍历LEVEL,再从LDR子数组中找
    int j=FindRoot(n,start,end);
    InsertTNode(T,LDR[j]);
    T->lchild=CreateBiTree(T->lchild,n,start,j-1);
    T->rchild=CreateBiTree(T->rchild,n,j+1,end);
    return T;
}

int SumTree(BiTree& T){
    //返回值是根结点的值,这样利于递归
    if(T->lchild==NULL && T->rchild==NULL){
        //满二叉树,不用考虑只有左孩子或者右孩子的情况
        int root=T->data;
        T->data=0;
        return root;
    }else{
        //相当于把结果存到边上
        int l=SumTree(T->lchild);
        int r=SumTree(T->rchild);
        int root=T->data;
        //因为相当于结果存在边上,所以左右结点还要加一次
        T->data=l + r + T->lchild->data + T->rchild->data;
        return root;
    }
}

void PostOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        PostOrder(T->lchild);
        cout<<T->data<<" ";
        PostOrder(T->rchild);
    }
    return;
}

int main(){
    BiTree T=NULL;
    int n;
    //input部分
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>DLR[i];
    }
    for(int i=0;i<n;++i){
        cin>>LDR[i];
    }

    T=CreateBiTree(T,n,0,n-1);
    SumTree(T);
    PostOrder(T);

    return 0;
}

根据后序遍历和中序遍历确定二叉树

根据层次遍历和中序遍历确定二叉树

XDOJ0320 二叉树的不同形态

在这里插入图片描述

这题梗概:给你层次遍历中序遍历,让你确定一棵树,再输出树的叶子结点前序遍历结果后序遍历结果
主要矛盾:如何通过层次遍历和中序遍历确定一颗二叉树?

要先明白,LDR数组里面,随便取一个元素,比如下图的A,左边子数组是DBFE,右边子数组是GCHI,因为是中序遍历,所以左边DBFE是左子树右边GCHI是右子树。以此类推。

下面给出一种递归的思路:
(1)从头按顺序取LEVEL数组的一个没取过的结点,再从LDR数组里面找到这个点,以这个点为分隔
(2)左边部分先遍历
(3)右边部分再遍历
(4)左右遍历部分仍然符合上述规则

比如下图:
(1)LEVEL中找到A,LDR[0,8]中找到A,左边是[0,3]右边是[4,8]
(2)LEVEL中找到B,LDR[0,3]中找到B,左边是[0,0]右边是[2,3]
(3)LEVEL中找到D,LDR[0,0]中找到D,退栈
(4)LEVEL中找到E,LDR[2,3]中找到E,左边是[2,2]右边为空
(5)LEVEL中找到F,LDR[2,2]中找到F,左边是空,右边是空,退栈
(6)LEVEL中找到G,LDR[4,8]中找到G,左边是空,右边是[6,8]
…依次类推
在这里插入图片描述

//xdoj0320.cpp
#include<iostream>
#include<cstdlib>

using namespace std;

typedef struct BiTNode{
    int data;
    struct BiTNode* lchild;
    struct BiTNode* rchild;
}BiTNode,*BiTree;

int LEVEL[100],LDR[100];

//插入x到T中,T传引用,避免传值导致返回时T实际上没有改变
void InsertTNode(BiTree& T,int x){
    T=(BiTree)malloc(sizeof(BiTNode));
    T->data=x;
    T->lchild=NULL;
    T->rchild=NULL;
}

int FindRoot(int n,int start,int end){
    int flag=0,i,j;
    for(i=0;i<n;++i){
        for(j=start;j<=end;++j){
            if(LDR[j]==LEVEL[i]){
                flag=1;
                break;
            }
        }
        if(flag){
            break;
        }
    }
    return j;
}

BiTree CreateBiTree(BiTree T,int n,int start,int end){
    //下面是终止条件 start和end都是LDR的索引
    if(start>end){
        //这种情况,明显不对头,直接返回
        return T;//会有这种情况,是下面递归的锅,可以先筛掉
    }else if(start==end){
        //说明LDR子数组只剩一个,直接插入结点 然后返回就行了
        InsertTNode(T,LDR[start]);
        //这里输出的是叶子结点
        //因为此时start已经等于end,说明这里直接插入一个结点并且往后不会有结点是他的孩子
        cout<<T->data<<" ";
        return T;
    }
    //这里是找ROOT结点用于插入到树中 先遍历LEVEL,再从LDR子数组中找
    int j=FindRoot(n,start,end);
    InsertTNode(T,LDR[j]);
    T->lchild=CreateBiTree(T->lchild,n,start,j-1);
    T->rchild=CreateBiTree(T->rchild,n,j+1,end);
    return T;
}

void PreOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        cout<<T->data<<" ";
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
    return;
}

void PostOrder(BiTree T){
    if(T==NULL){
        return;
    }else{
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        cout<<T->data<<" ";
    }
    return;
}

int main(){
    BiTree T=NULL;
    int n;
    cin>>n;

    for(int i=0;i<n;++i){
        cin>>LEVEL[i];
    }
    for(int i=0;i<n;++i){
        cin>>LDR[i];
    }

    T=CreateBiTree(T,n,0,n-1);

    cout<<endl;
    PreOrder(T);
    cout<<endl;
    PostOrder(T);

    return 0;
}

哈夫曼树

XDOJ0261 哈夫曼树

没用到树,直接数组
在这里插入图片描述

//xdoj0261.cpp
#include<iostream>
#include<algorithm>
using namespace std;
int main(){//4 5 2 10 8
    int n,sum=0;
    int a[30];
    cin>>n;
    for(int i=0;i<n;++i){
        cin>>a[i];
    }
    sort(a,a+n);
    for(int i=1;i<n;++i){
        a[i]=a[i-1]+a[i];
        sum+=a[i];
        sort(a+i-1,a+n);//重新排序
    }
    cout<<sum;
    return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值