数据结构(六)查找---二叉搜索树(排序树)
前提
前面的查找我们都是静态查找,因为数据集是有序存放,查找的方法有多种,可以使用折半,插值,斐波那契等,但是因为有序,在插入和删除操作上的效率并不高。
这时我们就需要一种动态查找方法,既可以高效实现查找,又可以使得插入和删除效率不错,这时我们可以考虑二叉排序树
二叉排序树
一:定义
又称为二叉搜索树(查找树),是一棵树,可以为空,但是需要满足以下性质:
1.非空左子树的所有键值小于其根节点的键值 2.非空右子树的所有键值大于其根节点的键值 3.左右子树都是二叉搜索树
二:操作
查找
/* BiTree T 我们要搜索的二叉树 ElemType key我们要搜索的关键字 BiTree F 记录下我们的当前搜索子树的双亲结点 BiTree* P 当我们插入之前,会先搜索是否存在数据,若存在,不插入,若不存在,我们通过这个可以获取我们要插入的位置,直接插入即可 */
Status SearchBST(BiTree T, ElemType key, BiTree F, BiTree* P)
/* BiTree T 我们要搜索的二叉树 ElemType key我们要搜索的关键字 BiTree F 记录下我们的当前搜索子树的双亲结点 BiTree* P 当我们插入之前,会先搜索是否存在数据,若存在,不插入,若不存在,我们通过这个可以获取我们要插入的位置,直接插入即可 */ Status SearchBST(BiTree T, ElemType key, BiTree F, BiTree* P) { if (!T) { *P = F; //若是未找到则返回父节点位置 return FALSE; } else { if (T->data == key) { *P = T; //若是找到则P返回该结点位置 return TRUE; } else if (T->data < key) return SearchBST(T->rchild, key, T, P); else return SearchBST(T->lchild, key, T, P); } }
插入
Status InsertBST(BiTree* T, int key)
由于二叉搜索树的特殊性质确定了二叉搜索树中每个元素只可能出现一次,所以在插入的过程中如果发现这个元素已经存在于二叉搜索树中,就不进行插入。否则就查找合适的位置进行插入。
第一种情况:树为空--->直接插入,返回TRUE
第二种情况:要插入的元素已经存在,如果在二叉搜索树中已经存在该元素,则不再进行插入,直接return false;
第三种情况:能够找到合适的位置,通过上面搜索函数搜索返回其父节点,直接插入即可
Status InsertBST(BiTree* T, int key) { BiTree P,s; if (!T) return ERROR; if (!SearchBST(*T, key, NULL, &P)) { //没有查找到有重复数据,获取到了应该插入的位置 s = (BiTree)malloc(sizeof(BiTNode)); s->data = key; s->lchild = s->rchild = NULL; if (!P) //空树 *T = s; else if (key < P->data) //插入左子树 P->lchild = s; else P->rchild = s; return OK; } else return ERROR; }
删除(上面两种比较容易,不再详说,删除的情况较多)
Status DeleteBST(BiTree* T, int key)
1.删除的结点是叶子结点,直接删除(直接判断左右指针是否存在)
2.删除的结点只有左孩子,直接让该结点的父节点指向其左孩子
3.删除的结点只有右孩子,直接让该结点的父节点指向其右孩子
注意:其实1可以由2,3代替,因为2,3一起就包括了左右子树都不存在的情况,其中2,3指向的左右子树为NULL就是1情况
4.删除的结点含有左右孩子,需要再进行分类讨论(重点)<这里考虑了一种,就是使用右子树最小来进行替换,还可以选择左子树最大来替换>
注意:上面两种情况可以归为一种,就是第二张图片那样,直接让黄色父节点左指针指向其绿色结点的右孩子。
第三种情况:是较为特殊的,绿色(不存在左子树)的父节点直接是要删除的红色结点,我们要使得其新的结点右指针指向原来绿色的右指针
Status Delete(BiTree* T) { BiTree q,f; if (!*T) return ERROR; if (!(*T)->lchild) //若是左子树不存在,我们只需要接到右子树 { q = *T; *T = (*T)->rchild; free(q); } else if (!(*T)->rchild) //若右子树不存在,接入左子树 { q = *T; *T = (*T)->lchild; free(q); } else //两边都存在,我们可以选择将右子树最小,或者左子树最大接入,这里选择右子树最小 { f = *T; //f指向q的双亲结点 q = (*T)->rchild; while (q) { f = q; q = q->lchild; //找到右子树最小,注意其可能存在右子树,我们要进行保存,接入其父节点 } //将最小的数据更新到根节点处即可,然后记录最小点处,删除即可 (*T)->data = q->data; if (f != (*T)) f->lchild = q->rchild; else f->rchild = q->rchild; //当右子树是一个右斜树 free(q); } return TRUE; } Status DeleteBST(BiTree* T, int key) { if (!*T) return ERROR; else { if ((*T)->data == key) //找到了,开始删除 { //删除该结点,由于要分情况讨论,所以另外写一个函数 } else if ((*T)->data < key) DeleteBST(&(*T)->rchild, key); else DeleteBST(&(*T)->lchild, key); } }
三:代码实现
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status; typedef int ElemType; typedef struct _BiTNode { ElemType data; struct _BiTNode* lchild, *rchild; }BiTNode,*BiTree; /* BiTree T 我们要搜索的二叉树 ElemType key我们要搜索的关键字 BiTree F 记录下我们的当前搜索子树的双亲结点 BiTree* P 当我们插入之前,会先搜索是否存在数据,若存在,不插入,若不存在,我们通过这个可以获取我们要插入的位置,直接插入即可 */ Status SearchBST(BiTree T, ElemType key, BiTree F, BiTree* P) { if (!T) { *P = F; //若是未找到则返回父节点位置 return FALSE; } else { if (T->data == key) { *P = T; //若是找到则P返回该结点位置 return TRUE; } else if (T->data < key) return SearchBST(T->rchild, key, T, P); else return SearchBST(T->lchild, key, T, P); } } Status InsertBST(BiTree* T, int key) { BiTree P,s; if (!T) return ERROR; if (!SearchBST(*T, key, NULL, &P)) { //没有查找到有重复数据,获取到了应该插入的位置 s = (BiTree)malloc(sizeof(BiTNode)); s->data = key; s->lchild = s->rchild = NULL; if (!P) //空树 *T = s; else if (key < P->data) //插入左子树 P->lchild = s; else P->rchild = s; return OK; } else return ERROR; } Status Delete(BiTree* T) { BiTree q,f; if (!*T) return ERROR; if (!(*T)->lchild) //若是左子树不存在,我们只需要接到右子树 { q = *T; *T = (*T)->rchild; free(q); } else if (!(*T)->rchild) //若右子树不存在,接入左子树 { q = *T; *T = (*T)->lchild; free(q); } else //两边都存在,我们可以选择将右子树最小,或者左子树最大接入,这里选择右子树最小 { f = *T; //f指向q的双亲结点 q = (*T)->rchild; while (q->lchild) { f = q; q = q->lchild; //找到右子树最小,注意其可能存在右子树,我们要进行保存,接入其父节点 } //将最小的数据更新到根节点处即可,然后记录最小点处,删除即可 (*T)->data = q->data; if (f != (*T)) f->lchild = q->rchild; else f->rchild = q->rchild; //当右子树是一个右斜树 free(q); } return TRUE; } Status DeleteBST(BiTree* T, int key) { if (!T || !*T) return ERROR; else { if ((*T)->data == key) //找到了,开始删除 { //删除该结点,由于要分情况讨论,所以另外写一个函数 } else if ((*T)->data < key) DeleteBST(&(*T)->rchild, key); else DeleteBST(&(*T)->lchild, key); } } int main() { int i,ret; int a[10] = { 62, 88, 58, 47, 35, 73, 51, 99, 37, 93 }; BiTree T = NULL; BiTree P=NULL; for (i = 0; i < 10;i++) InsertBST(&T, a[i]); ret=SearchBST(T, 47, NULL, &P); if (ret) printf("find node\n"); DeleteBST(&T, 73); ret = SearchBST(T, 47, NULL, &P); if (ret) printf("not find node ,it has deleted\n"); system("pause"); return 0; }