在构造二叉排序树之前,首先了解一下二叉排序树的概念及重要性质
什么是二叉排序树?
二叉排序树(也称二叉查找树)或者是一颗空树,或者是具有以下性质的二叉树:
1)若左子树非空,则左子树上的所有结点的值均小于根节点的值。
2)若右子树非空,则右子树上的所有结点的值均大于根节点的值。
3)左、右子树也分别是一棵二叉排序树(在这里就可以看出,二叉排序树是可以递归定义的)
根据二叉树的定义,左子树结点的值<根结点的值<右子树的值,因此对二叉树中序遍历(LNR)(先访问左子树再访问根结点再访问右子树),可以得到一个递增有序的序列
在了解完什么是二叉树,以及二叉树的一些性质之后我们再来看看如何构造一棵二叉树。
构造一棵二叉排序树,实际上就是对每一个结点的进行插入操作。对每一个结点进行插入的时候,首先要跟根结点进行比较,如果该结点的值比根结点的值要大,则往右与根节点的右孩子进行比较,如果该结点的值比根结点的值要小,则往左与根节点的左孩子进行比较。当进行了第一轮比较之后,此时该节点在根节点左孩子或者根节点的右孩子的位置,这时候就进行第二轮比较,在第二轮比较的时候,把当前要比较的结点又当成一个根节点,重复第一轮的一模一样的比较操作(这里就是递归构造,把一个大规模的问题拆分成相同规模的小问题,运用相同的解决步骤解决问题。此时的大规模问题就是如何根据给定的序列构造一棵二叉排序树,相同规模的小问题就是如何把给定序列的一个结点插入到二叉排序树),当遇到值为空的位置其实也就是空位置的时候,就将当前结点插入。
文字的表述或许有点绕,下面我们来看图像!
假设给定的序列是(40,72,38,35,67,51,90,8,55,21)
第一个结点的值是40,此时首先跟根节点比较,发现根节点的值为空,将当前结点的值插入
第二个结点的值是72,此时首先与根结点比较,72>40,往右与根节点的右孩子进行比较,此时发现根节点的右孩子为空,将此结点插入到根节点的右孩子的位置。
第三个结点的值是38,此时首先与根节点40相比较,38<40,往左与根节点的左孩子进行比较,此时发现根节点40的左孩子为空,将此结点插入到根节点的左孩子位置。
第四个结点的值是35,此时首先与根节点40比较,35<40,往左与根节点40的左孩子进行比较,此时发现第四个结点的值小于根结点40的左孩子的值 35<38。这个时候把38作为一个新的树根结点,由于35<38,往左与根结点38的左孩子进行比较,此时发现根结点38的左孩子为空,将此结点插入到根结点38的左孩子的位置。
第五个结点的值是67,此时首先与根节点40比较,67>40,往右与根节点40的右孩子进行比较,此时发现第五个结点的值小于根结点40的右孩子的值 67<72。这个时候把72作为一个新的树根结点,由于67<72,往左与根结点72的左孩子进行比较,此时发现根结点72的左孩子为空,将此结点插入到根结点72的左孩子的位置。
第六个结点的值是51,此时首先与根节点40比较,51>40,往右与根节点40的右孩子进行比较,此时发现第六个结点的值小于根结点40的右孩子的值51 <72。这个时候把72作为一个新的树根结点,由于51<72,往左与根结点72的左孩子进行比较,此时发现第六个结点的值小于根结点为72的左孩子的值51<67,这个时候把67作为一个新的树的根结点,由于51<67,往左与根结点为67的左孩子进行比较,此时发现根结点为67的左孩子为空,将此结点插入到根结点67的左孩子的位置。
第七个结点的值是90,此时首先与根节点40比较,90>40,往右与根节点40的右孩子进行比较,此时发现第七个结点的值大于根结点40的右孩子的值 90>72。这个时候把72作为一个新的树根结点,由于90>72,往右与根结点72的右孩子进行比较,此时发现根结点72的右孩子为空,将此结点插入到根结点72的右孩子的位置。
第八个结点的值是8,此时首先与根节点40比较,8<40,往左与根节点40的左孩子进行比较,此时发现第八个结点的值小于根结点40的左孩子的值8<38。这个时候把38作为一个新的树根结点,由于8<38,往左与根结点38的左孩子进行比较,此时发现第八个结点的值小于根结点为38的左孩子的值8<35,这个时候把35作为一个新的树的根结点,由于8<35,往左与根结点为35的左孩子进行比较,此时发现根结点为35的左孩子为空,将此结点插入到根结点35的左孩子的位置。
第九个结点的值是55,此时首先与根节点40比较,55>40,往右与根节点40的右孩子进行比较,此时发现第九个结点的值小于根结点40的右孩子的值55<72。这个时候把72作为一个新的树根结点,由于55<72,往左与根结点72的左孩子进行比较,此时发现第九个结点的值小于根结点为72的左孩子的值55<67,这个时候把67作为一个新的树的根结点,由于55<67,往左与根结点为67的左孩子进行比较,此时发现第九个结点的值大于根结点为67的左孩子的值55>51,这个时候把51作为一个新的树的根结点,由于55>51,往右与根结点为51的右孩子进行比较,此时发现根结点为51的右孩子为空,将此结点插入到根结点51的右孩子的位置。
第十个结点的值是21,此时首先与根节点40比较,21<40,往左与根节点40的左孩子进行比较,此时发现第十个结点的值小于根结点40的左孩子的值21<38。这个时候把38作为一个新的树根结点,由于21<38,往左与根结点32的左孩子进行比较,此时发现第十个结点的值小于根结点为38的左孩子的值21<35,这个时候把35作为一个新的树的根结点,由于21<35,往左与根结点为35的左孩子进行比较,此时发现第十个结点的值大于根结点为35的左孩子的值21>8,这个时候把8作为一个新的树的根结点,由于21>8,往右与根结点为8的右孩子进行比较,此时发现根结点为8的右孩子为空,将此结点插入到根结点8的右孩子的位置。
当十个结点全部插入完成后,这棵二叉排序树就构造好了,我们可以回过头来再检查是不是每一个的子树的左孩子的值<根结点的值<右子树的值,在检查完之后,发现的确是这样的,那么这棵二叉树的构造就是正确的!
上面是一个我们能够用自己语言表述出来的构造二叉树的过程,那么我们如何用代码实现呢?
根据上面的构造过程就可以知道,构造一棵二叉树实际上就是将每一个结点进行插入操作,而每一个结点的插入都会从树的根结点开始比较,当遇到空的位置的时候就插入。实际上这是一个重复操作的过程,所以我们考虑用递归构造二叉树。
下面是构造二叉树的代码
//构造二叉排序树
void Creat_BST(BiTree &T,KeyType str[ ],int n)//第一个参数是树的根结点有返回值
第二个参数是要传入的序列
第三个参数是要插入的结点的个数,其实就是序列
里面有几个值
{
T=NULL; //初始时T为空树
int i=0; //从数组下标为0的值开始插入
while(i<n) { //依次将每个关键字插入到二叉排序树中
BST_Insert(T,str[i]); //调用插入函数,第一个参数是根结点的值
第二个参数是要插入的关键字
}
}
每个结点的插入操作-->BST_Insert(BiTree &T,KeyType k)
int BST_Insert(BiTree &T,KeyType k) //第一个参数是树的根节点,第二个参数是当前结点的值
//每次比较都是根结点和当前结点的值进行比较,所以传入这两个参数
{
if(T == NULL) //遇到空位置,将当前结点插入
{
T=(BiTree)malloc(sizeof(BSTNode)); //给结点分配空间
T->data=k; //将值赋给分配的空间,就是插入的过程
T->lchild=T->rchild=NULL; //将这个插入的结点的左孩子右孩子都置为空
return 1; //返回值则表示插入成功
}
else if(k==T->data) //如果当前结点已经存在,就不用再插入了,因为有些序列里面有重复的元素
return 0;
else if(k<t->data)
return BST_Insert(T->lchild,k); //如果要插入的结点的值小于根结点的值,那么往左,与根
节点的左孩子进行比较,将根结点的左孩子变为新的根结点
else
return BST_Insert(T->rchild,k); //如果要插入的结点的值大于根结点的值,那么往右,与根
节点的右孩子进行比较,将根结点的右孩子变为新的根结点
}
二叉树的构造最重要的就是要理解整个树的构造实际上是每一个结点的插入过程
最后总结一下:
1.二叉树排序树的左孩子的值<根结点的值<右孩子的值
2.如果对二叉树排序树进行中序遍历,则会得到一个递增有序的序列
3..二叉树排序树的构造是一个递归的过程,每次考虑的是当前结点的插入到哪一个位置