查找树
-
定义
定义1:查找树T是一棵二叉树T,它或空,或满足下面三个条件:
(1)如果树T的根节点的左子树非空,那么左子树中的所有结 点的键值都小于T的根结点的键值。
(2)如果树T的根节点的右子树非空,那么右子树中的所有结 点的键值都大于T的根结点的键值。
(3)树T的根结点的左、右子树也都是查找树。定义2:
对于一棵给定的二叉树T,如果树T中的结点的中序是排好序的,那么我们称树T是一棵查找树。
-
查找
NODE* search(NODE* sub_root, char target) { if sub_root == NULL || subroot->data == target) return sub_root; else if (sub_root->data > target) return search (sub_root->lchild, target); else return search (sub_root->rchild, target); } NODE* search (NODE* sub_root, char target) { while (sub_root != NULL && subroot->data != target) if (sub_root->data < target) sub_root=sub_root->rchild; else sub_root=sub_root->lchild; return sub_root; }
//查找的同时获取父结点——为了插入和删除 void search(NODE *t,char target,NODE **p_p,NODE **p_q) //p_p,p_q指向指针变量的指针。 *p_q是指针,指向当前结点; *p_p指向当前结点 的父结点 { *p_p=NULL; *p_q=t; while (*p_q != NULL){ if (*p_q)->data == target) return; //找到结点 *p_p= * p_q; //准备“下移” if (target<(*p_q)->data) *p_q=(*p_q)->lchild; else * p_q=(*p_q)->rchild; }// 算法执行到结束,若*p_q不空,则*p_q所指的结点就是要查找的结点 } /* 算法执行到结束,若*p_q不空,则*p_q所指的结点就是要查找的结点 (找到了),而*p_p指向它的父结点。若此时*p_p为空,则根结点键值符合。 假若*p_q为空,则表示找不到target结点。此时*p_p有两种情况: (1)若为空,则查找树t为空树; (2) *p_p不为空,*p_p指向查找路径的最后一个结点。 */
-
插入
(1)已有键值为target的结点,不执行插入操作;
(2)树空,插入的结点成为树根;
(3)树不空, 寻找合适的位置插入int search_and_insert (NODE **sub_root, char new_data) { if (( *sub_root )== NULL) {*sub_root = ……; return (0);//构造结点成为树根 } else if (new_data <(*sub_root)->data) return search_and_insert(&(*sub_root)->lchild,new_data); else if (new_data >(*sub_root)->data) return search_and_insert(&(*sub_root)->rchild,new_data); else return(1);// 要插入的节点值重复,则插入失败 } int insert (NODE **p_t, char a) { NODE *p,*q,*r; Search (*p_t,a,&p;&q); if (q!=NULL) return (1); r=………… //构造结点r if (p==NULL) *p_t=r; else if (p->data>a) p->lchild=r; else p-rchild=r; return (0); }
-
构建
int main() { BSTNODE *root; root=NULL; int n; char a; cin>>n; while (n--) { cin>>a; insert (&root,a); } return 0; }
-
删除
找到要删除的结点,将它删去,要求删除后的二叉树仍是一棵查找树。
用中序序列检验
int delete (NODE **p_t, char to_delete) { NODE *p,*q,*r; search (*p_t, to_delete,&p;&q); //执行后q如果不空,指向找到的结点 //待删除结点如果不是根,p指向q的父结点 If (q==NULL) return (1); //结点“to_delete” 不在树中 If (p==NULL) //p为NULL,找到的结点为根,被删结点为根结点 if (q->lchild==NULL) *p_t=q->rchild; else { r=q->lchild; while (r->rchid!=NULL) r=r->rchild; //找“最右” r->rchild=q->rchild; *p_t =q->lchild; } //将要被删除的结点q不是根,作为子树的根来处理,同时完成父结点p的指针变化 else if (q->lchild == NULL ) if(q==p->lchild) p->lchild=q->rchild; else p->rchild = q->rchild; else{ r=q->lchild; while (r->rchid!=NULL) r=r->rchild; r->rchild=q->rchild; if (q==p->lchild) p->lchild=q->lchild; else p->rchild = q->lchild; } free (q); return (0); }
-
效率分析
当树中结点尽量靠近树根时,AVG(二叉查找法)的值达 到最小。当查找树退化成链接表时,AVG的值达到最大
-
满树,拟满树,丰满树
设二叉树T有n个结点,i=[ l o g 2 ( n + 1 ) log_2(n+1) log2(n+1)](向下取整),r=n-( 2 i − 1 2^i-1 2i−1)
如果其中( 2 i − 1 2^i-1 2i−1)放满第0至第(i-1)层。
(1)若r=0,则称树T是一棵满二叉树,简称满树。
(2)若r>0,且剩下的r个结点尽量靠左地排列在第i层上,则称树T是一棵拟 满二叉树(完全二叉树)。
(3)若r>0,且剩下的r个结点随意分布在第i层,则称树T是一棵丰满二叉树。如果树T是满树,那么树T一定是拟满树(完全二叉树)和丰满树。 如果树T是拟满树(完全二叉树) ,那么树T一定是丰满树。
拟满树和丰满树在查找效率上没有差别
构造丰满树
若丰满树T中有n个结点,则树T有[ l o g 2 n log_2n log2n]+1层(向下取整)。当n=2t-1时,丰
满树刚好是一棵满树。