二叉树的一个重要应用就是查找。
二叉搜索树 满足如下的性质:
左子树的关键字 < 节点的关键字 < 右子树的关键字
1. Find(x)
有了上述的性质后,我们就可以像二分查找那样查找给定的关键字x
具体如下: if x < node->val, Search in left sub-tree;
else if x > node->val, Search in right sub-tree;
else, found it!
2. Insert(x)
插入操作像Find(x)一样非常简单。要保证插入后,树仍然保持 左子树<本身<右子树,
那如何找到具体的插入位置呢? 我们可以换个思路考虑问题,假设x已经在树中,那它所在的位置就是插入位置!
Bingo! 上面的搜索算法得到的搜索路径的最后就是我们所求!
具体思路是:
按照Find的方法,搜索x, 如果发现已经有节点的值=x, 则返回;
否则, 在搜索路径的最后插入新的节点(x)
Figure 2: 插入关键字5
3. Delete(x)
删除操作相对麻烦一些。要考虑删除的节点有几个孩子。
首先搜索关键字x, 若没有,则返回。否则,删除该节点。
1> 若节点 has no child, 直接删除节点即可,能够保证所有节点满足 左子树<本身<右子树
2> 若节点 has one child,(不妨设为仅有右孩子)
3> 若 two children
为了保证搜索二叉树的性质,讲右子树中的最小值赋值给当前节点,然后递归删除右子树的最小节点
时间复杂度分析:
注意到上述的操作复杂度都是 O(h), h为树的高度。
因此如果二叉树是如下图的情况,二叉树将退化为链表,复杂度变为线性。
为了避免这种情况,在插入删除的时候引入平衡操作,保证树满足某一种平衡条件。这就是二叉平衡树。
// copyright @ L.J.SHOU Nov.8, 2013
// Binary Search Tree
#include "search-tree.h"
#include "binary-tree-printer.h"
#include <cstdlib>
#include <queue>
#include <iostream>
using namespace std;
const int N=10;
const int M=10;
typedef int ElementType;
/*
struct TreeNode
{
ElementType val;
TreeNode* left;
TreeNode* right;
TreeNode(ElementType x)
: val(x), left(NULL), right(NULL){}
};
*/
TreeNode* Destroy(TreeNode *t)
{
if(t != NULL)
{
t->left = Destroy(t->left);
t->right = Destroy(t->right);
delete t;
}
return NULL;
}
int Height(TreeNode *t)
{
if(t == NULL)
return -1;
int left = Height(t->left);
int right = Height(t->right);
if(left > right)
return left + 1;
else
return right + 1;
}
TreeNode* Find(TreeNode *t, ElementType x)
{
if(t != NULL)
{
if(x < t->val)
return Find(t->left, x);
else if(x > t->val)
return Find(t->right, x);
else
return t;
}
return NULL;
}
TreeNode* FindMin(TreeNode *t)
{ // iterative
TreeNode* p = t;
while(p && p->left)
p = p->left;
return p;
}
TreeNode* FindMax(TreeNode* t)
{ // recursive
if(t == NULL)
return NULL;
else if(t->right == NULL)
return t;
else
return FindMax(t->right);
}
TreeNode* Insert(TreeNode* t, ElementType x)
{ // recursive
if(t == NULL)
{
t = new TreeNode;
t->val = x;
t->left = t->right = NULL;
}
else if(x < t->val)
t->left = Insert(t->left, x);
else if(x > t->val)
t->right = Insert(t->right, x);
/* else x is already in the tree;
we'll do nothing */
return t;
}
TreeNode* Delete(TreeNode* t, ElementType x)
{ /* recursive */
TreeNode* p(NULL);
if(t == NULL)
cerr << "Element " << x << " Not Found" << endl;
else{
if(x < t->val)
t->left = Delete(t->left, x);
else if(x > t->val)
t->right = Delete(t->right, x);
else
{ /* t->val = x */
if(t->left && t->right)
{ /* two children */
/* find min in the right sub-tree */
p = FindMin(t->right);
t->val = p->val;
t->right = Delete(t->right, t->val);
}
else
{ /* one or no child */
p = t;
if(t->left == NULL)
t = t->right;
else if(t->right == NULL)
t = t->left;
delete p;
}
}
}
return t;
}
void PreOrderVisit(TreeNode* t)
{
if(t)
{
cout << t->val << " ";
PreOrderVisit(t->left);
PreOrderVisit(t->right);
}
}
void InOrderVisit(TreeNode* t)
{
if(t)
{
InOrderVisit(t->left);
cout << t->val << " ";
InOrderVisit(t->right);
}
}
void LevelOrderVisit(TreeNode* t)
{
if(t == NULL) return;
TreeNode *node(NULL);
queue<TreeNode* > q;
q.push(t);
while(!q.empty())
{
node = q.front(); q.pop();
cout << node->val << " ";
if(node->left)
q.push(node->left);
if(node->right)
q.push(node->right);
}
}
int main()
{
TreeNode* t(NULL);
srand(time(0));
for(int i=0; i<N; ++i)
t = Insert(t, rand()%M+1);
cout << "PreOrder: ";
PreOrderVisit(t);
cout << endl;
cout << "InOrder: ";
InOrderVisit(t);
cout << endl;
cout << "LevelOrder: ";
LevelOrderVisit(t);
cout << endl;
cout << "Height: " << Height(t) << endl;
cout << "Min: " << FindMin(t)->val << endl;
cout << "Max: " << FindMax(t)->val << endl;
PrintBinaryTree(t);
t = Destroy(t);
return 0;
}