二叉排序树
-
思考:
给定一组数列{7,3,10,12,5,1,9},要求能够高效的完成对数据的查询和添加
我们可能有以下解决方案:
1.使用数组
- (1)数组未排序,优点:直接在数组尾添加,速度快。缺点:查找速度慢.
- (2)数组排序,优点:可以使用二分查找,查找速度快,缺点:为了保证数组有序在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢。
2.使用链式存储--链表
- 不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体移动。
3.使用二叉排序树
二叉排序树介绍:
二叉排序树:BST:( Binary Sort( Search)Tree),
1.要么是一棵空树
2.要么对于二叉排序树的任何一个非叶子节点要求
左子节点的值比当前节点的值小
右子节点的值比当前节点的值大
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点
比如:针对前面的数据(7,3,10,12,5,1,9),对应的二叉排序树为:
解释:
- 首先它先存入7,然后发现3比7小,所以把3作为它的左节点
- 发现10比7大,放在它的右子节点
- 12比7大,然后和10比,同样比10大,所以放在10的右子节点
- 以此类推
性质:按照中序遍历把节点打印,将得到由小到大的排列
二叉搜索树的结构:
typedef struct NODE
{
int key;
struct node* left;
struct node* right;
}node,*bst;
关于二叉搜索树的基本操作:
-
查找:
- 当节点root为NULL的时候说明没有找到,查找失败,返回NULL;
- 如果想要查找的值x等于当前根结点数据域root->data,说明查找成功,访问该节点,输出该值root->key
- 如果想要查找的值x小于当前根结点数据域root->data,说明应该往左子树查找,所以向root->lchild递归
- 如果想要查找的值x大于当前根结点数据域root->data,说明应该往右子树查找,所以向root->rchild递归
bst find(int x, bst root) //查找操作,这里我们没有对传入的指针进行修改,故不需要使用引用!
{
if (root == NULL)
return NULL;
if (root->key < x)
return find(x, root->right);
else if (root->key > x)
return find(x, root->left);
else
return root;
}
插入:
- 如果树为空则创建一棵树,该值作为起点
- 如果相等则return,(二叉搜索树不能有相同的值,但是有的版本说如果真的有相同的值可以插入左边或者右边,但是依旧要保持左小右大的原则)
- 如果大于就递归右子树
- 如果小于就递归左子树
void insert(node* &root,int x){ //insert函数将在二叉树中插入一个数据域为x的新结点(注意参数root要加引用&)
if(root==NULL){
root=newNode(x);
return;
}
if(x==root->data){
return ;
}
else if(x<root->data){
insert(root->lchild,x);
}
else{
insert(root->rchild,x);
}
}
其他:
当我们有了插入函数之后,我们也可以通过插入函数进行创建二叉搜索树:
其中参数是要创建树的数组
node* Create(int data[],int n){
node* root=NULL;
for(int i=0;i<n;i++){
insert(root,data[i]);
}
return root;
}
删除:
- 把以二叉查找树中比结点权值小的最大结点称为该结点的前驱,而把比权值大的最小结点称为该结点后继。显然,结点的前驱是该结点左子树中的最右结点,而结点的后继是右子树中的最左结点。
- 如果当前结点root为空,说明不存在权值为给定权值x的结点,直接返回
- 如果当前结点root的权值恰好为给定权值x,说明找到了想要删除的结点,此时进行删除处理:a)如果当前结点root不存在左右孩子,说明是叶子结点,直接删除;b)如果当前结点存在左孩子,那么在左子树中寻找结点前驱pre,然后让pre的数据覆盖root,接着删除左子树中结点pre;c)如果当前结点存在右孩子,那么在右子树中寻找结点后继next,然后让next的数据覆盖root,接着删除右子树中结点next;
- 如果当前结点root的权值大于给定权值x,则在左子树中递归删除权值为x的结点
- 如果当前结点root的权值小于给定权值x,则在右子树中递归删除权值为x的结点
void deleteNode(node* &root,int x){
if(root==NULL) return ;
if(root->data==x){
if(root->lchild==NULL && root->rchild==NULL){
root=NULL;
}
else if(root->lchild!=NULL){
node* pre=findMax(root->lchild);
root->data=pre->data;
deleteNode(root->lchild,pre->data);
}
else{
node* next=findMin(root->rchild);
root->data=next->data;
deleteNode(root->rchild,next->data);
}
}
else if(root->data>x){
deleteNode(root->lchild,x);
}
else{
deletdNode(root->rchild,x);
}
}
最大值最小值:
通过以上的学习就可以知道 最小值就左边的数,最大值就是最右边的数 (所以递归到最左边/右边就ok) 这里就不多说了;
//寻找以root为根结点的树中的最大权值结点
node* findMax(node* root){
while(root->rchild!=NULL){
root=root->rchild;
}
return root;
}
//寻找以root为根结点的树中的最小权值结点
node* findMin(node* root){
while(root->lchild!=NULL){
root=root->lchild;
}
return root;
遍历:中序的遍历结果是有序的
关于遍历可以看我的其他两篇文章:
小白开始学二叉树:https://blog.csdn.net/alzzw/article/details/97283324
线索二叉树:https://blog.csdn.net/alzzw/article/details/97423394
哈夫曼树:https://blog.csdn.net/alzzw/article/details/97809047
平衡二叉树:https://blog.csdn.net/alzzw/article/details/97613193
完
参考:https://blog.csdn.net/object__/article/details/81916364