正文
所謂二叉查找樹,實質上是按二叉樹的結構來組織的,這樣的樹可以用鏈表結構來表示,其中每一個節點都是一個對象。
二叉查找樹中元素(也可稱爲關鍵字)的存儲方式總是滿足以下幾個性質:
1.若二叉樹的左子樹不爲空,則左子樹上所有節點的值均不大於其根節點的值;
2.若二叉樹的右子樹不爲空,則右子樹上所有節點的值均不小於其根節點的值;
3.該二叉樹的左子樹和右子樹也是二叉查找樹。
本文將討論二叉查找樹的創建、查詢、插入、刪除等操作。
第一節 二叉查找樹的創建
創建一顆二叉查找樹相對於創建二叉樹來說是比較麻煩的,因爲在創建二叉查找樹時,必須遵循其性質。但是就是在這樣的前提下,我們仍然會遇到麻煩。我們先來看一對二叉查找樹,如下圖:
如圖中所示,這兩顆二叉查找樹都有6個節點,且包含同樣的關鍵字,唯一不同的是這兩顆查找樹的高度不同。a圖的高度是2,b圖的高度是4。怎麼會出現這種情況呢?這就涉及到根節點關鍵字的選擇了。其實根節點關鍵字的選擇比較隨意,不同的根節點就可能使創建的二叉查找樹的高度不同。
二叉查找樹的類型描述如下:
二叉查找樹的查詢操作:查找某一個關鍵字、查找最小和最大元素、查找前驅和後繼。
1.查找某一個關鍵字
法則:該過程從樹的根節點開始查找,並沿着樹下降。設要找的那個數爲k,對碰到的每個節點x,就比較k和key[x]。如果相同,則查找結束。如果k小於key[x],則繼續查找x的左子樹(由二叉查找樹的性質知,k不可能在x的右子樹中)。同理,如果k大於key[x],則在x的右子樹中繼續查找。例如下圖:
二叉查找樹的類型描述如下:
typedef int ElemType; typedef struct BSTNode{ ElemType key; //關鍵字 struct BSTNode *left; //左兒子 struct BSTNode *right; //右兒子 struct BSTNode *p; //父節點 }BSTNode, *BSTree;創建二叉查找樹的節點如下代碼:
BSTree CreateNode(ElemType keynum) { BSTree tree = NULL; tree = (BSTree)malloc(sizeof(BSTNode)); tree->key = keynum; tree->left = NULL; tree->p = NULL; tree->right = NULL; return tree; }第二節 二叉查找樹的查詢
二叉查找樹的查詢操作:查找某一個關鍵字、查找最小和最大元素、查找前驅和後繼。
1.查找某一個關鍵字
法則:該過程從樹的根節點開始查找,並沿着樹下降。設要找的那個數爲k,對碰到的每個節點x,就比較k和key[x]。如果相同,則查找結束。如果k小於key[x],則繼續查找x的左子樹(由二叉查找樹的性質知,k不可能在x的右子樹中)。同理,如果k大於key[x],則在x的右子樹中繼續查找。例如下圖:
好,我們來看看查找某個關鍵字的算法:
①.查找最小元素:從根節點開始,沿着各節點的left指針查找下去,直至遇到NULL時爲止。如下圖:
BSTree SearchNode(BSTree tree, ElemType k) { while(tree != NULL && k != tree->key) { if(k < tree->key) //在左子樹中找 tree = tree->left; else tree = tree->right; //在右子樹中找 } return tree; }2.查找最小和最大元素
①.查找最小元素:從根節點開始,沿着各節點的left指針查找下去,直至遇到NULL時爲止。如下圖:
繼續,我們來寫一下算法:
BSTree MinNode(BSTree tree) { while(tree->left != NULL) tree = tree->left; return tree; }②.查找最大元素:從根節點開始,沿着各節點的right指針查找下去,直至遇到NULL時爲止。如下圖:
查找最大元素的算法:
①.查找後繼元素
定義:如果x有右子樹,則x的後繼是右子樹的最小關鍵字;否則查看x的祖先節點,直到某個結點y滿足此性質:x出現在y的左子樹中,如果y存在,則y是x的後繼,否則x沒有後繼。如下圖所示:
BSTree MaxNode(BSTree tree) { while(tree->right != NULL) tree = tree->right; return tree; }3.查找前驅和後繼
①.查找後繼元素
定義:如果x有右子樹,則x的後繼是右子樹的最小關鍵字;否則查看x的祖先節點,直到某個結點y滿足此性質:x出現在y的左子樹中,如果y存在,則y是x的後繼,否則x沒有後繼。如下圖所示:
查找後繼元素的算法:
定義:如果x有左子樹,那麼前驅是左子樹中的最大關鍵字;否則查看x的祖先節點,直到某個節點y滿足此性質:x出現在y的右子樹中。如果y存在,則y就是x的前驅,否則x沒有前驅。如下圖所示:
BSTree TreeSuccessor(BSTree tree) { BSTree y; if(tree->right != NULL) //該節點的右子樹不爲空,找到右子樹的最小關鍵字 return TreeMinimum(tree->right); y = tree->p; while(y != NULL && tree == y->right) { tree = y; y = y->p; } return y; }②.查找前驅元素
定義:如果x有左子樹,那麼前驅是左子樹中的最大關鍵字;否則查看x的祖先節點,直到某個節點y滿足此性質:x出現在y的右子樹中。如果y存在,則y就是x的前驅,否則x沒有前驅。如下圖所示:
查找前驅元素的算法:
1.插入:當講一個新值x插入到二叉查找樹中的適當位置時,設當前插入的位置爲z,需滿足:z->key = x, z->left = NULL, z->right = NULL。如下圖所示:
插入算法如下:
①.沒有子女節點:將父節點對應子女節點的指針置爲NULL。如下圖:
BSTree TreePrecursor(BSTree tree) { BSTree y; if(tree->left != NULL) return TreeMaximum(tree->left); y = tree->p; while(y != NULL && tree == y->left) { tree = y; y = y->p; } return y; }第三節 二叉查找樹的插入和刪除
1.插入:當講一個新值x插入到二叉查找樹中的適當位置時,設當前插入的位置爲z,需滿足:z->key = x, z->left = NULL, z->right = NULL。如下圖所示:
int InsertNode(BSTree *tree, BSTree z) { BSTree x, y; y = NULL; x = *tree; while(x != NULL) { y = x; if(z->key < x->key) x = x->left; else x = x->right; } z->p = y; if(y == NULL) *tree = z; else if(z->key < y->key) y->left = z; else y->right = z; return 0; }2.刪除:從二叉查找樹中刪除某一個節點元素可根據其是否有子女節點來操作:
①.沒有子女節點:將父節點對應子女節點的指針置爲NULL。如下圖:
②.有一個子女節點:將其父節點的指針指向其子女節點。如下圖:
③.有兩個子女節點:
法一:將當前結點與其左子樹中最大的元素交換,然後刪除當前結點。
法二:將當前結點與其右子樹中最小的元素交換,然後刪除當前結點。
法一:將當前結點與其左子樹中最大的元素交換,然後刪除當前結點。
法二:將當前結點與其右子樹中最小的元素交換,然後刪除當前結點。
最後我們來看看刪除操作的算法:
第四節 結束語
想想、寫寫、畫畫......
int TreeDelete(BSTree *tree, BSTree z) { BSTree x, y; if(z->left == NULL || z->right == NULL) //有一個或者沒有子女節點 y = z; else y = TreeSuccessor(&(**tree)); //有兩個子女節點 if(y->left != NULL) x = y->left; else x = y->right; if(x != NULL) x ->p = y->p; if(y->p == NULL) (*tree) = x; else if(y == y->p->left) y->p->left = x; else y->p->right = x; if(y != z) z->key = y->key; free(y); y = NULL; return 0; }二叉查找樹的操作就說完了。
第四節 結束語
想想、寫寫、畫畫......