二叉树的静态实现

通过下面的学习,读者应能完全不使用指针,而简单使用数组来完成二叉树的所有操作。

在定义二叉树时,采用的是二叉链表的结构,如下所示:

struct node{
    typename data;    //数据域 
    node* lchild;    //指向左子树根结点的指针 
    node* rchild;    //指向右子树根结点的指针 
};

在这个定义中,为了能够实时控制新生成结点的个数,结构体node中的左右指针域都使用了指针,但是指针对于一些刚入门的读者来说可能容易犯错,因此有必要想办法避免指针的使用,采用的方法就是使用静态的二叉链表

所谓的静态二叉链表是指,结点的左右指针域使用int型代替,用来表示左右子树的根结点在数组中的下标。为此需要建立一个大小为结点上限个数的node型数组,所有动态生成的结点都直接使用数组中的结点,所有对指针的操作都改为对数组下标的访问。于是,结点node的定义变为如下:

struct node{
    typename data;    //数据域 
    int lchild;    //指向左子树根结点的指针 
    int rchild;    //指向右子树根结点的指针 
}Node[maxn];    //结点数组,maxn为结点上限个数 

再这样的定义一下,结点的动态生成就可以转变成如下的静态指定

int index = 0;
int newNode(int v){        //分配一个Node数组中的结点给新的结点,index为其下标 
    Node[index].data = v;    //数据域为v 
    Node[index].lchild = -1;    //以-1或maxn表示空,因为数组范围是0~maxn-1 
    Node[index].rchild = -1;
    return index++;
}

下面给出二叉树的查找、插入、建立的代码,读者会发现这其实就是在之前使用指针的代码上进行了少量的修改,读者不妨将它们与原先的写法对比一下:

//查找,root为根结点在数组中的下标
void search(int root, int x, int newdata){
    if(root==-1){    //用-1代表NULL 
        return;        //空树,死胡同(递归边界) 
    }
    if(Node[root].data == x){    //找到数据域为x的结点,把它修改成newdata 
        Node[root].data = newdata;
    } 
    search(Node[root].lchild,x,newdata);    //往左子树搜索x(递归式) 
    search(Node[root].rchild,x,newdata);    //往右子树搜索x(递归式)
} 
 
//插入,root为根结点在数组中的下标
void insert(int &root, int x){        //记得加引用 
    if(root==-1){
        root = newNode(x);
        return;
    } 
    if(由二叉树的行知x应该插在左子树){
        insert(Node[root].lchild,x);    //往左子树搜索(递归式) 
    } else {
        insert(Node[root].rchild,x);    //往右子树搜索(递归式)
    }
} 
 
//二叉树的建立,函数返回根结点root的下标
int Create(int data[], int n) {
    int root = -1;        //新建根结点 
    for(int i=0;i<n;i++){
        insert(root,data[i]);    //将data[0]~data[n-1]插入二叉树中 
    }
    return root;    //返回二叉树根结点下标 
}

下面是静态二叉树的先序遍历、中序遍历、后序遍历、层次遍历的代码:

 
//先序遍历
void preorder(int root){
    if(root==-1){
        return;    //到达空树,递归边界 
    }
    //访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
    //访问左子树
    preorder(root->lchild);
    //访问右子树
    preorder(root->rchild);
}
//中序遍历 
void inorder(int root){
    if(root==-1){
        return;    //到达空树,递归边界 
    }
    //访问左子树
    inorder(root->lchild);
    //访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
    //访问右子树
    inorder(root->rchild);
}
//后序遍历
void postorder(int root){
    if(root==-1){
        return;    //到达空树,递归边界 
    }
    //访问左子树
    postorder(root->lchild);
    //访问右子树
    postorder(root->rchild);
        //访问根结点root,例如将其数据域输出
    printf("%d\n",root->data);
}
//层次遍历
void LayerOrder(int 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);
        }
    }
}

于是二叉树便可以完全静态化。

 

上一篇:线索二叉树


下一篇:数据结构——计算节点个数和二叉树高度(C语言版)