关于树基本的概念和操作(基础)

关于树已经算接触的很多了,但是从来没有做过一个系统的总结,总是学完忘忘了学,为此做一个基本的总结。

首先,对于基本的树结构来说,有几个名词还是值得注意的,例如深度,层次空树等,对于一些基本的理论内容,本次总结不再赘述,后续另开一章总结,本次总结主要以思想和代码为主。

一、二叉树的存储和基本操作

对于二叉树来演,最基本的还是其存储结构和基本操作。

对于二叉树的基本存储结构,就采用一个简单的结构体;

struct node{
    typename data;
    node* lchild;
    node* rchild;
}

对于insert和建立二叉树,建立二叉树一般采用调用insert来进行插入,从而进行建树操作的完成;

对于完全二叉树,也可以有不一样的存储操作。由于完全二叉树任何节点,只要有左右孩子,其左孩子编号必定是2X,右孩子编号必定为2X+1,所以我们可以利用数组来进行存储操作,并且数组的顺序正好为二叉树的层序遍历。

二、二叉树的遍历

对于二叉树来说,也有老生常谈的四种遍历方法;

1.先序遍历,也就是爹->左孩子->右孩子的遍历顺序;值得注意的是,先序遍历的结果序列,任何一个子树所在的序列,第一个节点必为根节点。

2.中序遍历,也就是左孩子->爹->右孩子的遍历顺序;同样,在该序列情况下,根节点总是在左子树和右子树中间,所以只要知道了根节点,就可以判断出左右子树都是哪些。

3.后序遍历,也就是左孩子->右孩子->爹的遍历顺序;在该序列情况下,任何子树序列,最后一个节点必定为根节点。

4.层序遍历,也就是逐层来进行访问。该种访问方式的实现主要利用队列来实现。将根节点进队列,然后取出将其子节点继续塞入队列,从而进行层序遍历。

void LayerOrder(node* root){
    queue<node*>q;
    q.push(root);
    while(q.empty()){
        node* now=q.front();
        q.pop();
        printf("%d",now->data);
        if(now->lchild!=NULL)
            q.push(now->lchild);
        if(now->rchild!=NULL)
            q.push(now->rchild);
    }
}

(注意指针拷贝的问题,所以queue内的元素为指针而不是node型,不然就会无法对树进行操作)

对于层序遍历,如果想知道该节点位于第几层,也可以进行节点的改造和遍历过程的改造。

struct node{
    int data;
    int layer;
    node* lchild;
    node* rchild;
};
void LayerOrder(node* root){
    queue<node*>q;
    root->layer=1;
    q.push(root);
    while(q.empty()){
        node* now=q.front();
        q.pop();
        printf("%d",now->data);
        if(now->lchild!=NULL){
            now->lchild->layer=now->layer+1;
            q.push(now->lchild);
        }
        if(now->rchild!=NULL){
            now->rchild->layer=now->layer+1;
            q.push(now->rchild);
        }
    }
}

值得注意的是这几种遍历方式的混合。中序序列+其余的任何一种遍历方式都可以唯一的确立一个二叉树。原因是只有中序遍历可以根据根节点建立左右子树,而其唯一需要的就是根节点信息,其他三种方法都可以提供给其必要的根节点信息。

所以对于考研或者oj题目来说,有一道题目便是给两个序列,让你求出一个唯一的二叉树。

举个例子:给定一个二叉树的先序遍历序列和中序遍历序列,重建这一棵二叉树。

对于这一类的题目,其实有固定的解法和套路,那就是从给定的两个序列中强推出树的根节点和左右子树,从而进行递归构建,从根节点开始递归构建。

对于先序序列来说,第一个节点必为根节点,所以就可以从中序序列中找到根节点的左右子树,然后在先序序列中找左右子树的根节点,以此往复循环。

代码如下:

node* create(int preL,int preR,int inL,int inR){
    if(preL>preR){
        return NULL;
    }
    node* root=new node;
    root->data=pre[preL];//先序序列
    int k;
    for(int k=inL;k<=inR;k++){
        if(in[k]==pre[preL]){
            break;//寻找中序序列中的根节点,从而找出左右子树;
        }
    }
    int numLeft=k-inL;
    root->lchild=create(preL+1,preL+numLeft,inL,k-1);//进行左子树的构建
    root->rchild=create(preL+numLeft+1,preR,k+1,inR);//进行左子树的构建
    return root;
}

三、二叉树的静态实现

使用指针和结构体的实现被称为动态实现,下面介绍一下数组作为存储结构的静态实现。对于数组来说,进行左右节点的访问途径为使用数组下标进行访问。

struct node{
    typename data;
    int lchild;
    int rchild;
}Node[Maxn];//Maxn为节点上限个数

对于新节点的构建:

初始的index值为0。

int newnode(int v){
    Node[index].data=v;
    Node[index].lchild=-1;
    Node[index].rchild=-1;
    return index++;
}

对于节点的搜寻和修改:

int newnode(int v){
    Node[index].data=v;
    Node[index].lchild=-1;
    Node[index].rchild=-1;
    return index++;
}

对于节点的插入:

void insert(int &root,int x){
    if(root==-1){
        root=newnode(x);
        return;
    }
    if(插在左子树)
        insert(Node[root].lchild,x);
    else
        insert(Node[root].rchild,x);
}

对于二叉树的建立:

int Create(int data[],int n){
    int root=-1;
    for(int i=0;i<n;i++){
        insert(root,data[i]);
    }
    return root;
}

对于静态二叉树来说,四种遍历也大相径庭,所以不再赘述,仍然是采用递归和队列的方式来实现。

三、树的遍历操作

注意一点,对于二叉树来说,其左右子树有严格的先后次序,但是对于普通的树却不是如此,普通的树的子节点和子树并没有严格的先后次序。

对于树的存储,这里采用静态的写法总结。对于动态的指针结构体存储来说,我们只需要利用数组来保存数组即可,和二叉树不同的也就是其内部存储的子节点指针的个数。

其中树的节点构造和二叉树静态存储类似,开一个vector来进行子节点存储;

struct node{
    typename data;
    vector child;
}Node[maxn];

对于节点的新建,有构建函数:

int index=0;
int newNode(int v){
    Node[index].data=v;
    Node[index].child.clear();
    return index++;
}

对于树的构建来说,往往会对节点进行编号,所以直接将编号作为数组的下标来进行存储,而不用自己指定。同理,子节点的vector数组里的子节点index也是自己图中所给的标号。

对于树来说,有两种遍历方式,先根遍历和层序遍历;

先根遍历和先序遍历相似,唯一不同的在于没有递归边界。

void PreOrder(int root){
    print("%d",Node[root].data);
    for(int i=0;i<Node[root].child.size();i++){
        PreOrder(Node[root].child[i]);
    }
}

层序遍历和二叉树的实现方式也类似,也是通过队列来放置节点在数组中的下标,每次取出队首元素来进行访问,之后将其所有的子节点放入队列,直到队列为空。

void LayerOrder(int root){
    queue<int> Q;
    Q.push(root);
    while(!Q.empty()){
        int front=Q.front();
        Q.pop();
        printf("%d",Node[front].data);
        for(int i=0;i<Node[front].child.size();i++){
            Q.push(Node[front].child[i]);
        }
    }
}

同样,可以在struct内置layer来进行层数计数,和二叉树层序遍历相同,这里不再赘述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值