【二叉搜尋樹】二叉搜尋樹的基本操作
阿新 • • 發佈:2019-02-07
什麼是二叉搜尋樹
二叉查詢樹(BinarySearch Tree,也叫二叉搜尋樹,或稱二叉排序樹Binary Sort Tree)或者是一棵空樹,或者是具有下列性質的二叉樹:
(1)若它的左子樹不為空,則左子樹上所有結點的值均小於它的根結點的值;
(2)若它的右子樹不為空,則右子樹上所有結點的值均大於它的根結點的值;
(3)它的左、右子樹也分別為二叉查詢樹。
二叉樹的節點
template<typename K, typename V> struct BianrySearchNode { BianrySearchNode(const K& key, const V& value) : _key(key) , _value(value) , _pLeft(NULL) , _pRight(NULL) {} K _key; V _value; BianrySearchNode<K, V>* _pLeft; BianrySearchNode<K, V>* _pRight; };
二叉搜尋樹主要的操作:查詢,插入,刪除。
【插入】
(1)插入操作需要遍歷二叉搜尋樹,
(2)a 待插入元素的值小於當前結點的key值,則訪問當前結點的 左子樹
b 待插入元素的值大於當前結點的key值,則訪問當前結點的右子樹
c 待插入元素的值等於當前結點的key值,則返回false,表示該元素已存在
程式碼實現
typedef BianrySearchNode<K, V> Node; bool Insert(const K& key, const V& value) //這裡實現用鍵值對 { if (NULL == _pRoot) { _pRoot = new Node(key, value); return true; } //找插入的點 Node *pCur = _pRoot; Node *pParent = NULL; while (pCur) { pParent = pCur; if (key < pCur->_key) pCur = pCur->_pLeft; else if (key > pCur->_key) pCur = pCur->_pRight; else return false; } // 插入 pCur = new Node(key, value); if (key < pParent->_key) pParent->_pLeft = pCur; else pParent->_pRight = pCur; return true; }
【刪除】
首先要查詢該元素是否存在,如果不存在就返回false;存在要分為以下幾種情況:
1、要刪除的結點無孩子結點;(可以劃分到2 或者3)
2、要刪除的結點只有左孩子結點;
3、要刪除的結點只有右孩子結點;
4、要刪除的結點有左、右孩子結點;
其實上面還可以分為根節點和非根節點
對於上述情況,相應的刪除方法如下:
a、直接刪除該結點
b、刪除該結點且使被刪除節點的雙親結點指向被刪除節點的左孩子結點;
c、刪除該結點且使被刪除節點的雙親結點指向被刪除結點的右孩子結點;
d、在它的右子樹中尋找中序下的第一個結點(關鍵碼最小),用它的值填補到被刪除節點中,再來處理該結點的刪除
畫幾個圖來表示更清楚
在第三種情況中:黑色實體為刪除的點,紅色原點為查詢的右子樹中最小的節點。
刪除元素為根節點和非根節點可以合併成一種情況
程式碼實現:
s
bool Remove(const K& key)
{
//找出要刪除的節點
Node* pCur = _pRoot;
Node* pParent = NULL;
while (pCur)
{
if (key < pCur->_key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (key > pCur->_key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
break;
}
//跳出迴圈,pCur為空,不為空 :可能為根節點,
//1該結點只有左子樹 :包含左右子樹都為空
//2該節點只有右子樹
//3該節點有左右子樹
if (pCur)
{
//只有左子樹
if (pCur->_pRight == NULL)
{
if (pCur == _pRoot) //刪除的節點為根節點,且只有左子樹
{
_pRoot = pCur->_pLeft;
}
else
{ //不為根
if (pParent->_pLeft == pCur) //判斷在左還是右
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
delete pCur;
}
else if (pCur->_pLeft == NULL) //只有右子樹
{
if (pCur == _pRoot)
_pRoot = pCur->_pRight;
else
{
if (pParent->_pLeft == pCur)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pCur;
}
else //左右子樹都存在:找右子樹中最小的,再把兩個的值互換,刪除最小的的那個結點
{
//找右子樹中最小的
Node* pRightMin = pCur->_pRight;
Node* pRightMinParent = pCur;
while (pRightMin->_pLeft)
{
pRightMinParent = pRightMin;
pRightMin = pRightMin->_pLeft;
}
//不分根節點和非根節點
pCur->_key = pRightMin->_key;
pCur->_value = pRightMin->_value;
if (pRightMin == pCur->_pRight)
pRightMinParent->_pRight = pRightMin->_pRight;
else
pRightMinParent->_pLeft = pRightMin->_pRight;
delete pRightMin;
}
return true;
}
return false;
}
這幾種情況的測試用例
void TestRemove1() //左子樹
{
//int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BinarySearchTree<int, int> bs;
BinarySearchTree<int, int> bs1;
bs.Insert(5, 5);
bs.Insert(3, 3);
bs.Insert(1, 1);
bs.Insert(7, 7);
bs.Insert(2, 2);
bs.Insert(0, 0);
bs.Remove(3); // 測試普通節點
//2種情況:根節點
bs1.Insert(5, 5);
bs1.Insert(3, 3);
bs1.Insert(1, 1);
bs1.Insert(2, 2);
bs1.Insert(0, 0);
bs1.Insert(4, 4);
bs1.Remove(5);//刪除根節點
}
void TestRemove2() //右子樹
{
//int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BinarySearchTree<int, int> bs;
bs.Insert(5, 5);
bs.Insert(6, 6);
bs.Insert(8, 8);
bs.Insert(7, 7);
bs.Insert(9, 9);
bs.Remove(6);
//測試根節點
bs.Remove(5);
}
void TestRemove3() //左右子樹
{
//int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BinarySearchTree<int, int> bs;
BinarySearchTree<int, int> bs1;
bs.Insert(4, 4);
bs.Insert(3, 3);
bs.Insert(7, 7);
bs.Insert(8, 8);
bs.Insert(5, 5);
bs.Insert(6, 6);
bs.Remove(4); //1
//不為根節點
bs1.Insert(4, 4);
bs1.Insert(3, 3);
bs1.Insert(6, 6);
bs1.Insert(5, 5);
bs1.Insert(9, 9);
//bs1.Insert(7, 7);
//bs1.Insert(8, 8);
bs1.Insert(10, 10);
bs1.Remove(6);
}