【資料結構】二叉搜尋樹
阿新 • • 發佈:2018-12-26
概念及其性質
二叉搜尋樹,又名二叉排序樹,二叉查詢樹
二叉搜尋樹有一下特點:
(1)若左子樹不為空,則左子樹的所有節點均小於根節點
(2)若右子樹不為空,則右子樹的所有節點均大於根節點
(3)左右子樹也是二叉搜尋樹
(4)每棵樹都有自己的key值,而且不能重複
如何定義二叉搜尋樹
//二叉搜尋樹的節點,Key-Value結構 template<typename K,typename V> struct ResearchBinaryTreeNode { ResearchBinaryTreeNode<K, V>* _left; ResearchBinaryTreeNode<K, V>* _right; K _key; V _value; ResearchBinaryTreeNode(const K& key,const V& value); }; //定義二叉搜尋樹 template<typename K,typename V> class ResearchBinaryTree { typedef ResearchBinaryTreeNode<K,V> Node; public: ResearchBinaryTree();//建構函式 ~ResearchBinaryTree();//解構函式 bool Insert(const K& key,const V& value);//插入 Node* Find(const K& key);//查詢 bool Remove(const K& key);//刪除 void InOrder();//中序遍歷 Node* FindR(const K& key);//遞迴形式查詢 bool InsertR(const K& key, const V& value);//遞迴形式插入 bool RemoveR(const K&key);//遞迴形式刪除 protected: Node* _root; };
二叉搜尋樹的查詢
二叉搜尋樹的查詢,就是從根節點開始,進行key值的比較
若相同,則查詢到;若大於查詢的key值,則走左孩子;小於的話走右孩子;如果為空,則沒找到
非遞迴實現
Node* Find(const K& key) { Node* cur = _root; //根據搜尋二叉樹的特點來進行查詢 while (cur) { if (key < cur->_key) cur = cur->_left; else if (key>cur->_key) cur = cur->_right; else return cur; } return NULL; }
遞迴實現
Node* FindR(const K& key) { return _FindR(_root, key); } Node* _FindR(Node* root, const K& key) { if (root == NULL) return NULL; if (key < root->_key) return _FindR(root->_left, key); else if (key>root->_key) return _FindR(root->_right, key); else return root; }
二叉搜尋樹的插入
非遞迴實現
bool Insert(const K& key,const V& value)
{
if (_root == NULL)
{
_root = new Node(key,value);
return true;
}
Node* cur = _root;
Node* parent = cur;
//找到需要插入節點的父親節點
while (cur)
{
parent = cur;
if (cur->_key < key)
cur = cur->_right;
else if (cur->_key>key)
cur = cur->_left;
else
return false;
}
//parent為需要插入節點的父親節點
if (parent->_key > key)
parent->_left = new Node(key,value);
else if (parent->_key<key)
parent->_right = new Node(key,value);
return true;
}
遞迴實現
bool InsertR(const K& key, const V& value)
{
return _InsertR(_root, key, value);
}
bool _InsertR(Node*& root, const K& key, const V& value)
{
//構建新節點
if (root == NULL)
{
root = new Node(key, value);
return true;
}
if (key < root->_key)
return _InsertR(root->_left, key, value);
else if (key > root->_key)
return _InsertR(root->_right, key, value);
else
return false;
}
二叉搜尋樹的刪除
二叉搜尋樹稍微複雜一點的地方就是刪除部分,在刪除一個節點的時候,有四種情況
(1)刪除節點的左子樹為空
如刪除節點6
(2)刪除節點的右子樹為空
如刪除節點9
(3)刪除節點的左子樹和右子樹都為空
如刪除節點2
(4)刪除節點的左子樹和右子樹都不為空
如刪除節點7
由於當刪除節點的左子樹和右子樹都為空時,左子樹和右子樹都為空,滿足左子樹為空(或右子樹為空)的條件,因為我們可以將這種情況劃分到左子樹為空的情況中
因此,三種情況的處理結果如下:
(1)若左子樹為空,就讓父親節點指向刪除節點的右子樹;比如刪除6,就讓7指向6的右子樹
(2)若右子樹為空,就讓父親節點指向刪除節點的左子樹;比如刪除3,就讓5指向3的左子樹
(3)若都不為空,則用替換法進行刪除;比如刪除7,就找7的右子樹(9)的最左節點8,將8放到7的位置,然後刪除原來的8
非遞迴實現
bool Remove(const K& key)
{
Node* cur = _root;
Node* parent = NULL;
Node* delNode = NULL;
//找出要刪除的節點以及其父親節點
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key >cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
break;
}
}
if (cur == NULL)
return false;
//如果刪除的是根節點,那麼parent的值為NULL
//cur此時是要刪除的節點
if (cur->_left == NULL)
{
delNode = cur;
//cur是父親節點的左孩子的話,就把cur的右孩子賦給父親節點的左孩子
//否則,將cur的右孩子賦給父親節點的右孩子
if (parent == NULL)
_root = cur->_right;
else if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
else if (cur->_right == NULL)
{
delNode = cur;
//cur是父親節點的左孩子的話,就把cur的右孩子賦給父親節點的左孩子
//否則,將cur的右孩子賦給父親節點的右孩子
if (parent == NULL)
_root = cur->_left;
else if(parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
else
{
//都不為空的情況,需要採用替換法來解決
Node* subLeft = NULL;//定義右子樹的最左節點
//迴圈找到右子樹的最左節點
//這裡subLeft不可能為空
subLeft = cur->_right;
parent = cur;
while (subLeft->_left)
{
parent = subLeft;
subLeft = subLeft->_left;
}
cur->_key = subLeft->_key;
if (parent->_left == subLeft)
parent->_left = subLeft->_right;
else
parent->_right = subLeft->_right;
delNode = subLeft;
}
delete delNode;
delNode = NULL;
return true;
}
遞迴實現
bool RemoveR(const K&key)
{
return _RemoveR(_root, key);
}
bool _RemoveR(Node* root,const K& key)
{
if (root == NULL)
return false;
//遞迴,找到要刪除的節點
if (root->_key < key)
return _RemoveR(root->_right, key);
else if (root->_key > key)
return _RemoveR(root->_left, key);
else
{
Node* delNode = root;
//刪除節點的左為空
if (root->_left == NULL)
root = root->_right;
else if (root->_right == NULL)
root = root->_left;
else//左右都不為空的情況
{
Node* parent = root;
Node* subLeft = root->_right;
while (subLeft->_left)
{
parent = subLeft;
subLeft = subLeft->_left;
}
delNode = subLeft;
//若為左子樹,代表走了while迴圈,否則沒有走迴圈
//要刪除的節點是subLeft
root->_key = subLeft->_key;
if (parent->_left == subLeft)
parent->_left = subLeft->_right;
else
parent->_right = subLeft->_right;
delete delNode;
return true;
}
}
}