二叉排序樹(Binary Sort Tree,二叉查找樹,二叉搜索樹)--【算法導論】


今天的收獲就是二叉搜索樹,“好記性不如爛筆頭”,寫下來加深一下印象;

1、首先是了解了二叉搜索樹(Binary Sort Tree)又稱二叉查找樹,亦稱二叉排序樹 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別為二叉搜索樹。

2、接下來看的是查詢二叉搜索樹,查詢二叉搜索樹的內容比較多;

(1)查找:即是查詢關鍵字,若存在,返回該節點的指針;否則,返回空;這個書中給了兩段偽碼,迭代、遞歸隨意;

(2)最大關鍵字及最小關鍵字元素:顧名思義,肯定是二叉搜索樹的最大最小值,以最大關鍵字為例,一直查詢樹的右孩子,直到改節點無右孩子為止,該節點就是最大關鍵字,當然,最小關鍵字同理;

(3)后繼與前驅:對一個節點來說,最大的小於該節點值的即是前驅,最小的大於該節點值的即是后繼。以后繼為例,如果該節點的右子樹不為空,那么后繼就是右子樹中最小關鍵字元素;若是該節點右孩子不存在,這時,只需由該節點往上尋找,直到這個節點是其父節點的左孩子即可。當然,前驅也是類似情況;

3、然后看的是插入與刪除,這一節也挺關鍵的;

(1)插入:插入我們都知道,建立的二叉搜索樹就是一個節點一個節點的進行插入的。遍歷該樹,找到適合的位置,就將節點插入了;

(2)刪除:這個就比較復雜了,考慮的情況比較多;

一:該節點是葉子節點,這個刪除是最簡單的,將其改為空,並修改父節點即可;

二:該節點只有一個孩子(左孩子或右孩子,這個在具體實現時不同操作),這時只要將該節點的孩子節點放在該節點上,同時修改該節點的父節點即可;

三:該節點有兩個孩子,這個是最復雜的,不過由於后來沒思路,編代碼的時候參考一位仁兄,他的思想是直接找到該節點的后繼節點,將其刪除(這個后繼節點一定是存在的,因為它有兩個孩子啊,至少右孩子就比它大,故而存在),然后將該節點的值改為后繼節點的值即可(當然提前把后繼的值留下...)

4、最后看的是隨機構建二叉搜索樹,貌似這樣二叉樹的高度比較理想,平均深度是lgn,不會發生“過激”的情況。

如上時間復雜度皆為O(h),這一章是《算法導論》的內容,書中寫得還是比較詳細的,偽碼也給了不少,建的二叉搜索樹如下;


    ElemType nodeArray[11] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};  //二叉數
Create(root, nodeArray, 11); //創建二叉搜索數

cout<<"該節點的值"<<Search(root,15)->key<<endl;
cout<<"min = "<<SearchMin(root)->key<<endl; //最小值
cout<<"max = "<<SearchMax(root)->key<<endl; //最大值
cout<<"前驅"<<SearchPredecessor(root->right->right)->key<<endl; //前驅
cout<<"后繼"<<SearchSuccessor(root->left->right->right)->key<<endl; //后繼
DeleteNode(root, 6);
cout<<"該節點的值"<<Search(root,15)->key<<endl;
cout<<"min = "<<SearchMin(root)->key<<endl; //最小值
cout<<"max = "<<SearchMax(root)->key<<endl; //最大值
cout<<"前驅"<<SearchPredecessor(root->right->right)->key<<endl; //前驅
cout<<"后繼"<<SearchSuccessor(root->left->right->left)->key<<endl; //后繼
進行如上操作,創建二叉搜索樹就不多說了,另外如查找關鍵字返回該指針也是直接使用,並未判斷,在實際程序中需要寫出,如上只是簡單測試一下;

首先是cout<<"該節點的值"<<Search(root,15)->key<<endl;輸入關鍵字15這個節點的值,即15;

接下來cout<<"min = "<<SearchMin(root)->key<<endl;  cout<<"max = "<<SearchMax(root)->key<<endl;  分別是輸出二叉搜索樹的最小及最大關鍵字,是2,20;

root->right->right的值20,故而它的前驅cout<<"前驅"<<SearchPredecessor(root->right->right)->key<<endl;是18;

root->left->right->right是13,故而它的后繼是cout<<"后繼"<<SearchSuccessor(root->left->right->right)->key<<endl;  是15;

這時刪除節點值為6的節點DeleteNode(root, 6);成了一個新的二叉搜索樹;


按照之前刪除的思想,因為刪除的是節點6,它的后繼是7,這時刪除7,此時是第二種情況,直接將13,9移上來,將6改為7;

這時cout<<"該節點的值"<<Search(root,15)->key<<endl;依然是15;

最大最小也依然沒變,cout<<"min = "<<SearchMin(root)->key<<endl;   cout<<"max = "<<SearchMax(root)->key<<endl;  
root->left的值是7
,它的前驅cout<<"前驅"<<SearchPredecessor(root->left)->key<<endl;是4;
root->left->right->left是9,它的后繼cout<<"后繼"<<SearchSuccessor(root->left->right->left)->key<<endl;是13;

運行如下:


大致就是這樣的,代碼如下(補丁不少):

#include <iostream>
#include <cstdio>
#include <cstdlib>

using namespace std;
typedef int ElemType;
typedef struct Node
{
ElemType key; //關鍵字
struct Node *left; //左孩子
struct Node *right; //右孩子
struct Node *parent; //父節點
} Node, *PNode;

//插入
void Insert(PNode &root, ElemType key)
{
//初始化b被插入結點
PNode p=(PNode)malloc(sizeof(Node));
p->key = key;
p->left = NULL;
p->right = NULL;
p->parent = NULL;
//空樹
if(root == NULL)
{
root = p;
return;
}
//左孩子
if(root->left == NULL && root->key > key)
{
p->parent = root;
root->left = p;
return;
}
//右孩子
if(root->right == NULL && root->key < key)
{
p->parent = root;
root->right = p;
return;
}

//關鍵值小於此時的節點值,放在左樹
//准備改回迭代的,今晚沒成功...
if(key < root->key)
Insert(root->left,key);
else
Insert(root->right,key);
}

//查找元素,找到返回關鍵字的結點指針,沒找到返回NULL
PNode Search(PNode root, ElemType key)
{
if(root == NULL || root->key == key)
return root;

if(key < root->key) //查找左子樹
return Search(root->left,key);
else //查找右子樹
return Search(root->right,key);
}

//查找最小關鍵字,空樹時返回NULL
PNode SearchMin(PNode root)
{
if(root == NULL)
return root;

while(root->left != NULL)
root = root->left;
return root;
}

//查找最大關鍵字,空樹時返回NULL
PNode SearchMax(PNode root)
{
if(root == NULL)
return root;

while(root->right != NULL) //迭代
root = root->right;
return root;
}

//查找前驅
PNode SearchPredecessor(PNode x)
{
if(x == NULL) //空
return x;

//若存在左孩子,前驅是其左子樹中最大的
if(x->left != NULL)
return SearchMax(x->left);

PNode y = x->parent;
while(y != NULL && x == y->left)
{
x = y;
y = x->parent;
}
return y;
}

//查找后繼
PNode SearchSuccessor(PNode x)
{
if(x == NULL) //空
return x;

//若存在右孩子,后繼是其右子樹中最小的
if(x->right != NULL)
return SearchMin(x->right);

PNode y = x->parent;
while(y != NULL && x == y->right)
{
x = y;
y = x->parent;
}
return y;
}

int DeleteNode(PNode &root,ElemType key)
{
PNode q;
//查找到要刪除的結點
PNode p = Search(root, key);

//沒查到此關鍵字
if(p == NULL)
return 0;
//一共有四種情況,該節點是葉子節點、只有左孩子、只有右孩子、左右孩子都有
//葉子結點
if(p->left == NULL && p->right == NULL)
{
//只有根節點
if(p->parent == NULL)
{
free(p);
root = NULL;
}
else
{
//刪除的結點是左孩子
if(p->parent->left == p)
p->parent->left = NULL;
else
p->parent->right = NULL;
free(p);
}
}

//左孩子
else if(p->left != NULL && p->right == NULL)
{
p->left->parent = p->parent;
//如果刪除是根結點
if(p->parent == NULL)
root = p->left;
//父節點的左孩子
else if(p->parent->left == p)
p->parent->left = p->left;
else
p->parent->right = p->left;
free(p);
}
//右孩子
else if(p->right != NULL && p->left == NULL)
{
p->right->parent = p->parent;
//如果刪除是根結點
if(p->parent == NULL)
root = p->right;
//是父節點的左孩子
else if(p->parent->left == p)
p->parent->left=p->right;
else
p->parent->right=p->right;
free(p);
}
//左右孩子都有
//該結點的后繼結點肯定無左子樹
//刪掉后繼結點,后繼結點的值代替該結點
else
{
//找到要刪除結點的后繼
q = SearchSuccessor(p);
ElemType temp = q->key; //暫存后繼結點的值

//刪除后繼結點
DeleteNode(root, q->key);
p->key = temp;
}
return 1;
}

//創建樹
void Create(PNode& root, ElemType *keyArray, int length)
{
for(int i = 0; i < length; i++)
Insert(root, keyArray[i]); //插入
}

int main()
{
PNode root = NULL;
ElemType nodeArray[11] = {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9}; //二叉數
Create(root, nodeArray, 11); //創建二叉搜索數

cout<<"該節點的值"<<Search(root,15)->key<<endl;
cout<<"min = "<<SearchMin(root)->key<<endl; //最小值
cout<<"max = "<<SearchMax(root)->key<<endl; //最大值
cout<<"前驅"<<SearchPredecessor(root->right->right)->key<<endl; //前驅
cout<<"后繼"<<SearchSuccessor(root->left->right->right)->key<<endl; //后繼
DeleteNode(root, 6);
cout<<"該節點的值"<<Search(root,15)->key<<endl;
cout<<"min = "<<SearchMin(root)->key<<endl; //最小值
cout<<"max = "<<SearchMax(root)->key<<endl; //最大值
cout<<"前驅"<<SearchPredecessor(root->left)->key<<endl; //前驅
cout<<"后繼"<<SearchSuccessor(root->left->right->left)->key<<endl; //后繼

return 0;
}

o(∩_∩)o 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com