首先說的應該是我們的搜索二叉樹是的,而這個有序是中序的有序
-
相對其他兩個操作搜索二叉樹的刪除應該是最複雜的這個複雜的刪除還是應該畫圖的,如果不畫圖是很難說清楚也很難理清楚的
好了我們的圖先派在這裏了,下面操作也都是用這個圖作爲標準了
當我們只是查看搜索二叉樹時候我們並不需要修改搜索二叉樹裏面的節點也就是元素的順序沒有被我們打亂,所以了我們只需要清楚一個比較重要的概念就是頭節點這個也是我們判斷一個結點位置很重要的條件我們的插入,還要刪除主要的核心點也就是和父節點的比較當中 -
先來定義一顆搜索二叉樹吧
#pragma once
#include <iostream>
using namespace std;
template<class T>
struct BSTNode
{
BSTNode(const T& data)
: _pLeft(nullptr)
, _pRight(nullptr)
, _data(data)
{}
BSTNode<T>* _pLeft;
BSTNode<T>* _pRight;
T _data;
};
這裏也沒有什麼好說的c++裏面初始化一下
- 插入
template<class T>
class BSTree
{
typedef BSTNode<T> Node;
typedef Node* PNode;
public:
BSTree()
: _pRoot(nullptr)
{}
bool Insert(const T& data)
{
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
// 找帶插入元素在二叉搜索樹中的位置
PNode pCur = _pRoot;
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;
}
// 插入節點
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
我們在插入節點時候分別要在根節點的左孩子,右孩子裏面去判斷一下我們要插入的數據在節點中是否存在,如果這個節點存在了,我們也不用插入了。所以這裏我們最好用一個bool類型的,如果存在就直接結束掉
- 查找
PNode Find(const T& data)
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
插入裏面判斷存在就已經是查找了,這裏查找還是比較簡單的。
- 刪除
bool Delete(const T& data)
{
PNode pCur = _pRoot;
PNode pParent = nullptr;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
if (nullptr == pCur)
return false;
// 刪除節點
/*
1. 待刪除節點沒有孩子
2. 待刪除節點只有左孩子
3. 待刪除節點只有右孩子
4. 待刪除節點左右孩子均存在
*/
PNode pDelNode = pCur;
if (nullptr == pCur->_pLeft)
{
// 當前節點只有右孩子
if (pCur == _pRoot)
{
_pRoot = pCur->_pRight;
}
else
{
// 待刪除節點爲其雙親的左孩子
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else // 待刪除節點爲其雙親的右孩子孩子
pParent->_pRight = pCur->_pRight;
}
}
else if (nullptr == pCur->_pRight)
{
// 當前節點只有左孩子
if (pCur == _pRoot)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
}
else
{
// 當前節點左右孩子均存在
// 直接刪除,不好刪除,在右子樹中找一個替代節點
pParent = pCur;
pCur = pCur->_pRight;
// 找中序遍歷下的第一個節點
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
// 用替代節點中內容替換待刪除節點
pDelNode->_data = pCur->_data;
pDelNode = pCur;
// 刪除替代節點
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pDelNode;
return true;
}
刪除還是複雜好多,由於沒有要求控制時間複雜度,其實搜素樹的刪除明顯還是相對avl樹簡單很多吧但是就算這樣其實還是挺麻煩的,因爲多數情況下,拿腦殼想搜索二叉樹的刪除還是很麻煩是呀,我們要畫圖,畫圖。圖我就在放這裏了
-
我們需要先看被刪除節點左孩子爲空情況
左孩子爲空時候,被刪節點就沒有左邊的干擾了。右孩子又給左孩子大,我們的右孩子就直接可以先放到左孩子的位置,然後刪除,那樣整個搜索二叉樹的順尋結構其實也並沒有被破壞。
-
被刪除節點右孩子爲空
被刪節點後面的節點是直接可以上移的,主要考慮一下被刪節點在根節點的左邊還是在根節點的右邊畫畫圖思路就自然裏清楚了。 -
被刪節點左右孩子均存在的情況
當被刪節點的左右孩子均存在時候,我們可以去查找被刪節點的右孩子然後拿被刪節點右孩子的中序遍歷查找出來被刪節點右孩子節點子樹當中最小的節點,**這個節點其實是距離被刪節點的值最接近的值**用這個值去替代被刪節點。然後鏈接好以後刪除被刪節點就可以了。 -
完整代碼
#pragma once
#include <iostream>
using namespace std;
template<class T>
struct BSTNode
{
BSTNode(const T& data)
: _pLeft(nullptr)
, _pRight(nullptr)
, _data(data)
{}
BSTNode<T>* _pLeft;
BSTNode<T>* _pRight;
T _data;
};
template<class T>
class BSTree
{
typedef BSTNode<T> Node;
typedef Node* PNode;
public:
BSTree()
: _pRoot(nullptr)
{}
bool Insert(const T& data)
{
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
// 找帶插入元素在二叉搜索樹中的位置
PNode pCur = _pRoot;
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;
}
// 插入節點
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
PNode Find(const T& data)
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
bool Delete(const T& data)
{
PNode pCur = _pRoot;
PNode pParent = nullptr;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
if (nullptr == pCur)
return false;
// 刪除節點
/*
1. 待刪除節點沒有孩子
2. 待刪除節點只有左孩子
3. 待刪除節點只有右孩子
4. 待刪除節點左右孩子均存在
*/
PNode pDelNode = pCur;
if (nullptr == pCur->_pLeft)
{
// 當前節點只有右孩子
if (pCur == _pRoot)
{
_pRoot = pCur->_pRight;
}
else
{
// 待刪除節點爲其雙親的左孩子
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else // 待刪除節點爲其雙親的右孩子孩子
pParent->_pRight = pCur->_pRight;
}
}
else if (nullptr == pCur->_pRight)
{
// 當前節點只有左孩子
if (pCur == _pRoot)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
}
else
{
// 當前節點左右孩子均存在
// 直接刪除,不好刪除,在右子樹中找一個替代節點
pParent = pCur;
pCur = pCur->_pRight;
// 找中序遍歷下的第一個節點
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
// 用替代節點中內容替換待刪除節點
pDelNode->_data = pCur->_data;
pDelNode = pCur;
// 刪除替代節點
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pDelNode;
return true;
}
void InOrder()
{
_InOrder(_pRoot);
}
void Destroy()
{
_Destroy(_pRoot);
}
private:
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
void _Destroy(PNode& pRoot)
{
if (pRoot)
{
_Destroy(pRoot->_pLeft);
_Destroy(pRoot->_pRight);
delete pRoot;
pRoot = nullptr;
}
}
private:
PNode _pRoot;
};
void TestBSTree()
{
int array[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BSTree<int> t;
for (auto e : array)
{
t.Insert(e);
}
//t.Delete(5);
//t.Delete(8);
t.Delete(6);
t.InOrder();
t.Destroy();
}
int main()
{
TestBSTree();
return 0;
}
我們刪除節點6,然後中旬遍歷打印節點,驗證一下結果,和搜索二叉樹的操作是否有問題。
被刪節點6成功刪除,沒有啥問題。