二查排序树

来源:互联网 发布:德国禁止儿童手表知乎 编辑:程序博客网 时间:2024/05/17 20:31

二叉排序树(Binary Sort Tree),又称为二查搜索树。它要么是一颗空树,要么是符合性质的二叉树

  • 若它的左子树不为空,那么左子树上的所有结点的值均小于它的根结点的值
  • 若它的右子树不为空,那么右子树上的所有结点的值均大于它的根结点的值
  • 它的左右子树也分别为二叉排序树

以上性质就造成了左结点<根结点<右结点,那么可以想象一下,如果对二叉排序树进行中序遍历,很明显,我们得到的是一个递增的序列。我们可以根据这个,来判断自己建立的二叉排序树的正确性。需要说明的是,二叉排序树虽然带排序俩字,但却不是为了排序,而是为了提高查找和插入删除的效率。

接下来,我们看一下,二叉排序树的查找是如何实现的。首先给出二叉树的结点结构体

//二叉树结点结构定义typedef struct BSTNode{int m_data;BSTNode *Lchild,*Rchild;}BSTNode,*BiTree;

二叉排序树查找代码如下

//在二查搜索树中查找特定的值,返回值在树中的地址//递归查找树pRoot中是否存在value//指针pfather指向pRoot的双亲,当pRoot是开始的跟结点时,值为NULL//指针pValue,指向找到value的结点,找不到则指向最后一个结点bool SearchBST(BiTree pRoot,int value,BiTree pfather,BiTree *pValue){if (!pRoot)                //当最后发现value不存在时,pfther指向最后一个结点{*pValue = pfather;return false;}else if (value == pRoot->m_data)    //等于根结点,返回true{*pValue = pRoot;return true;}else if (value < pRoot->m_data)           //比根结点小,递归左子树查询{SearchBST(pRoot->Lchild,value,pRoot,pValue);}else                                                    SearchBST(pRoot->Rchild,value,pRoot,pValue); //递归右子树查询}
以上代码很好理解,接下我们看一下如何对二叉排序树进行插入操作。

//向二叉排序树中插入一个元素value//*pRoot代表一颗二叉排序树bool insertBST(BiTree * pRoot, int value){BiTree p = NULL;                 //用于接收返回的查找的结点地址或路径中最后一个结点地址if (!SearchBST(*pRoot,value,NULL,&p))   //二叉排序树中不能有重复的值{BiTree s = new BSTNode();s->m_data = value;s->Lchild = NULL;s->Rchild = NULL;if (NULL == *pRoot)       //如果传过来的是一颗空树{*pRoot = s;}else if (value < p->m_data)  //比最后一个结点小,放左边{p->Lchild = s;}elsep->Rchild = s;           //否则放右边return true;}else{cout<<"invalid value"<<endl;return false;}}
插入有了,那么创建一颗二叉排序树的可想而知了

//创建二叉排序树//state用于判断上一次插入是否成功,不成功返回bool creatBinarySortTree(BiTree *pRoot,int *array, int size){bool state = true;for (int i = 0;(i < size)&&state; i++){state = insertBST(pRoot, array[i]);}return state;}
//销毁二查搜索树void DestoryBST(BiTree pRoot){if(NULL != pRoot){DestoryBST(pRoot->Lchild);  //释放左子树DestoryBST(pRoot->Rchild);  //释放右子树delete pRoot;}}
好了,至此我们创建了一棵二叉排序树,可以对它进行查找,插入操作了,那么问题来了,我要是想对它进行删除怎么办?对于二叉排序树可是“请神容易送神难”。想想删除可那么简单,因为不能因为你删除了一个结点,这棵树就不是二叉排序树了吧?所以删除时,要考虑多种情况。
  • 删除的是叶子结点,可以想象,删除叶子结点不会对其他结点照成任何影响,此种情况可以直接删除。
  • 删除的结点仅有左子树或者右子树,这种情况也相对容易,我把那个结点删除了,把它的左子树或者右子树整体移动到它的位置就可以了。
  • 左右子树都存在,那么找要删除结点的前驱或者后继去替换它,然后把它的前驱或者后继删掉。

接下来看看代码怎么怎么写

<pre name="code" class="cpp">bool deleteBST(BiTree *pRoot,int key){if (!*pRoot)         //删除的结点不存在return false;else{if (key == (*pRoot)->m_data)        //找到值等于key的结点returnDelete(pRoot);else if (key < (*pRoot)->m_data){return deleteBST(&(*pRoot)->Lchild,key);}elsereturn deleteBST(&(*pRoot)->Rchild,key);}}bool Delete(BiTree * pRoot){BiTree q = NULL ,s = NULL;if ((*pRoot)->Lchild == NULL)   //左子树为空,重接右子树{q = *pRoot;(*pRoot) = (*pRoot)->Rchild;free(q);}else if ((*pRoot)->Rchild == NULL) //右子树为空,重接左子树{q =*pRoot;*pRoot = (*pRoot)->Lchild;free(q);}else{q = *pRoot;           //q指向待删除的结点s =(*pRoot)->Lchild;   //while (s->Rchild)        //前驱没有右子树{q = s;s = s->Rchild;}(*pRoot)->m_data = s->m_data;  //s指向被删除结点的前驱if (q != *pRoot){q->Rchild = s->Lchild;   //前驱的左孩子做前驱父节点的右孩子}else{q->Lchild = s->Lchild; //重接q的左子树}free(s);}return true;}

二叉排序树的查找,走的就是根节点到要查找的结点路径,其比较次数等于给定值的结点在二叉树的深度,极端情况下,最少为1,即根节点就是要找的结点,最多不会超过树的深度,也就是说,查找的时间很大层度上取决于树的形状,但是二叉排序树的形状又是不确定的,那么问题有来了,由于形状的不同,查找的时间差异很大,怎么样才能保证树的深度呢,嘿嘿,往下就涉及到了平衡二叉树。


0 0