数据结构-二叉树实现和遍历

113 阅读3分钟

二叉树的存储结构

二叉树也是一种数据结构,其数据一般有顺序表存储和链式存储两种形式。

顺序表存储

顾名思义,这种形式是将二叉树的各个结点的数据通过顺序数组进行存储。但是二叉树是一种分层式的数据结构,如果采用顺序存储的方式,就不太容易体现出分层、分支的结构。所以在顺序表存储下,我们再一次参照该二叉树对应的满二叉树的编号,根据各个结点的编号来进行结点数据的存储。比如根结点编号为1,就将其数据存放到顺序数组下标为1的位置(为了更好地计算孩子结点在下标的位置,顺序表存储从下标为1的地方开始)。

不过,这种存储方式具有很大的缺陷,比如要想存放一个层数为3、除叶子结点外的其他结点只有右孩子结点的二叉树,明明树中只有3个结点,却必须用一个空间为8的数组存放(数组下标为0的位置空出不用),在存放右子树很多的二叉树时,这种存储方式空间利用率很低。所以这种存储方式一般只适用于完全二叉树和满二叉树。按照二叉树的分层结构,我们一般使用链式存储方式来存放二叉树。

链式存储

这是二叉树最常用的存储方式,相较于顺序表存储,链式存储不仅空间利用率高,还更能体现出二叉树的层次结构。在链式存储中,我们使用链表结点来作为二叉树的每个结点,链表结点有三个变量,分别左孩子结点指针lchild、右孩子结点指针rchild和数据域data。如果结点是叶子结点,它的左右孩子指针均指向空。

//二叉树链式存储的实现
struct node_bit {
    int data;
    node_bit *lchild,*rchild;
};

二叉树的遍历

由于二叉树是一种分层结构,所以遍历的方式比较多样,我们常见的遍历方式主要有先序遍历、中序遍历和后续遍历。

先序遍历(preOrder)

//读取当前结点的data域
void getx (node_bit x) {
    cout<<x->data<<“ ”;	
}
//先序遍历
void preOrder (node_bit t) {
    if(t!=NULL) {
        getx(t);//这里可以是对遍历到某个结点要做的任何操作
	preOrder(t->lchild);
	preOrder(t->rchild);
    }
}

由于树是一种递归结构,我们可以很方便地使用递归函数来遍历整个二叉树。

在先序遍历中,对于任何一个树结点,我们都先对该结点进行数据访问,再先后遍历该结点的左子树和右子树。根据此规则,我们只需要在对结点判空后按照顺序进行对应操作。

中序遍历(midOrder)

//中序遍历
void midOrder (node_bit t) {
    if(t!=NULL) {
	midOrder(t->lchild);
        getx(t);
	midOrder(t->rchild);
    }
}

在中序遍历中,对于任何一个树结点,我们都先遍历该结点的左子树,之后访问该结点再遍历该结点的右子树。

后续遍历(postOrder)

//后序遍历
void postOrder (node_bit t) {
    if(t!=NULL) {
	postOrder(t->lchild);
	postOrder(t->rchild);
        getx(t);
    }
}

在后序遍历中,对于任何一个树结点,我们都先遍历该结点的左子树,再遍历该结点的右子树,最后再访问该节点。