二叉查找树的C++实现
二叉查找树(BST)是父节点的值比左儿子的值大,比右儿子的值小的一种二叉树。其数据元素集合包括每个节点,每个节点又包含节点的值和它的左儿子和右儿子。基本操作有:构造空的BST;判空;查找;插入;删除和遍历。可以采用递归的方法来定义这些操作,也可采用非递归方法。
插入和删除相对来说比较有难度,如果有不对的还请看到的童鞋指出来。
在非递归中,插入和删除都用到了查找,在插入的时候,如果该元素已经在树中,就不用再次插入了,在删除的时候,如果该元素不在树中,那还删除什么?
所以,先写一下查找函数吧。
- 查找Search
找到这一个值item,如果找到了found=true,还需要item这个值所在的位置p和它父亲所在的位置pre。 - 插入
先查找一遍,没有该值则插入,由于Search函数中记录了p和pre,那么,只需判断一下该值与pre->data的大小,就可以了。 - 删除
我觉得删除是这几个中最麻烦的,因为有三种情况:1. 删除的是叶子节点;2.删除的是只有一个儿子的节点;3.删除的是有两个儿子的节点。
情况1和2都比较好解决,先Search一遍,这样可以找到要删除的点item的p和pre,然后建立一颗子树subtree,如果p的左子树为NULL,那么subtree=p->right,比较item和pre->data的大小,然后,将pre的left或right指向subtree就OK了。
说说情况3,可以这样想,将情况3转换成情况1或2,怎么转换呢?当然,Search后记录了p和pre,想想,把当前这个节点删了,你要找其它的来替换它吧,不然它的孩子怎么办?找谁呢?要找的这个值要比它的左值大,又要比右值小,所以一定要在它的右子树找,找到什么时候为止呢?找到最小点!也就是要找的那个点没有左儿子!这时,将这个点赋值给要删除的那个点,而将这个点删除,这个点有什么特点?它的left=null对不对?又变为了情况2或1。
非递归的实现:
#include <iostream>
using namespace std;
class BST
{
public:
class node
{
public:
node(){ left = right = NULL; }
public:
int data;
node* left;
node* right;
};
public:
BST(){ root = NULL; };
BST(int item){ root->data = item; root = NULL; }
bool empty()const;
void Search(int item, bool &found, node* &pre, node* &p);
void Insert(int value);
void Delete(int item);
private:
node* root;
};
bool BST::empty()const
{
return (root == NULL);
}
void BST::Search(int item, bool &found, node* &pre, node* &p)
{
for (;;)
{
if (found||p==NULL)
break;
else
{
if (item<p->data)
{
pre = p;
p = p->left;
}
else if (item > p->data)
{
pre = p;
p = p->right;
}
else
found = true;
}
}
}
void BST::Insert(int value)
{
bool found = false;
node* p = root;
node* pre = p;
Search(value, found, pre, p);
if (found)
cout << "The value has been in the BST" << endl;
else
{
node* n = new node;
n->data = value;
if (empty())
root = n;
else
{
if (value < pre->data)
pre->left = n;
if (value>pre->data)
pre->right = n;
}
}
}
void BST::Delete(int item)
{
bool found = false;
node* p = root;
node* pre = p;
Search(item, found, pre, p);
if (!found)
cout << "The value is not in the BST" << endl;
else
{
if (p->left!=NULL&&p->right!=NULL)
{
node* p2 = p->right;//找到右子树最小点
node* pre2 = p;//找到右子树最小点的父节点
while (p2->left!=NULL)
{
pre2 = p2;
p2 = p2->left;
}
p->data = p2->data;
pre = pre2;//与情况1和2合并
p = p2;//与情况1和2合并
}
node* subtree = NULL;
if (p->left == NULL)
subtree = p->right;
if (p->right == NULL)
subtree = p->left;
if (pre == NULL)
root = subtree;
else
if (pre->right==p)
pre->right = subtree;
else
pre->left = subtree;
delete p;
}
}
递归的用来遍历是最方便的了
void BST::Inorder(node* p)const
{
if (p!=NULL)
{
Inorder(p->left);
cout << p->data;
Inorder(p->right);
}
}
附上一个我看过了一个题目,我当时做不出来,现在可以了。
题目:在二元树中找出和为某一值的所有路径
输入一个整数和一棵二元树。从树的根结点开始往下访问一直到叶结点所经过的所有结点形成一条路径。
打印出和与输入整数相等的所有路径。
例如输入整数22 和如下二元树
10
/ \
5 12
/ \
4 7
则打印出两条路径:10, 12 和10, 5, 7。
这个题目基本就是遍历,打印路径,那么应该用递归+队列或数组
思路,由于要打印路径,那么就应该将走过的路径记录下来 ,可以放入数组中,还需构造一个二叉树,添加10/5/4/7/12这几个数,然后先访问10这个数,入数组,用10与sum=22比较,10<22,sum-10=12,继续比较,12入数组,这时,与12刚好相等,sum=0,同时12是叶子节点,依次出数组;回到父节点,继续。代码如下:
#include <iostream>
#include <vector>
using namespace std;
class BSTNode
{
public:
BSTNode(){ Left = Right = 0; }
BSTNode(int & item){ data = item; }
public:
int data;
BSTNode* Left;
BSTNode* Right;
};
class BSTTree
{
public:
BSTTree(){ root = 0; }
void Insert(BSTNode* &loc, int value);
void PrintPath(BSTNode* loc, int sum, vector<int>& path);
public:
BSTNode* root;
};
void BSTTree::Insert(BSTNode* & loc,int value)
{
if (loc == 0)
loc = new BSTNode(value);
else if (value<loc->data)
{
Insert(loc->Left, value);
}
else if (value > loc->data)
{
Insert(loc->Right, value);
}
else
cout << "该数已经在树中了!" << endl;
}
void BSTTree::PrintPath(BSTNode* loc, int sum, vector<int>& path)
{
bool isleaf = loc->Left == 0 && loc->Right==0;
path.push_back(loc->data);
if (loc->data > sum || loc == 0)
return;
else if (loc->data==sum&&isleaf)
{
for (vector<int>::iterator i = path.begin(); i != path.end();i++)
{
cout << *i << " ";
}
}
else
{
sum = sum - loc->data;
if (loc->Left)
{
PrintPath(loc->Left, sum, path);
}
if (loc->Right)
{
PrintPath(loc->Right, sum, path);
}
}
sum = sum + loc->data;
path.pop_back();
}
int main()
{
BSTTree T;
vector<int> path;
T.Insert(T.root, 10);
T.Insert(T.root, 5);
T.Insert(T.root, 4);
T.Insert(T.root, 7);
T.Insert(T.root, 12);
T.PrintPath(T.root, 22, path);
return 0;
}
个人觉得,在插入,遍历和查找时用递归很方便,删除的话我比较喜欢非递归方式。