二叉搜索樹是一種常用的數據結構,其也是面試中常被問到的問題,因此掌握二叉搜索樹十分必要。
本文就二叉搜索樹上的一些常見操作進行較爲詳細的介紹。
二叉搜索樹
首先看一下二叉搜索樹的定義:
二叉搜索樹或者是一棵空樹,或者是具有下列性質的二叉樹: 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; 它的左、右子樹也分別爲二叉排序樹。——百度百科
憑藉二叉搜索樹的性質,其可以用做字典樹和優先隊列。
二叉搜索樹上的常見操作主要有查找,插入,刪除,求最大值,最小值,求一個節點的前驅結點和後繼節點,前驅結點和後繼節點我會單獨寫文章來介紹。上述所有的操作都能在O(lgN)時間內完成,也就是樹的高度。
操作
我們首先定義二叉搜索樹節點的結構:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
可以看出我們不考慮有父親指針。
查找
二叉搜索樹的查找類似於二分查找。給定一個value值,我們從根節點root開始,
- 若當前節點的val值和value相等,那麼就返回該節點
- 若val值小於value,就查找當前節點的右子樹
- 若val值大於value,就查找當前節點的左子樹
整體思路非常簡單,實現代碼如下:
TreeNode* TreeSearch(int val,TreeNode* root)
{
TreeNode* cur = root;
while(cur)
{
if(cur->val == val) return cur;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
return cur;
}
插入
插入過程和查找過程比較像,從根節點開始不斷向下查找插入的位置,過程一直記錄當前節點的父親節點,最後完成插入操作。代碼如下:
TreeNode* TreeInsert(int val,TreeNode* root)
{
TreeNode* cur = root;
TreeNode* parent =NULL;
while(cur)
{
parent = cur;
if(cur->val == val) return cur;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
TreeNode* newNode = new TreeNode(val);
if(NULL != parent)//非空樹
{ if(parent->val > val)
parent->left = newNode;
else
parent->right = newNode;
}
return newNode;
}
刪除節點
刪除節點是這幾個操作中最複雜的一個操作,要分3中不同的情況討論。
根據給定val值找到對應的節點,若
- 該節點沒有左子樹也沒有右子樹,那麼直接刪除該節點
- 該節點只有左子樹或者只有右子樹,那麼可以通過其父親節點和其子節點建立一條連接來刪除目標節點
- 該節點既有左子樹又有右子樹,那麼首先要刪除當前節點的後繼節點m(該節點是val值大於當前節點val值中最小的節點,該節點一定沒有左子樹),再用m的內容來替代當前節點
下面盜用算法導論中圖來進行直觀的解釋…
沒有左右子樹的情況.
只有左子樹或者只有右子樹的情況
左右子樹都有的情況
實現代碼如下:
TreeNode* getNextNode(TreeNode* root)
{
if(NULL == root) return root;
while(root->left)
{
root = root->left;
}
return root;
}
void TreeDelete(int val, TreeNode* root)
{
TreeNode* node= root;
TreeNode* parent =NULL;
while(cur)
{
parent = cur;
if(cur->val == val) break;
if(cur->val > val) cur = cur->left;
else cur = cur->right;
}
if(NULL == node) return;
if(!node->left && !node->right)//左右子樹都沒有的情況
{ delete node; return;}
if(node->left && node->right)//左右子樹都有
{
//找到node節點的後繼節點 即是右子樹中val最小的節點(求後繼節點的一種情況)
TreeNode* next = getNextNode(node->right);
int newVal = next->val;
TreeDelete(next->val,root);//刪除後繼節點
node->val = newVal;//重新設定當前節點的val值
}else{
//只有左子樹或者右子樹
TreeNode* child = node->left ? node->left : node->right;
if(parent->left == node)
parent->left = child;
else
parent->right =child;
delete node;
}
}
最大值最小值
返回一個二叉搜索樹中的最大值最小值可以說是最簡單的一個操作,最大值是樹中“最右邊”的節點的val值,最小值是“最左邊”的節點的val值,實現代碼如下:
int getMax(TreeNode* root)
{
if(NULL == root) return -1;//?並不是很合適,假設樹中元素值都爲正..
while(root->right)
root = root->right;
return root->val;
}
int getMin(TreeNode* root)
{
if(NULL == root) return -1;//?並不是很合適,假設樹中元素值都爲正..
while(root->left)
root = root->left;
return root->val;
}
本文總結了二叉搜索樹的常見操作,這些操作包括:查找,插入,刪除,最大最小值。這些操作的時間複雜度都爲O(lgN),即爲樹的高度。求一個節點的後繼和前驅節點會在另外一篇文章中給出..本文有點類似搬運工的感覺,但是自己總結一遍總會比不總結強,代碼自敲,有問題麻煩請指出哈。