16.二叉排序樹,16二叉排序
轉載請表名出處:http://blog.csdn.net/u012637501一、二叉排序樹 如果要尋找的資料集是有序線性表且是順序儲存的,尋找可以用折半、插值、斐波那契等尋找演算法來實現。然後,由於有序,當我們在插入和刪除操作上,就需要耗費大量的時間。下面將要學習的二叉排序樹,就是一種既可以使得插入和刪除效率不錯,又可以比較高效率地實現尋找的演算法。為此,構造一棵二叉排序樹的目的並不是為了排序,而是為了提供尋找和插入刪除關鍵字的速度。1.二叉排序樹概念 二叉排序樹(Binary Sort Tree),又稱為二叉尋找樹,或者是一棵空樹,或者是具有下列性質的二叉樹。 ◆若它的左子樹不空,則左子樹上所有結點的值均小於它的根結構(雙親結點)的值; ◆若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點(雙親結點)的值; ◆它的左、右子樹也分別為二叉排序樹。2.二叉樹結點結構
/*二叉樹的二叉鏈表結點結構定義*/typedef struct BiTNode //結點結構{ int data; //結點資料 Struct BiTNode *lchild,*rchild; //左右孩子指標}BiTNode,*BiTree;
二、二叉排序樹操作演算法
1.二叉排序樹的查詢操作
/*遞迴尋找二叉排序樹T中是否存在key, * 指標f指向T的雙親,其初始調用值為NULL *若尋找成功,則指標p指向該資料元素結點,並返回TRUE;否則指標p指向尋找路徑上訪問的最後一個結點並返回FALSE*/Status SearchBST(BiTree T,int key,BiTree f,BiTree *p){ if(!T) //尋找不成功(為空白樹) { *p=f; return FALSE; } else if(key==T->data) //尋找成功 { *p=T; return TRUE; } else if(key<T->data) return SearchBST(T->lchild,key,T,p); //在左子樹繼續尋找 else return SearchBST(T->rchild,key,T,p); //在右子樹繼續尋找 }
執行個體: 假如有一資料集合={62,88,58,47,35,73,51,99,37,93},尋找的關鍵字key=93。使用二叉排序樹尋找演算法步驟如下:①根據二叉排序樹定義將該資料集合構造成一棵二叉排序樹(中序遍曆);
②調用二叉排序樹查詢演算法SearchBST(T,93,NULL,P)查詢關鍵字,其中,SearchBST函數是一個可遞迴啟動並執行函數,參數T是一個二叉樹鏈表、key代表要查詢的關鍵字、二叉樹f指向T的雙親。當T指向根結點時,f的初值就為NULL,它在遞迴時有用,最後的參數p是為了尋找成功後可以得到尋找到的結點位置。③ if(!T){ .... }語句。用來判斷當前二叉樹是否到葉子結點,此時當前T指向根結點62的位置,由於T不為空白,故該語句片段不執行。④esle if(key==T->data)語句。即尋找到相匹配的關鍵字執行語句,顯然93!=62,故該語句片段不執行。⑤else if(key<T->data)語句。即當要尋找關鍵字小於當前結點時執行語句,由於93>62,故該語句片段不執行。⑥else{....}語句。即當即當要尋找關鍵字大於當前結點時執行語句,93>62,所以以遞迴調用SearchBST(T->rchild,key,T,p)。此時,T指向了62的右孩子88,f指向88的雙親結點,即62。
⑦此時第二層SearchBST,因93比88大,所以執行else{....}語句,再次遞迴調用SearchBST(T->rchild,key,T,p)。此時T指向了88的右孩子99。
⑧此時第三層SearchBST,因93比99小,所以執行else if(key<T->data)語句,再次遞迴調用SearchBST(T->lchild,key,T,p)。此時T指向了99的左孩子93。
⑨第四層SearchBST,因key等於T->data,所以執行第10~11行,此時指標p指向93所在的結點並返回True到第三層、第二層、第一層,最終返回函數True。
2.二叉排序樹的插入操作 所謂二叉排序樹的插入,即將關鍵字放到樹中的合適位置。
(1)二叉排序樹的插入演算法
/*當二叉排序樹T中不存在關鍵字等於key的資料元素時, * 插入key並返回TRUE,否則返回FALSE*/Status InsertBST(BiTree *T,int key){ BiTree p,s; /*調用尋找函數尋找是否存在該關鍵字*/ //a.若尋找不成功 if(!SearchBST(*T,key,NULL,&p)) { s=(BiTree)malloc(sizeof(BiTNode)); //為結點s開闢一段記憶體空間 s->data=key; //將關鍵字存放到s指向結點的資料域中 s->lchid=s->rchild=NULL; //初始化結點s的左右指標域 if(!p) *T=s; //插入s為新的根結點 else if(key<p->data)//若關鍵字小於p結點資料值,插入s為結點p的左孩子 p->lchild = s; else //若關鍵字大於p結點資料值,插入s為結點p的右孩子 p->rchild=s; } /*樹中已有關鍵字相同的結點,不再插入*/ else { return FALSE; }}
舉例:假如我們調用函數是"InsertBST(T,93);",那麼結果就是FALSE;假如調用函數為"InsertBST(T,95);",那麼一定是就是在93的結點增加一個右孩子95,並返回TRUE。需要注意的是,由於插入演算法事先調用了SearchBST(*T,key,NULL,&p)尋找演算法且使用中序遍曆二叉樹,最終我們可知指標p指向的結點為93.
3.構建二叉排序樹演算法
/*假如有一個資料集={62,88,58,47,35,73,51,99,37,93} * 構建一個二叉排序樹*/int i;int a[0]={62,88,58,47,35,73,51,99,37,93};BiTree T=NULL;for(i=0;i<10;i++){ InsertBST(&T,a[i]);}
4.二叉排序樹刪除操作演算法
(1)採用遞迴方式對二叉排序樹T尋找key,找到後調用Delete函數刪除該結點/*若二叉排序樹T中存在關鍵字等於key的資料元素時,則刪除該資料元素結點 * 並返回TRUE;否則返回FALSE*/
Status DeleteBST(BiTree *T,int key){ if(!*T) //不存在關鍵字等於key的資料元素 return FALSE; else { if(key==(*T)->data) //找到關鍵字等於key的資料元素 return Delete(T); //調用Delete函數刪除該結點 else if(key<(*T)->data) return DeleteBST(&(*T)->lchild,key); else return DeleteBST(&(*T)->rchild,key); } }
(2)Delete刪除演算法
/*從二叉排序樹中刪除結點p,並重接它的左或右子樹*/Status Delete(BiTree *p){ BiTree q,s; /*情況二:刪除結點p的右子樹或左子樹為空白*/ if((*p)->lchild==NULL) //a.右子樹空則只需重接它的左子樹 { q=*p; *p=(*p)->lchild; free(q); } else if((*p)->rchild==NULL) //b.只需重接它的右子樹 { q=*p; *p=(*p)->rchild; free(q); //將指標p指向的結點 } //情況三:左右子樹均不為空白 else { q=*p; s=(*p)->lchild; while(s->rchild) //轉左,然後向右到盡頭(找待刪結點的前驅) { q=s; s=s->rchild; } (*p)->data=s->data; //s指向被刪結點的直接前驅 if(q!=*p) q->rchild=s->lchild; //重接q的右子樹 else q->lchild=s->lchild; //重接q的左子樹 free(s); } return TRUE;}
源碼分析:q=*p; *p=(*p)->rchild; free(q); 作用:將指標p指向的結點賦值給新結點q,並使p指標指向的左結點,即實現了重接右子樹,再釋放結點q.
三、二叉排序樹總結 二叉排序樹是以連結的方式儲存,保持了連結儲存結構在執行插入或刪除操作時不用移動元素的優點,只要找到合適的插入和刪除位元置後,僅需修改連結指標即可。插入刪除的時間效能比較好,而對於二叉排序樹的尋找,走的就是從根結點到要尋找的結點的路徑,其比較次數等於給定值的結點在二叉排序樹的層數。極端情況,最少為1次,即跟結點就是要找的結點,最多也不會超過樹的深度,即二叉排序樹的尋找效能取決於二叉排序樹的形狀。