二叉排序樹介紹
我們知道二叉樹,每個結點最多有2棵子樹,被稱為左子樹 和 右子樹。
二叉排序樹,顯然也是一顆二叉樹,它有更突出的特性在資料域上呈現排序的特性。
在二叉排序樹中,每個樹的左子樹的資料域均小於它的根結點值,每個樹的右子樹的資料域均大於它的根結點值,每棵樹的左、右子樹 均為二叉排序樹。從它特性很容易得到,對二叉排序樹 進行中序遍曆一定是升序序列。 代碼實現思路
二叉排序樹,相關演算法主要有: 建立二叉排序樹 結點的查詢 結點的插入 結點的刪除
查詢。很好實現吧,遞迴結點查詢key的值是否相等 ,不相等就遞迴查詢唄,遞迴出口 node==NULL,查詢到結點通過傳出參數返回,沒有查詢到返回離key值最近的結點。
插入。根據二叉排序的特性進行插入唄,可以調用查詢介面,如果二叉排序樹沒有該結點,則允許插入,將插入結點掛到返回的結點上唄。
刪除。刪除就要分情況了,第3種情況有點繞,初學者要好好結合代碼理解。 待刪除結點,只有左子樹,可以直接用其左子樹替換刪除結點,因為它的左子樹上的所以結點都比它值小,仍然符合二叉排序的特性 待刪除結點,只有右子樹,同理可以直接使用其右子樹替換刪除結點,因為它的右子樹上 待刪除結點,左右子樹都存在,如何是好呢。因為我們刪除結點之後,仍然要滿足二叉排序樹的特性,每個樹結點的左子樹比都比它小,右子樹都比它大。現在就要想到前面提到的特性了,二叉排排序樹的中序遍曆一定是升序序列,刪除結點的資料域可以使用 中序遍曆的前驅結點 或者後驅結點資料域替換。有木有,注意這裡只替換資料域,因為原來的邏輯關係還要保留,所以不能釋放刪除結點的記憶體,而釋放的是替換的前驅結點或者後驅結點。
建立二叉排序樹。利用插入演算法直接實現唄,剛開始為空白二叉樹,利用插入演算法,可以產生二叉排序樹。 實現代碼
#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>//二叉樹節點typedef struct BiNode { int data; struct BiNode* lchild, *rchild;}BiNode,*BiTree;typedef enum bool{ false,true }bool;//二叉樹 中序遍曆void TraverseMid(BiTree root){ if (root == NULL) { return; } TraverseMid(root->lchild); printf("%d ", root->data); TraverseMid(root->rchild);}/*二叉排序樹的查詢 tree 尋找的二叉樹根結點 parent tree的雙親結點 node 傳出參數,尋找到的結點指標,沒找到為最接近key的結點的指標 key 尋找關鍵字*/bool SearchBST(BiTree root,BiTree parent,BiNode** node,int key ){ if (root == NULL) //已經找到頭了,還沒有找到,返回最接近key的結點的指標 { *node = parent; return false; } else { if (key < root->data) //key 比root 小,搜尋root的左子樹 { return SearchBST(root->lchild, root, node, key); } else if(key > root->data) //key 比root 大,搜尋root的右子樹 { return SearchBST(root->rchild, root, node, key); } else //key == root->data,找到key 返回 { *node = root; return true; } }}//插入bool InsertBST(BiTree root, int data){ BiNode* node = NULL; if (SearchBST(root, NULL, &node, data)) //在樹上有相同的結點,不允許插入 { return false; } else { BiNode * newNode = (BiNode*)malloc(sizeof(BiNode)); newNode->lchild = newNode->rchild = NULL; newNode->data = data; if (data < node->data) { node->lchild = newNode; } else { node->rchild = newNode; } return true; }}//具體刪除結點動作bool DeleteNode(BiTree* node){ BiTree tmp, pre, pre_parent; if ((*node)->lchild == NULL) //刪除結點的左子樹為NULL,將右子樹頂替,釋放刪除結點記憶體 { tmp = *node; *node = (*node)->rchild; free(tmp); } else if ((*node)->rchild == NULL) //刪除結點的右子樹為NULL,將左子樹頂替,釋放刪除結點記憶體 { tmp = *node; *node = (*node)->lchild; free(tmp); } else //刪除結點的左右子樹均不為空白,刪除結點資料換成其前驅或者後繼的資料 結點並不釋放,這裡換成前驅 { pre_parent = *node; pre = pre_parent->lchild; //pre 刪除結點的中序遍曆前驅結點 while (pre->rchild != NULL) //while迴圈 尋找左子樹中最大結點 { pre_parent = pre; pre = pre->rchild; } if (pre == (*node)->lchild) { (*node)->data = pre->data; (*node)->lchild = pre->lchild; } else { (*node)->data = pre->data; pre_parent->rchild = pre->lchild; } free(pre); } return true;}//刪除bool DeleteBST(BiTree *root, int key){ if (root == NULL) return false; if (key == (*root)->data) { return DeleteNode(root); } else if (key < (*root)->data) { return DeleteBST(&(*root)->lchild, key); } else { return DeleteBST(&(*root)->rchild, key); }}//利用二叉樹插入操作,建立二叉排序樹void CreateBST(BiTree *tree){ int num; printf("請輸入根節點資料:"); scanf("%d", &num); *tree = malloc(sizeof(BiNode)); (*tree)->data = num; (*tree)->lchild = (*tree)->rchild = NULL; while (1) { printf("請輸入結點資料(-1結束):"); scanf("%d", &num); if (num == -1) { break; } InsertBST(*tree, num); }}int main(int argc, char *argv[]){ BiTree binarySortTree;//二叉排序樹 CreateBST(&binarySortTree); int menu, key,ret; BiNode* node; while (1) { printf("----菜單----------------\n"); printf("----1.中序遍曆二叉排序樹\n"); printf("----2.尋找結點\n"); printf("----3.插入結點\n"); printf("----4.刪除結點\n"); printf("----5.退出\n"); scanf("%d", &menu); switch (menu) { case 1: TraverseMid(binarySortTree); printf("\n"); break; case 2: printf("請輸入key:"); scanf("%d", &key); ret = SearchBST(binarySortTree, NULL, &node, key); ret == true ? printf("%d存在\n",key) : printf("%d不存在\n", key); break; case 3: printf("請輸入插入data:"); scanf("%d", &key); ret = InsertBST(binarySortTree,key); ret == true ? printf("%d插入成功\n", key) : printf("%d插入失敗\n", key); break; case 4: printf("請輸入刪除data:"); scanf("%d", &key); ret = DeleteBST(&binarySortTree,key); ret == true ? printf("%d刪除成功\n", key) : printf("%d刪除失敗\n", key); break; case 5: exit(0); } } return 0;}
運行檢測