C++簡(jiǎn)單實(shí)現(xiàn)與分析二叉搜索樹(shù)流程
二叉搜索樹(shù)
二叉搜索樹(shù)又被稱為二叉排序樹(shù)。它可以是一個(gè)空樹(shù),如果不是空樹(shù)則滿足下列性質(zhì):
1、如果它的左子樹(shù)不為空,那么左子樹(shù)上的所有節(jié)點(diǎn)都小于根。
2、如果它的右子樹(shù)不為空,那么右子樹(shù)上的所有節(jié)點(diǎn)都大于根
3、它的左子樹(shù)、右子樹(shù)也是二叉搜索樹(shù)。

二叉搜索樹(shù)的重要操作
二叉搜索樹(shù)的插入
1、如果樹(shù)為空,則直接插入
2、如果樹(shù)不為空,則找到對(duì)應(yīng)的位置插入。
查找辦法:
根據(jù)二叉搜索樹(shù)的性質(zhì),
1、如果給出的關(guān)鍵碼比當(dāng)前節(jié)點(diǎn)的關(guān)鍵碼小,則在當(dāng)前節(jié)點(diǎn)的左子樹(shù)中找位置
2、如果給出的關(guān)鍵碼比當(dāng)前節(jié)點(diǎn)的關(guān)鍵碼大,則在當(dāng)前節(jié)點(diǎn)的右子樹(shù)中找位置
如此反復(fù)循環(huán)……,直到找到一個(gè)空的位置插入。
二叉搜索樹(shù)的刪除
刪除分為三種情況:
- 情況一:要?jiǎng)h除的節(jié)點(diǎn)左孩子為空
- 情況二:要?jiǎng)h除的節(jié)點(diǎn)左孩子不為空,右孩子為空
- 情況三:要?jiǎng)h除的節(jié)點(diǎn)既有左孩子也有右孩子。
刪除情況一分析:

例如,刪除關(guān)鍵碼為1的節(jié)點(diǎn)。它的左孩子為空,那么遍歷這個(gè)二叉樹(shù),找到這個(gè)節(jié)點(diǎn)。讓這個(gè)節(jié)點(diǎn)的父親節(jié)點(diǎn)指向該節(jié)點(diǎn)的右孩子節(jié)點(diǎn)

但是需要考慮刪除節(jié)點(diǎn)的父節(jié)點(diǎn)是右孩子指向,還是左孩子指向。

刪除情況二分析:

例如,刪除關(guān)鍵碼為7的節(jié)點(diǎn)。它的左孩子不為空,右孩子為空。首先遍歷這個(gè)二叉樹(shù),找到這個(gè)節(jié)點(diǎn)。讓這個(gè)節(jié)點(diǎn)的父親節(jié)點(diǎn)指向該節(jié)點(diǎn)的左孩子節(jié)點(diǎn)。同樣需要考慮刪除節(jié)點(diǎn)的父節(jié)點(diǎn)是左孩子指向還是右孩子指向。
情況一和情況二都面臨這樣一個(gè)問(wèn)題,如果刪除的是根節(jié)點(diǎn)則需要單獨(dú)考慮。


刪除情況三分析:

解決辦法:替換法
替換法:如果刪除節(jié)點(diǎn)既有左孩子又有右孩子,為了刪除之后依舊能使其保留二叉搜索樹(shù)的性質(zhì),則需要將刪除的節(jié)點(diǎn)和一個(gè)合適的節(jié)點(diǎn)進(jìn)行替換,使這個(gè)合適的節(jié)點(diǎn)替換到刪除節(jié)點(diǎn)的位置,然后刪除被替換的節(jié)點(diǎn)即可解決。
兩個(gè)合適的節(jié)點(diǎn):
1、刪除節(jié)點(diǎn)的左子樹(shù)中最大節(jié)點(diǎn)。
2、刪除節(jié)點(diǎn)的右子樹(shù)中最小節(jié)點(diǎn)。
例如,刪除關(guān)鍵碼為5的節(jié)點(diǎn)。它的左孩子、右孩子都不為空。首先遍歷這個(gè)二叉樹(shù),找到這個(gè)節(jié)點(diǎn)。為使刪除后依舊能保持二叉搜索樹(shù)的性質(zhì),需要挑選一個(gè)合適的節(jié)點(diǎn)進(jìn)行替換。這個(gè)合適的節(jié)點(diǎn)是關(guān)鍵碼為4的節(jié)點(diǎn)(刪除節(jié)點(diǎn)的左子樹(shù)中最大節(jié)點(diǎn))和關(guān)鍵碼為6的節(jié)點(diǎn)(刪除節(jié)點(diǎn)的右子樹(shù)中最小節(jié)點(diǎn)),選一個(gè)即可。將替換節(jié)點(diǎn)的值給刪除節(jié)點(diǎn)后,刪除替換節(jié)點(diǎn),然后這個(gè)時(shí)候就轉(zhuǎn)變?yōu)榱藙h除情況一了,按照刪除情況一的做法即可完美刪除!


二叉搜索樹(shù)實(shí)現(xiàn)(key模型)
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
//insert
bool insert(const K& key)
{
if (_root == nullptr)
{
//為空
//直接就是給成根節(jié)點(diǎn)
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
//找到插入的位置
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false; //已經(jīng)有了,則不能插入
}
}
cur = new Node(key);
if (parent->_key > key)
{
//插入左邊
parent->_left = cur;
}
else
{
//插入右邊
parent->_right = cur;
}
return true;
}
bool Find(const K& key)
{
if (_root == nullptr)
{
return false;
}
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
bool erase(const K& value)
{
if (_root == nullptr)
{
return false;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > value)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < value)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到了,開(kāi)始刪除
//情況一:要?jiǎng)h除的節(jié)點(diǎn)左孩子為空
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
//刪除的是根節(jié)點(diǎn)
_root = cur->_right;
}
//判斷刪除的是左孩子節(jié)點(diǎn)還是右孩子節(jié)點(diǎn)以便更改連接關(guān)系
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr)
{
//情況二:要?jiǎng)h除的節(jié)點(diǎn)左孩子不為空、右孩子為空
if (parent == nullptr)
{
//刪除的是根節(jié)點(diǎn)
_root = cur->_left;
}
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
}
else
{
//情況三:要?jiǎng)h除的節(jié)點(diǎn)即有左孩子也有右孩子
//使用替換法
//兩種替換:1、用該節(jié)點(diǎn)的左子樹(shù)最大節(jié)點(diǎn) 2、用該節(jié)點(diǎn)右子樹(shù)的最小節(jié)點(diǎn)
//這里使用第一種替換方法
//找到用于替換的節(jié)點(diǎn)
Node* maxParent = cur;
Node* maxCur = cur->_right;
while (maxCur->_right)
{
maxParent = maxCur;
maxCur = maxCur->_right;
}
//
cur->_key = maxCur->_key;
//刪除用于替換的節(jié)點(diǎn)
if (maxParent->_left == maxCur)
{
maxParent->_left = maxCur->_left;
}
else
{
maxParent->_right = maxCur->_left;
}
delete maxCur;
}
return true;
}
}
//要?jiǎng)h除的節(jié)點(diǎn)不存在
return false;
}
//由于類外使用不到私有成員_root
//增加一個(gè)函數(shù)
void inorder()
{
_inorder(_root);
}
//遞歸版
Node* FindR(const K& key)
{
return _FindR(_root, key);
}
bool insertR(const K& key)
{
return _insertR(_root, key);
}
bool eraseR(const K& key)
{
return _eraseR(_root, key);
}
private:
void _inorder(Node* root) //不需要在類外顯示調(diào)用它,所以放在私有
{
if (root == nullptr)
{
return;
}
_inorder(root->_left);
cout << root->_key << " ";
_inorder(root->_right);
}
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key > key)
{
_FindR(root->_left, key);
}
else if (root->_key < key)
{
_FindR(root->_right, key);
}
else
{
//找到了
return root;
}
}
bool _insertR(Node*& root, const K& key) //注意root加引用
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (root->_key > key)
{
_insertR(root->_left, key);
}
else if (root->_key < key)
{
_insertR(root->_right, key);
}
else
{
return false;
}
}
bool _eraseR(Node*& root, const K& key)
{
if (root == nullptr)
{
//都已經(jīng)找到空了,表示不存在
return false;
}
if (root->_key > key)
{
_eraseR(root->_left, key);
}
else if (root->_key < key)
{
_eraseR(root->_right, key);
}
else
{
//找到要?jiǎng)h除的節(jié)點(diǎn)了,開(kāi)始刪除
Node* del = root;
//左孩子為空
if (root->_left == nullptr)
{
root = root->_right; //使用了引用,直接就是
}
else if (root->_right == nullptr)
{
//左孩子不為空,右孩子為空
root = root->_left;
}
else
{
Node* min = root->_right;
while (min->_left)
{
min = min->_left;
}
swap(min->_key, root->_key);
// 遞歸到右子樹(shù)去刪除
return _eraseR(root->_right, key);
}
delete del;
return true;
}
}
private:
Node* _root;
};二叉搜索樹(shù)的應(yīng)用
應(yīng)用一:排序+去重
應(yīng)用二:key模型、key/value模型
二叉搜索樹(shù)的排序體現(xiàn)在中序遍歷二叉搜索樹(shù)時(shí)是有序的。
key模型:key模型即只有key作為關(guān)鍵碼,結(jié)構(gòu)中只需要存儲(chǔ)Key即可,關(guān)鍵碼即為需要搜索到的值。其價(jià)值在于判斷“在不在”。
比如:給一個(gè)單詞word,判斷該單詞是否拼寫(xiě)正確,具體方式如下:
以單詞集合中的每個(gè)單詞作為key,構(gòu)建一棵二叉搜索樹(shù)
在二叉搜索樹(shù)中檢索該單詞是否存在,存在則拼寫(xiě)正確,不存在則拼寫(xiě)錯(cuò)誤。
key/value模型:每一個(gè)關(guān)鍵碼key,都有與之對(duì)應(yīng)的值Value,即<Key, Value>的鍵值對(duì)。該種方式在現(xiàn)實(shí)生活中非常常見(jiàn)。其價(jià)值在于可通過(guò)一個(gè)信息,找到其對(duì)應(yīng)的其他東西。。
比如:
1、通過(guò)英文查找對(duì)應(yīng)的中文;
2、高鐵檢票通過(guò)身份證查找對(duì)應(yīng)的乘車信息……
二叉搜索樹(shù)的實(shí)現(xiàn)(key/value模型)
//二叉搜索樹(shù)key/value模型
namespace KV
{
template<class K, class V>
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
:_left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
BSTree()
:_root(nullptr)
{}
bool insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
//找到要插入的位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
//在左子樹(shù)
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
//在右子樹(shù)
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key, value);
//
if (parent->_key > key)
{
//插入左孩子節(jié)點(diǎn)
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
Node* Find(const K& key)
{
if (_root == nullptr)
{
return nullptr;
}
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
//在左子樹(shù)
cur = cur->_left;
}
else if (cur->_key < key)
{
//在右子樹(shù)
cur = cur->_right;
}
else
{
//相等,找到了
return cur;
}
}
//不存在
return nullptr;
}
bool Erase(const K& key)
{
if (_root == nullptr)
{
return false;
}
//找到要?jiǎng)h除的節(jié)點(diǎn)
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到了
//開(kāi)始刪除
//情況一:要?jiǎng)h除的節(jié)點(diǎn)沒(méi)有左子樹(shù)
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
//刪除的是根節(jié)點(diǎn)
_root = cur->_right;
}
//判斷刪除的是左孩子節(jié)點(diǎn)還是右孩子節(jié)點(diǎn),方便更改連接關(guān)系
if (parent->_left = cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr)
{
//情況二:要?jiǎng)h除的節(jié)點(diǎn)左孩子不為空,,右孩子為空
if (parent == nullptr)
{
_root = cur->_left;
}
if (parent->_left = cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
}
else
{
//情況三:要?jiǎng)h除的節(jié)點(diǎn)既有左孩子也有右孩子
//要使用替換法刪除
//使用右子樹(shù)的最小節(jié)點(diǎn)進(jìn)行替換
Node* minParent = cur;
Node* minCur = cur->_right;
//找到右子樹(shù)的最小節(jié)點(diǎn)
while (minCur->_left)
{
minParent = minCur;
minCur = minCur->_left;
}
//替換
cur->_key = minCur->_key;
cur->_value = minCur->_value;
//刪除替換節(jié)點(diǎn),并更改連接關(guān)系
if (minParent->_left == minCur)
{
minParent->_left = minCur->_right;
}
else
{
minParent->_right = minCur->_right;
}
delete minCur;
}
return true;
}
}
return false;
}
void inorder()
{
_inorder(_root);
}
private:
void _inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_inorder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_inorder(root->_right);
}
private:
Node* _root;
};
}到此這篇關(guān)于C++簡(jiǎn)單實(shí)現(xiàn)與分析二叉搜索樹(shù)流程的文章就介紹到這了,更多相關(guān)C++二叉搜索樹(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++ 二叉搜索樹(shù)(BST)的實(shí)現(xiàn)方法
- C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹(shù))
- C++數(shù)據(jù)結(jié)構(gòu)二叉搜索樹(shù)的實(shí)現(xiàn)應(yīng)用與分析
- C++ 超詳細(xì)快速掌握二叉搜索樹(shù)
- C++深入細(xì)致探究二叉搜索樹(shù)
- C++?LeetCode0538二叉搜索樹(shù)轉(zhuǎn)換累加樹(shù)示例
- C++二叉搜索樹(shù)BSTree使用詳解
- C++二叉搜索樹(shù)模擬實(shí)現(xiàn)示例
相關(guān)文章
c++的glog與spdlog的性能對(duì)比測(cè)試分析
這篇文章主要為大家介紹了c++的glog與spdlog的性能對(duì)比測(cè)試分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
如何在C++中通過(guò)模板去除強(qiáng)制轉(zhuǎn)換
本文講解的是如何在C++中通過(guò)模板去除強(qiáng)制轉(zhuǎn)換,在編程工作中應(yīng)盡量少使用強(qiáng)制類型轉(zhuǎn)換,模板有助于我們實(shí)現(xiàn)這一目的,需要的朋友可以參考下2015-07-07
C語(yǔ)言的isatty函數(shù)和ttyname函數(shù)以及sendmsg函數(shù)用法
這篇文章主要介紹了C語(yǔ)言的isatty函數(shù)和ttyname函數(shù)以及sendmsg函數(shù)用法,是C語(yǔ)言入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09
c語(yǔ)言實(shí)現(xiàn)php的trim標(biāo)簽
本文給大家介紹的是使用C語(yǔ)言實(shí)現(xiàn)php的trim標(biāo)簽功能的代碼,非常的實(shí)用,其主要作用是清除字符串開(kāi)頭結(jié)尾除空白,有需要的小伙伴可以參考下。2016-01-01
C++數(shù)據(jù)結(jié)構(gòu)深入探究棧與隊(duì)列
棧和隊(duì)列,嚴(yán)格意義上來(lái)說(shuō),也屬于線性表,因?yàn)樗鼈円捕加糜诖鎯?chǔ)邏輯關(guān)系為 "一對(duì)一" 的數(shù)據(jù),但由于它們比較特殊,本章講解分別用隊(duì)列實(shí)現(xiàn)棧與用棧實(shí)現(xiàn)隊(duì)列2022-05-05

