二叉查找树定义与C源码
来源:互联网 发布:网络语小主是什么意思 编辑:程序博客网 时间:2024/05/17 23:30
一、定义
二叉查找(搜索)树(Binary Search Tree)。其定义为:二叉查找树或者是空树,或者是满足如下性质的二叉树:
①若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
②若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
③左、右子树本身又各是一棵二叉查找树。
上述性质简称二叉查找树性质(BST性质),故二叉查找树实际上是满足BST性质的二叉树。
二、特点
由BST性质可得:
(1) 二叉查找树中任一结点x,其左(右)子树中任一结点y(若存在)的关键字必小(大)于x的关键字。
(2) 二叉查找树中,各结点关键字是惟一的。注意:实际应用中,不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉查找树定义中BST性质(1)里的"小于"改为"大于等于",或将BST性质(2)里的"大于"改为"小于等于",甚至可同时修改这两个性质。
(3) 按中序遍历该树所得到的中序序列是一个递增有序序列。如下图所示的两棵树均是二叉查找树,它们的中序序列均为有序序列:2,3,4,5,7,8。
三、插入运算
在二叉查找树中插入新结点,要保证插入后仍满足BST性质。其插入过程是:
(a)若二叉查找树T为空,则为待插入的关键字key申请一个新结点,并令其为根;
(b)若二叉查找树T不为空,则将key和根的关键字比较:
(i)若二者相等,则说明树中已有此关键字key,无须插入。
(ii)若key<T→key,则将key插入根的左子树中。
(iii)若key>T→key,则将它插入根的右子树中。
子树中的插入过程与上述的树中插入过程相同。如此进行下去,直到将key作为一个新的叶结点的关键字插入到二叉查找树中,或者直到发现树中已有此关键字为止。
注意:输入序列决定了二叉查找树的形态。二叉查找树的中序序列是一个有序序列。所以对于一个任意的关键字序列构造一棵二叉查找树,其实质是对此关键字序列进行排序,使其变为有序序列。因此,人们又常常将二叉查找树称为二叉排序树。通常将这种排序称为树排序(Tree Sort),可以证明这种排序的平均执行时间亦为O(nlgn)。对相同的输入实例,树排序的执行时间约为堆排序的2至3倍。因此在一般情况下,构造二叉排序树的目的并非为了排序,而是用它来加速查找,这是因为在一个有序的集合上查找通常比在无序集合上查找更快,“查找树"的名称也由此而来。
四、删除运算
从二叉查找树中删除一个结点,不能把以该结点为根的子树都删去,并且还要保证删除后所得的二叉树仍然满足BST性质。
一般步骤:
(a) 进行查找。查找时,令p指向当前访问到的结点,parent指向其双亲(其初值为NULL)。若树中找不到被删结点则返回,否则被删结点是p。
(b) 删去p。删去p时,应将p的子树(若有)仍连接在树上且保持BST性质不变。按p的孩子数目分三种情况进行处理。
(i)p是叶子(即它的孩子数为0),无须连接p的子树,只需将p的双亲parent中指向p的指针域置空即可。
(ii)p只有一个孩子child,只需将child和p的双亲直接连接后,即可删去p。注意,p既可能是parent的左孩子也可能是其右孩子,而child可能是p的左孩子或右孩子,故共有4种状态。
(iii)p有两个孩子,先令q=p,将被删结点的地址保存在q中;然后找q的中序后继p,并在查找过程中仍用parent记住p的双亲位置。q的中序后继p一定是q的右子树中最左下的结点,它无左子树。因此,可以将删去q的操作转换为删去的p的操作,即在释放结点p之前将其数据复制到q中,就相当于删去了q。如下图BST中删除37:
五、查找运算
在BST中查找一个值。如果树中的某个值与要查找的值相等,则返回这个结点,否则报告查找不成功。
进行查找时,先用目标值与树根的值相比较。如果相等,则找到;否则,如果目标值大于根的值,则继续在根的右子树中继续查找;如果目标值小于根的值,则继续在根的左子树中继续查找。
相同的关键字集合可能构成多棵不同的BST树。查找目标过程中,从根到目标结点之间的路径上的结点个数就是要进行比较的次数。树越平衡,要比较的次数越少。
如果二叉树接近平衡,则在树中查找一个结点时,需要进行的比较次数接近O(log n)。如果树退化为一个直链,则树的查找类似于顺序查找,其比较次数接近于O(n) ,这是树查找中的最坏时间复杂度。
一般不能预测要建立的二叉树的树型。实际上,如果关键字的排列是随机的,则建立的二叉树的树型将比较平衡,查找的效率接近二分查找。
六、代码部分
#include <stdio.h>#include <stdlib.h>typedef char Key_t;typedef struct TreeNode{ Key_t key; struct TreeNode *left, *right; struct TreeNode *parent;}Tnode;//查到指定key的Tnode结点Tnode *search(Tnode *root, Key_t key){ if(NULL == root) return NULL; //二叉树二分查找 if(key > root->key) return search(root->right, key); else if(key < root->key) return search(root->left, key); else return root;}//查找一个P节点的前驱节点Tnode *search_former(Tnode *p){ if(NULL == p) return p;//P结点有左子树,此左子树下的最右子树便是P的前驱 if(p->left){ p = p->left; while(p->right) p = p->right; return p; }else{//P没有左子树,向上查找到,第一个开始折向右边的结点便是P的前驱 while(p->parent){ if(p->parent->right == p) break; p = p->parent; } return p->parent; }}Tnode *search_latter(Tnode *p){ if(NULL == p) return p;//P结点有右子树,此右子树下的最左子树便是P的后继 if(p->right){ p = p->right; while(p->left) p = p->left; return p; }else{//P没有右子树,向上查找到,第一个开始折向左边的结点便是P的后继 while(p->parent){ if(p->parent->left == p) break; p = p->parent; } return p->parent; }}//delete_tnode中调用,是删除操作的递归部分。void part_of_dele(Tnode **root, Tnode *p){//没有左右子节点时,直接删除P结点,要将在P的父指向P的指针要置空 if(!p->left && !p->right){ //如果P节点的父节点不为空,即P不是根节点 if(p->parent){ if(p == p->parent->left) p->parent->left = NULL; else if(p == p->parent->right) p->parent->right = NULL; }else{ *root = NULL; } }//P有左节点,没有右节点时,分三种情况 else if(p->left && !p->right){ //P为根节点,设置根节点为p->left if(!p->parent){ *root = p->left; } //P为父节点的左分支,将p->left的父节点指向P的父节点,p父节点左分支指向p->left else if(p == p->parent->left){ p->parent->left = p->left; p->left->parent = p->parent; } //P为父节点的右分支,将p->left的父节点指向P的父节点,p父节点右分支指向p->left else if(p == p->parent->right){ p->parent->right = p->left; p->left->parent = p->parent; } }//P有右节点,没有左节点时,分三种情况 else if(!p->left && p->right){ //P为根节点,设置根节点为p->right if(!p->parent){ *root = p->right; } //P为父节点的左分支,将p->right的父节点指向P的父节点,p父节点左分支指向p->right else if(p == p->parent->left){ p->parent->left = p->right; p->right->parent = p->parent; } //P为父节点的右分支,将p->right的父节点指向P的父节点,p父节点右分支指向p->right else if(p == p->parent->right){ p->parent->right = p->right; p->right->parent = p->parent; } }//P即有左节点也有右节点时,查找P的后继结点q,将q值付于p,通过递归将q值删除 else if(p->left && p->right){ Tnode *q = search_latter(p); p->key = q->key; part_of_dele(root, q); return; }//将p指向的空间释放 free(p); return;}//删除指定KEY的节点void delete_tnode(Tnode **root, Key_t key){//在二叉树中查找值为key的节点p,如果为NULL,意味着没有此节点 Tnode *p = search(*root, key); printf("p->key:%d\n", p->key); if(NULL == p) return; part_of_dele(root, p);}//insert_tnode中调用,是插入操作的递归部分。void part_of_insert(Tnode **root, Tnode *p){//当第一次调用时,设置根节点 if(NULL == *root){ p->parent = NULL; *root = p; return; }//左分支为空,且key值小于根节点时,插入到左分支 else if(NULL == (*root)->left && (*root)->key > p->key){ p->parent = *root; (*root)->left = p; return; }//右分支为空,且key值大于等于根节点时,插入到右分支 else if(NULL == (*root)->right && (*root)->key <= p->key){ p->parent = *root; (*root)->right = p; return; }//当两分支都不空时,递归查找并插入到相应位置 if((*root)->key > p->key) part_of_insert(&(*root)->left, p); else if((*root)->key <= p->key) part_of_insert(&(*root)->right, p); return;}//插入节点操作int insert_tnode(Tnode **root, Key_t key){ Tnode *p = (Tnode *)malloc(sizeof(Tnode)); if(NULL == p) return -1; p->key = key; p->left = p->right = p->parent = NULL;//递归插入部分 part_of_insert(root, p); return 0;}//通过数组生成二叉树,root根节点,table数组指针,count数组大小int create_tree(Tnode **root, Key_t *table, int count){ int i; int err = -1; *root = NULL; for(i = 0; i < count; ++i){ err = insert_tnode(root, table[i]); if(err < 0) break; } return err;}//中序遍历二叉树void mid_order(Tnode *root){ if(root == NULL) return; mid_order(root->left); printf("%d ", root->key); mid_order(root->right); return;}//后序摧毁二叉树void destroy_tree(Tnode **root){ if(!*root) return; destroy_tree(&(*root)->left); destroy_tree(&(*root)->right); free(*root); *root = NULL; return;}int main(){ Tnode *root; Tnode *node; int err = -1; Key_t keytable[] = {5,3,6,2,4,7}; err = create_tree(&root, keytable, sizeof(keytable) / sizeof(keytable[0])); if(err < 0){ printf("tree create error!!!\n"); exit(-1); } err = insert_tnode(&root, 6); err = insert_tnode(&root, 6); mid_order(root); printf("\n"); node = search_former(search(root, 3)); printf("%d\n", node->key); node = search_latter(search(root, 3)); printf("%d\n", node->key); delete_tnode(&root, 5); delete_tnode(&root, 6); delete_tnode(&root, 7); delete_tnode(&root, 3); mid_order(root); printf("\n"); destroy_tree(&root); return 0;}
引用:
http://www.cnblogs.com/xiaosuo/archive/2010/02/10/1656183.html
http://blog.csdn.net/touch_2011/article/details/6831924
- 二叉查找树定义与C源码
- 二叉查找树的定义
- 二叉查找树(C#)
- <<c>>二叉查找树
- 树定义及二叉查找树实现
- 注意二叉查找树的定义
- 数据结构与算法(C语言版)__二叉查找树
- 【二叉查找树】c实现
- C++模板实现二叉查找树(一 树的数据结构定义与节点插入)
- 二叉树与二叉查找树
- 二叉树与二叉查找树
- 二叉树与二叉查找树
- 二叉树定义与存储
- 二叉查找树与中间值查找
- 折半查找与二叉查找树
- 查找--二叉查找树分析与实现
- 二叉树建立与查找
- 二叉树查找与清除
- RSA算法类(PHP)
- linux创建raw设备
- Linux中的同步机制 -- Futex
- Linux环境安装配置Swftools
- 非等值sql的优化
- 二叉查找树定义与C源码
- JAVA中String对象split方法注意事项
- magento 常用方法
- 项目中配置多个数据源
- 【转载】使用JAVA在TOMCAT下实现计划任务监听器
- oracle database 11g 第一版的安装
- Rime&Contiki Chameleon
- 录音
- MDL(内存描述符表) 详解