二叉搜尋樹的初始化、插入、刪除、查詢、銷燬等操作
阿新 • • 發佈:2018-12-10
二叉搜尋樹的概念
二叉搜尋樹又稱二叉排序樹,它或者是一顆空樹,或者是具有以下性質的二叉樹:
- 若它的左子樹不為空,則左子樹上所有結點的值都小於根結點的值
- 若它的右子樹不為空,則右子樹上所有結點的值都大於根結點的值
- 它的左右子樹也分別為二叉搜尋樹 例: 我們要實現這些操作。先定義一個二叉搜尋樹的結構體,這個結構體成員包括左右孩子兩個指標,還有它的資料:
typedef int BSDataType;
typedef struct BSTreeNode
{
struct BSTreeNode* _pLeft;
struct BSTreeNode* _pRight;
BSDataType _data;
}BSTNode;
二叉搜尋樹的初始化: 需要傳入二級指標,因為要改變根結點的指向,讓根結點指向空
void InitBSTree(BSTNode** pRoot)
{
assert(pRoot);
*pRoot = NULL;
}
二叉搜尋樹的插入: 分析:
- 樹為空,則直接插入(這也是傳二級指標的原因,因為要改變根結點的指向) 2.樹不空,按二叉搜尋樹性質查詢插入位置,插入新結點
非遞迴程式碼:
bool InsertBSTree(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (*pRoot == NULL)
{
(*pRoot) = BuyBSTNode(data);
return true;
}
else
{
BSTNode* pCur = (*pRoot);
BSTNode *pParent = NULL;
//找到插入結點的位置
while (pCur)
{
pParent = pCur;
if (pCur->_data == data)//如果當前結點=要插入的結點,則退出,因為這個元素已經存在
{
return false;
}
else if (pCur->_data > data)//如果當前結點數>要插入的數,向當前結點的左子樹去找插入位置
{
pCur = pCur->_pLeft;
}
else//如果當前結點數<要插入的數,向當前結點的右子樹去找插入位置
{
pCur = pCur->_pRight;
}
}
//插入新結點
if (data > pParent->_data)//如果插入的元素比它要插入位置的元素大,則要插入的元素應該是要插入位置結點的右孩子
{
pParent->_pRight = BuyBSTNode(data);
return true;
}
if (data < pParent->_data)//如果插入的元素比它要插入位置的元素小,則要插入的元素應該是要插入位置結點的左孩子
{
pParent->_pLeft = BuyBSTNode(data);
return true;
}
}
return false;
}
遞迴程式碼:
bool InsertBSTreeNor(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
*pRoot = BuyBSTNode(data);
else
{
if (data == (*pRoot)->_data)
{
return false;
}
else if (data < (*pRoot)->_data)
return InsertBSTree(&(*pRoot)->_pLeft, data);
else
return InsertBSTree(&(*pRoot)->_pRight, data);
}
return true;
}
二叉搜尋樹的刪除: 分析: 刪除的話就有點複雜了,因為刪除結點的位置不同,我們處理的方式不同。 首先查詢元素是否在二叉搜尋樹中,如果不存在,則返回,否則要刪除的結點可能分下面四種情況: 1.要刪除的結點無孩子結點 2.要刪除的結點只有左孩子結點 3.要刪除的結點只有右孩子 4.要刪除的結點有左、有結點 非遞迴程式碼:
bool DelectBSTree(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
BSTNode* pCur = NULL;
BSTNode* pParent = NULL;
if (NULL == *pRoot)
return false;
else
{
//找到待刪除結點
pCur = *pRoot;
while (pCur)
{
if (data > pCur->_data)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
break;
}
//刪除結點
//待刪結點是葉子節點或者只有右孩子
if (NULL == pCur->_pLeft)
{
if (pCur == *pRoot)//若果刪除的是根結點
*pRoot = pCur->_pRight;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
//只有左孩子
else if (NULL == pCur->_pRight)
{
if (pCur == *pRoot)//若果刪除的是根結點
*pRoot = pCur->_pLeft;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
//左右孩子都存在
else
{
//替代法,向待刪除點的右子樹找最左邊的數(最小)/向待刪除點的左子樹找最右邊的數(最大),將待刪除點與最左邊/最右邊的數交換,刪除最左邊/最右邊的數
BSTNode* Inorder = pCur->_pRight;
while (Inorder->_pLeft)//除了迴圈函式,Inorder不可能有左孩子
{
pParent = Inorder;
Inorder = Inorder->_pLeft;
}
pCur->_data = Inorder->_data;//交換
if (Inorder == pParent->_pLeft)
pParent->_pLeft = Inorder->_pRight;
else if (Inorder == pParent->_pRight)
pParent->_pRight = Inorder->_pRight;
pCur = Inorder;
}
}
free(pCur);
pCur = NULL;
return true;
}
遞迴程式碼:
bool DelectBSTreeNor(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
return false;
else
{
if (data > (*pRoot)->_data)
{
return DelectBSTreeNor(&(*pRoot)->_pRight, data);
}
else if (data < (*pRoot)->_data)
{
return DelectBSTreeNor(&(*pRoot)->_pLeft, data);
}
else //data == (*pRoot)->_data
{
//這時已經找到了待刪除結點
BSTNode* pDel = *pRoot;
if (NULL == pDel->_pLeft)
{
*pRoot = pDel->_pRight;
free(pDel);
return true;
}
else if (NULL == (*pRoot)->_pRight)
{
*pRoot = pDel->_pLeft;
free(pDel);
return true;
}
else //左右孩子都存在
{
BSTNode* Inorder = pDel->_pRight;
while (Inorder->_pLeft)
Inorder = Inorder->_pLeft;
pDel->_data = Inorder->_data;
return DelectBSTreeNor(&(*pRoot)->_pRight, pDel->_data);
}
}
}
}
二叉搜尋樹的查詢: 我們根據二叉搜尋樹的性質可以得出,如果要找的資料比當前根結點的資料小,我們應該往當前根結點的左子樹去找,如果比當前根結點的資料大,往當前根結點的右子樹去找。
非遞迴程式碼:
BSTNode* FindBSTree(BSTNode* pRoot, BSDataType data)
{
BSTNode* pCur = NULL;
if (NULL == pRoot)
return NULL;
pCur = pRoot;
while (pCur)
{
if (pCur->_data == data)
{
return pCur;
}
else if (pCur->_data > data)
{
pCur = pCur->_pLeft;
}
else
{
pCur = pCur->_pRight;
}
}
return NULL;
}
遞迴程式碼:
BSTNode* FindBSTreeNor(BSTNode* pRoot, BSDataType data)
{
if (NULL == pRoot)
return NULL;
else if (data == pRoot->_data)
return pRoot;
else if (data < pRoot->_data)
return FindBSTreeNor(pRoot->_pLeft, data);
else
return FindBSTreeNor(pRoot->_pRight, data);
}
下面是完整的程式碼: BSTree.h
#ifndef __BSTREE_H__
#define __BSTREE_H__
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
#define bool int
#define true 1
#define false 0
typedef int BSDataType;
typedef struct BSTreeNode
{
struct BSTreeNode* _pLeft;
struct BSTreeNode* _pRight;
BSDataType _data;
}BSTNode;
void InitBSTree(BSTNode** pRoot);
bool InsertBSTree(BSTNode** pRoot, BSDataType data);
bool InsertBSTreeNor(BSTNode** pRoot, BSDataType data);
bool DelectBSTree(BSTNode** pRoot, BSDataType data);
bool DelectBSTreeNor(BSTNode** pRoot, BSDataType data);
BSTNode* FindBSTree(BSTNode* pRoot, BSDataType data);
BSTNode* FindBSTreeNor(BSTNode* pRoot, BSDataType data);
void DestroyBSTree(BSTNode** pRoot);
#endif //__BSTREE_H__
BSTree.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BSTree.h"
void InitBSTree(BSTNode** pRoot)
{
assert(pRoot);
*pRoot = NULL;
}
BSTNode* BuyBSTNode(BSDataType data)
{
BSTNode* newNode = (BSTNode*)malloc(sizeof(BSTNode));
if (NULL == newNode)
{
printf("建立節點失敗\n");
return NULL;
}
newNode->_data = data;
newNode->_pLeft = NULL;
newNode->_pRight = NULL;
return newNode;
}
void InOrder(BSTNode* pRoot)
{
if (NULL == pRoot)
return;
InOrder(pRoot->_pLeft);
printf("%d ", pRoot->_data);
InOrder(pRoot->_pRight);
}
bool InsertBSTree(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (*pRoot == NULL)
{
(*pRoot) = BuyBSTNode(data);
return true;
}
else
{
BSTNode* pCur = (*pRoot);
BSTNode *pParent = NULL;
//找到插入結點的位置
while (pCur)
{
pParent = pCur;
if (pCur->_data == data)//如果當前結點=要插入的結點,則退出,因為這個元素已經存在
{
return false;
}
else if (pCur->_data > data)//如果當前結點數>要插入的數,向當前結點的左子樹去找插入位置
{
pCur = pCur->_pLeft;
}
else//如果當前結點數<要插入的數,向當前結點的右子樹去找插入位置
{
pCur = pCur->_pRight;
}
}
//插入新結點
if (data > pParent->_data)//如果插入的元素比它要插入位置的元素大,則要插入的元素應該是要插入位置結點的右孩子
{
pParent->_pRight = BuyBSTNode(data);
return true;
}
if (data < pParent->_data)//如果插入的元素比它要插入位置的元素小,則要插入的元素應該是要插入位置結點的左孩子
{
pParent->_pLeft = BuyBSTNode(data);
return true;
}
}
return false;
}
bool DelectBSTree(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
BSTNode* pCur = NULL;
BSTNode* pParent = NULL;
if (NULL == *pRoot)
return false;
else
{
//找到待刪除結點
pCur = *pRoot;
while (pCur)
{
if (data > pCur->_data)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
break;
}
//刪除結點
//待刪結點是葉子節點或者只有右孩子
if (NULL == pCur->_pLeft)
{
if (pCur == *pRoot)//若果刪除的是根結點
*pRoot = pCur->_pRight;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
//只有左孩子
else if (NULL == pCur->_pRight)
{
if (pCur == *pRoot)//若果刪除的是根結點
*pRoot = pCur->_pLeft;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
//左右孩子都存在
else
{
//替代法,向待刪除點的右子樹找最左邊的數(最小)/向待刪除點的左子樹找最右邊的數(最大),將待刪除點與最左邊/最右邊的數交換,刪除最左邊/最右邊的數
BSTNode* Inorder = pCur->_pRight;
while (Inorder->_pLeft)//除了迴圈函式,Inorder不可能有左孩子
{
pParent = Inorder;
Inorder = Inorder->_pLeft;
}
pCur->_data = Inorder->_data;//交換
if (Inorder == pParent->_pLeft)
pParent->_pLeft = Inorder->_pRight;
else if (Inorder == pParent->_pRight)
pParent->_pRight = Inorder->_pRight;
pCur = Inorder;
}
}
free(pCur);
pCur = NULL;
return true;
}
BSTNode* FindBSTree(BSTNode* pRoot, BSDataType data)
{
BSTNode* pCur = NULL;
if (NULL == pRoot)
return NULL;
pCur = pRoot;
while (pCur)
{
if (pCur->_data == data)
{
return pCur;
}
else if (pCur->_data > data)
{
pCur = pCur->_pLeft;
}
else
{
pCur = pCur->_pRight;
}
}
return NULL;
}
void DestroyBSTree(BSTNode** pRoot)
{
assert(pRoot);
if (*pRoot)
{
DestroyBSTree(&(*pRoot)->_pLeft);
DestroyBSTree(&(*pRoot)->_pRight);
free(*pRoot);
*pRoot = NULL;
}
}
BSTNode* FindBSTreeNor(BSTNode* pRoot, BSDataType data)
{
if (NULL == pRoot)
return NULL;
else if (data == pRoot->_data)
return pRoot;
else if (data < pRoot->_data)
return FindBSTreeNor(pRoot->_pLeft, data);
else
return FindBSTreeNor(pRoot->_pRight, data);
}
bool InsertBSTreeNor(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
*pRoot = BuyBSTNode(data);
else
{
if (data == (*pRoot)->_data)
{
return false;
}
else if (data < (*pRoot)->_data)
return InsertBSTree(&(*pRoot)->_pLeft, data);
else
return InsertBSTree(&(*pRoot)->_pRight, data);
}
return true;
}
bool DelectBSTreeNor(BSTNode** pRoot, BSDataType data)
{
assert(pRoot);
if (NULL == *pRoot)
return false;
else
{
if (data > (*pRoot)->_data)
{
return DelectBSTreeNor(&(*pRoot)->_pRight, data);
}
else if (data < (*pRoot)->_data)
{
return DelectBSTreeNor(&(*pRoot)->_pLeft, data);
}
else //data == (*pRoot)->_data
{
//這時已經找到了待刪除結點
BSTNode* pDel = *pRoot;
if (NULL == pDel->_pLeft)
{
*pRoot = pDel->_pRight;
free(pDel);
return true;
}
else if (NULL == (*pRoot)->_pRight)
{
*pRoot = pDel->_pLeft;
free(pDel);
return true;
}
else //左右孩子都存在
{
BSTNode* Inorder = pDel->_pRight;
while (Inorder->_pLeft)
Inorder = Inorder->_pLeft;
pDel->_data = Inorder->_data;
return DelectBSTreeNor(&(*pRoot)->_pRight, pDel->_data);
}
}
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"BSTree.h"
int main()
{
BSTNode* pRoot;
InitBSTree(&pRoot);
int i = 0;
for (i = 0; i < 5; i++)
{
InsertBSTreeNor(&pRoot, i);
}
//DestroyBSTree(&pRoot);
InOrder(pRoot);
BSTNode* ret = FindBSTreeNor(pRoot, 4);
printf("\n");
DelectBSTreeNor(&pRoot, 3);
InOrder(pRoot);
printf("\n");
if (ret)
{
printf("該元素在二叉樹中\n");
}
else
{
printf("該元素不在二叉樹中\n");
}
return 0;
}