/*一个关键字元素不重复的二叉查找树及其典型操作的实现,记录关键字出现次数*/ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <assert.h> #define N 20 #define N_RAND 200 typedef struct node{ int key; //关键字 int num; //出现次数 struct node *l; //左结点 struct node *r; //右结点 struct node *p; //父结点 }Node,*Pnode; //以关键字初始化结点 Pnode IniNode(int key){ Pnode p=(Pnode)malloc(sizeof(Node)); p->key=key; p->num=1; p->l=NULL; p->r=NULL; p->p=NULL; } //中序遍历输出二叉树,由小变大 void BSTreeWalk(Pnode h){ if(h!=NULL){ BSTreeWalk(h->l); printf("key=%d,num=%d\n",h->key,h->num); BSTreeWalk(h->r); } } //查找含有关键字key的结点 Pnode Search(Pnode h, int key){ /* //递归版本 if (h==NULL || key == h->key) return h; if(key < h->key) return Search(h>l, int key); else return Search(h>r, int key); */ //非递归版本 while(h!=NULL && key!=h->key){ if(key < h->key) h=h->l; else h=h->r; } return h; } //返回最大结点指针 Pnode MaxElem(Pnode h){ //一直向左查找左子树 Pnode p; for(p=h;p->r!=NULL;p=p->r); return p; } //返回最小结点指针 Pnode MinElem(Pnode h){ //一直向右查找右子树 Pnode p; for(p=h;p->l!=NULL;p=p->l); return p; } //插入一个结点,如果有key值相等,出现数量加1 void Insert(Pnode h, Pnode p){ Pnode q=h; Pnode pre=q; while(q!=NULL && q->key != p->key){ if(p->key < q->key){ pre=q; q=q->l; }else if(p->key > q->key){ pre=q; q=q->r; } } if(q!=NULL) q->num ++; //结点已经存在 ,出现数量加1 else{ p->p=pre; if(pre->key < p->key) pre->r=p; else pre->l=p; } } //查找结点p的后前驱结点 Pnode Predecessor(Pnode h, Pnode p){ assert(h!=NULL || p!=NULL); //左子树不为空,则为左子树的最大元素 if(p->l != NULL) return MaxElem(p->l); //右子树为空的情况,则该结点p的后继y为p的最低祖先结点,且y的右儿子也是x的祖先。 Pnode y=p->p; while(y!=NULL && y->l==p){ //如果是右子树,还要向上查找,支到遇到左子树。 p=y; y=p->p; } return y; } //查找结点p的后继结点 Pnode Successor(Pnode h, Pnode p){ assert(h!=NULL || p!=NULL); //右子树不为空,则为右子树的最小元素 if(p->r != NULL) return MinElem(p->r); //右子树为空的情况,则该结点p的后继y为p的最低祖先结点,且y的左儿子也是x的祖先。 Pnode y=p->p; while(y!=NULL && y->r==p){ //如果是右子树,还要向上查找,支到遇到左子树。 p=y; y=p->p; } return y; } /*删除p结点 ,可能会有删除根节点情况,跟节点地址改变,所以需要*h,取根节点指针地址 * delete可能不太好理解,基于的思想为先找到实际要删除的结点是哪个,可能是该结点本身也可能是后继,然后根据不同的情况改变连接该结点的 * 指针指向,如果删除的是结点后继,则将结点后继信息替换过来,相当于用结点后继替换了该结点。 */ Pnode Delete(Pnode *h, Pnode p){ assert(*h!=NULL || p!=NULL); Pnode x=NULL,y=NULL; //分三种情况,即删除结点没有子女,只有一个子女,有两个子女的情况 if(p->l==NULL || p->r==NULL) y=p; //p有一个子女或者没有子女,则直接删除p else y=Successor(*h,p); //p有两个子女,则删除其后继结点,然后将后继结点信息拷贝到p的位置 //x被置为y的非NULL子女,或者当y无子女时,x为空 if(y->l != NULL) x=y->l; else x=y->r; //通过修改y->p和x的指针将y删除。 if(x!=NULL) x->p=y->p; if(y->p==NULL) *h=x; //此为删除的为根节点情况 else if(y == y->p->l) y->p->l=x; else y->p->r=x; //交换信息 if(y!=p){ p->key=y->key; p->num=y->num; } return y; } //根据输入数据构建一个二叉查找树 int BuildBSTree(Pnode *h, int *a, int len){ int i; *h=IniNode(a[0]); for(i=1;i<len;i++){ Pnode p=IniNode(a[i]); Insert(*h,p); } return 1; } int main(){ int i, a[N]; Pnode head = NULL; srand((unsigned)time(NULL)); for(i=0;i<N;i++){ a[i]=rand()%N_RAND; printf("%d ",a[i]); } printf("\n"); if(BuildBSTree(&head, a, N) == 1) printf("构建二叉查找树成功!\n"); else{ printf("构建二叉查找树失败!\n"); return 0; } BSTreeWalk(head); printf("最小元素是:%d\n",MinElem(head)->key); printf("最大元素是:%d\n",MaxElem(head)->key); for(i=0;i<N;i++){ Pnode find=Search(head,a[i]); if(find == NULL) printf("未找到该节点!\n"); else{ Pnode pre= Predecessor(head,find); // if(pre!=NULL) printf("前驱节点为:key=%d,num=%d\n",pre->key,pre->num); printf("删除结点为:key=%d,num=%d\n",find->key,find->num); if(Delete(&head,find) == NULL) printf("删除失败\n"); if(head == NULL){ printf("二叉查找树已空!\n"); }else{ BSTreeWalk(head); } } } system("pause"); }
相关链接:前两个原理,后面是另外的一个C实现。
http://baike.baidu.com/view/389459.htm
http://www.cnblogs.com/xiaosuo/archive/2010/02/10/1656183.html
http://blog.sina.com.cn/s/blog_76871c5e0100s5xj.html