二叉搜索树(Binary Search Tree)

什么是二叉查找树

二叉搜索树,以下简称BST。他有如下特性:

  • 它是一个二叉树。
  • 如果x是BST的一个节点,L为x的左子树中的任意一个节点,R为x的右子树中的任意一个节点,满足
    L.key \leq x.key \geq R.key

搜索树支持很多动态集操作(dynamic-set),比如SEARCH, MINIMUM, MAXIMUM, PREDECESSOR, SUCCESSOR,INSERT, 和DELETE 因此,我们可以将搜索树用作字典和优先级队列

BST

如果中序遍历BST将会得到一个有序的序列。这个是显然的,二叉树的性质就是左边元素小,右边元素大,按左中右的顺序遍历得到的就是有序的。

BST

BST查询相关的操作

①元素查找
在BST中查找关键字为k的节点,查找成功返回指向该节点的指针,失败返回NULL;
②最大值和最小值
最大值在最右边
最小值在最左边
③前驱和后继
节点x的前驱是指比他小的最大一个节点
节点x的后继是指比他大的最小一个节点

求节点的前驱和后继节点是对称的,前驱和后继节点可能不存在,比如一个BST中最大值没有后继,最小值没有前驱。下面以求后继为例,求后继节点分为两种情况:

  • case1: 有右孩子,这种情况比较简单s,直接转化为以右孩子为根节点的子树的最小值。即y节点。


    存在右孩子
  • case2:没有右孩子,需要向上查找第一个左拐的祖先


    没有右孩子

We can implement the dynamic-set operations SEARCH, MINIMUM, MAXIMUM,
SUCCESSOR, and PREDECESSOR so that each one runs in O(h) time on a binary
search tree of height h

④插入
插入和查找类似,需要一个尾指针记录插入的位置
⑤删除
删除操作分为三种情况
1.要删除的节点没有左右孩子。直接删除
2.要删除的节点只有左孩子或只有右孩子。例如要删除z,只需要将z的parent q和l链接起来就行。


只有一个孩子

3.要删除的节点既有左孩子也有右孩子 。删除思想是使用后继替换删除的节点转化为第二种情况。根据删除节点的后继是否为删除节点的右孩子,又可以分为两种情况:
3.1 删除节点的后继为右孩子:


后继为右孩子

3.2 删除节点的后继不是右孩子:


后继不是右孩子

c语言实现

BST.h

#ifndef BST_H
#define BST_H 
#endif
#include<stdio.h>
#include<stdlib.h>
typedef  int BSTELEM_TYPE;
//typedef enum{false,true } bool;//定义bool 
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
typedef struct BSTNode{
    struct BSTNode *left;
    struct BSTNode *right;
    struct BSTNode *parent;
    BSTELEM_TYPE key;
}BSTNode,*BSTTree;
//递归查找 
//根据指针x所指的BST中递归的查找关键字等于k 的元素
//若查找成功返回指向该数据元素节点的指针,失败返回null; 
BSTTree Tree_Search(BSTTree x,BSTELEM_TYPE k);
//迭代查找
BSTTree Iterative_Tree_Search(BSTTree x,BSTELEM_TYPE k); 
//查找x指针指向的BST的最小值
BSTTree Tree_Minimum(BSTTree x);
//查找x指针指向的BST的最大值
BSTTree Tree_Maximum(BSTTree x); 
//查找x指针指向的BST的后继 
BSTTree Tree_Successor(BSTTree x); 
BSTTree Tree_Predecessor(BSTTree x);
int Tree_Insert(BSTTree *t,BSTELEM_TYPE k);
//递归实现中序遍历 
void inOrderTraverse(BSTTree t);
void Transplant(BSTTree *x,BSTTree *y);
void Tree_Delete(BSTTree *t, BSTELEM_TYPE k) ;

BST.c

#ifndef BST_C
#define BST_C
#endif

#include "BST.h"

BSTTree Tree_Search(BSTTree x,BSTELEM_TYPE k){
    //x为空或者x指向的元素等于k,查找结束 
    if((x==NULL)||EQ(k,x->key)){
        return x;
    //k 小于x指向的元素,在左子树中查找    
    }else if(LT(k,x->key)) {
        return Tree_Search(x->left,k);
    //k 大于x指向的元素,在右子树中查找 
    }else {
        return Tree_Search(x->right,k);
    }
}
BSTTree Iterative_Tree_Search(BSTTree x,BSTELEM_TYPE k){
    while(x&&!EQ(k,x->key)){
        if(LT(k,x->key)){
            x=x->left; 
        }else{
            x=x->right;
        }
    }
    return x;
}
BSTTree Tree_Minimum(BSTTree x){
    if(!x){
        return NULL; 
    }
    while(!x->left){
        x=x->left;
    }
    return x;
} 
BSTTree Tree_Maximum(BSTTree x){
        if(!x){
        return NULL; 
    }
    while(!x->right){
        x=x->right;
    }
    return x;
} 
BSTTree Tree_Successor(BSTTree x){
    //case 1 有右孩子 ,直接返回右孩子的最小值 
    if(x->right!=NULL){
        return Tree_Minimum(x->right);
    }
    //case 2 没有右孩子,找到第一个左拐的祖先 
    BSTTree y=x->parent;
    while(y!=NULL&&y->right==x){//右拐的都是比他小的,继续循环 
        x=y;
        y=y->parent;
    }
    return y;
     
}
 
BSTTree Tree_Predecessor(BSTTree x){
    //case1 有左孩子,直接返回左孩子的最大值 
    if(x->left!=NULL){
        return Tree_Maximum(x->left);       
    } 
    BSTTree y=x->parent;
    while(y!=NULL&&y->left==x){//左拐的都是比他大的,继续向上
        x=y;
        y=y->parent; 
    } 
    return y;   
}
int Tree_Insert(BSTTree *t,BSTELEM_TYPE k){
    //重复元素不插入 
    if(Iterative_Tree_Search(*t,k)){
        return 0;
    }
    //z为待插入元素,为期动态分配空间同时初始化 
    BSTTree z=(BSTTree)malloc(sizeof(BSTNode));
    z->key=k;
    z->left=z->right=z->parent=NULL;
    //树为空直接插入 
    if(*t==NULL){
        *t=z;
        return 1;
    }
    
    BSTTree y;//记录待插入节点z的父亲指针,尾指针 
    BSTTree x=*t;
    while(x!=NULL){
        y=x;
        if(LT(k,x->key)){
            x=x->left;
        }else{
            x=x->right;
        }
    } 
    //y即为z的父指针 
    z->parent=y;
    if(LT(k,y->key)){
        y->left=z;
    }else{
        y->right=z;
    }
    return 1;
}
void inOrderTraverse(BSTTree t){
    if(t){
        inOrderTraverse(t->left);
        printf("%d",t->key);
        inOrderTraverse(t->right);
    }
    
}

//将x的父节点和y连起来 
void Transplant(BSTTree *x,BSTTree *y){
    if((*x)->parent==NULL){
        return;
    }else if((*x)->parent->left==*x){
        (*x)->parent->left=*y;
    }else{
        (*x)->parent->right=*y;
    }
    if(*y!=NULL){
        (*y)->parent=(*x)->parent;
    }
    
}
void Tree_Delete(BSTTree *t, BSTELEM_TYPE k) {
    BSTTree z=Iterative_Tree_Search(*t,k);
    //要删除的节点不存在,直接返回 
    if(z==NULL){
        return ;
    }
    //左孩子不存在 
    if(z->left==NULL){
        Transplant(&z,&z->right);
    //右孩子不存在 
    }else if(z->right==NULL){
            Transplant(&z,&z->left);
    }else {
        BSTTree y=  Tree_Successor(z);
        if(y->parent!=z){//后继不是右孩子 
            Transplant(&y,&y->right);
            y->right=z->right;
            y->right->parent=y;
            }   
        Transplant(&z,&y) ;
        y->left=z->left;
        y->left->parent=y;
        }
        z->left=z->right=z->parent=NULL;
        free(z); 
    
        
    
}
void createBST(BSTTree *t,int  a[],int n){
    int i=0;
    for(i;i<n;i++){
        Tree_Insert(t,a[i]);
    }
}
int main(){
    BSTTree t=NULL;
    int a[]={10,5,15,18,17,16};
    //创建BST 
    createBST(&t,a,sizeof(a)/sizeof(int));  
    //删除key为15的节点 
    Tree_Delete(&t,15);
    中序遍历 
    inOrderTraverse(t);
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容