二叉排序树
什么是二叉排序树:即一个二叉树,它的每一个结点的左孩子的key值比当前结点的key值小,而右孩子结点的key值比当前结点的key值大,这样的一个树就是二叉排序树,也叫二叉搜索树(BST_tree);
一颗排序二叉树的结点框架大概就是这样:
template<class K>
struct SearchBinaryTreeNode
{
SearchBinaryTreeNode<K>* _left;
SearchBinaryTreeNode<K>* _right;
K _key;
//当然还可以自己加一些其它成员,比如说: int value;等等
//但是注意:结点中的key是树中唯一的,且不能改变的标识;
SearchBinaryTreeNode(const K& key)
:_left(NULL)
,_right(NULL)
,_key(key)
{}
};
为什么是排序树?
是因为如果你去中序遍历这棵树的话,遍历的结果是有序的(升序);
下面是一棵二叉排序树的例子:
实现这棵树的时候我们一般有 增,删,查三个操作,为什么没有改的操作?
其实可以有的,但是改结点的操作正如定义结点框架时所讲的,只能修改无关值value等,不能修改key值标识,二叉排序树是根据key值构造的树,如果修改了key值,那么这棵树就不确实是否依旧是排序树了;
实现操作代码之前,我会在这里对实现思路进行一定的说明;
首先,这三个操作都可以用递归和非递归的方法实现,代码中对递归和非递归都有一定的说明;
第一:增结点操作
即在这颗二叉树中增加一个结点,并且保持仍然是排序树,再次强调:树中的结点key值都是唯一的,不能重复;(后面不再说明,贯彻全文);所以,唯一需要解决的问题就是结点应该插在哪个位置?
看下图,如果我要在原图中插入一个结点,根据树的特点,比当前结点小,就往它的左树走,反之右树,直到左树为空,或者右树为空,就代表找到了可以插入的位置;
第二:查找操作
查找操作是最简单的操作,只要比较需要查找的Key值和二叉树的结点的key值,如果大于树中结点的key值就往右子树继续找,反之往左子树继续找,相等的话就是找到了;如果结点都为NULL了还没找到就是没有;
第三: 删除结点操作
删除结点的操作就略微有些麻烦了,因为要保持排序树的性质,所以删除的时候需要额外注意,首先需要在树中找到需要删除的结点,如果删除的是叶子结点的话就比较好删除,只要让父节点的left或者right指向空就好了(至于是父节点的right,还是父节点的left,递归和非递归的判别方法不一样,具体看代码),如果不是叶子结点呢,又可以分为三种情况:
第一种:当前结点的左为NULL,那么就让父节点的left或者right指向当前结点的右子树;
第二种:当前结点的右为NULL,那么就让父节点的left或者right指向当前结点的左子树;
其实可以看出来,要删除的是叶子结点的话也可归为左子树为NULL或者右子树为NULL的情况,因为叶子结点的左右子树都为NULL;
注意:前两种情况有一个坑:
//有一种情况是,删除根节点时,根节点只有一个子树,即按照左子树为NULL或者右子树为NULL的情况处理,此时根节点的parent是NULL,所以需要单独处理!
第三种:左右子树都不为NULL的情况
这种情况就需要想一下如何在不改变树性质的情况下,达到删除的目的,我们可以想想,在删除一个无头指针的指定结点的时候,应该怎么做?因为是没有头指针的,所以我们无法从投遍历链表,就不知道当前删除结点的前一个结点的指针,那么,解决的办法就是把当前结点的值和下一个结点的值进行交换,然后删除下一个结点就好了; 我们排序树这里的原理也是类似的,我们可以把当前需要删除的非叶子结点和一个比较容易删除的结点的key值进行交换,然后删除那个比较容易删除的结点;
那么,怎么找到这个比较容易删除的结点,可以交换key值并且不改变树的性质?
有两种方法,我们可以和当前结点左子树的最右结点的key值交换;或者和当前结点的右子树的最左结点的key值交换;
为什么呢?
因为交换之后还要保持当前结点的key值比它的左子树的key值大,比右子树的key值小,所以只有以上两种情况的结点可以满足要求;
例如下图:
交换结果如下,依然是二叉排序树:
然后删除结点3即可到到目的!
下图是删除结点的总分析图:
参考实现代码:
#include<iostream>
#include<cassert>
using namespace std;
template<class K>
struct SearchBinaryTreeNode
{
SearchBinaryTreeNode<K>* _left;
SearchBinaryTreeNode<K>* _right;
K _key;
SearchBinaryTreeNode(const K& key)
:_left(NULL)
,_right(NULL)
,_key(key)
{}
};
template<class K>
class SearchBinaryTree
{
public:
typedef SearchBinaryTreeNode<K> Node;
SearchBinaryTree():_root(NULL){}
SearchBinaryTree( K* arr, size_t size)
{
assert(arr);
_root = NULL;
for(size_t i = 0; i < size; i++)
{
//InsertR(_root,arr[i]);
Insert(arr[i]);
}
}
//增 递归
bool InsertR(Node*& root, K key)
{
if(!root)
{
root = new Node(key);
return true;
}
else if(root->_key == key)
return false;
else if(root->_key > key)
{
return Insert(root->_left ,key);
}
return Insert(root->_right ,key);
}
//增 非递归
bool Insert(const K& key)
{
if(_root == NULL)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = NULL;
int flag = -1;
while(cur)
{
parent = cur;
if(cur->_key == key)
return false;
else if(cur->_key < key)
{
cur = cur->_right ;
flag = 1;
}
else if(cur->_key > key)
{
cur = cur->_left ;
flag = 0;
}
}
cur = new Node(key);
if(flag == 1)
parent->_right = cur;
else if(flag == 0)
parent->_left = cur;
return true;
}
//删 递归
bool RemoveR(const K& key)
{
Node* parent = NULL;
return _RemoveR(_root, key);
}
//删 非递归
bool Remove(const K& key)
{
//左为NULL
//右为NULL
//左右都不为NULL
Node* cur = _root;
Node* parent = NULL;
while(cur)
{
if(cur->_key == key)
{
Node* del = cur;
if(cur->_left && cur->_right )
{
//删除左右孩子都不为NULL的情况
//可以选择左树的最右结点或者右树的最左结点交换删除
Node* minright = cur->_right ;
while(minright->_left )
minright = minright ->_left ;
cur->_key = minright->_key ;
del = minright ;
}
else if(cur->_left == NULL)
{
//有一种情况是,删除根节点时,根节点只有一个子树,即按照左子树为NULL
//或者右子树为NULL的情况处理,此时根节点的parent是NULL,所以需要单独处理!
if(parent == NULL)
{
_root = _root->_right ;
}
else
{
if(parent->_key < key)
parent->_right = cur->_right ;
else
parent->_left = cur->_right;
}
}
else if(cur->_right ==NULL)
{
if(parent == NULL)
{
_root = cur->_left ;
}
else
{
if(parent->_key < key)
parent->_right = cur->_left ;
else
parent->_left = cur->_left ;
}
}
delete del;
return true;
}
else if(cur->_key < key)
{
parent = cur;
cur = cur->_right ;
}
else if(cur->_key > key)
{
parent = cur;
cur = cur->_left ;
}
}
return false;
}
//查 非递归
bool Find(const K& key)
{
Node* cur = _root;
while(cur)
{
if(cur->_key == key)
return true;
else if(cur->_key < key)
cur = cur->_right ;
else
cur = cur->_left ;
}
return false;
}
//查找 递归
bool FindR(const K& key)
{
Node* root = _root;
return _FindR(root, key);
}
//中序遍历
void Inorder()
{
_Inorder(_root);
cout<<endl;
}
protected:
//删除 递归
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* del = root;
if(root->_left == NULL)
root = root->_right ;
else if(root->_right == NULL)
root = root->_left ;
else
{
Node* minright = root; //寻找右树的最左结点进行key值的交换
minright = root->_right ;
while(minright->_left)
minright = minright->_left ;
root->_key = minright->_key ;
del = minright;
}
delete del;
return true;
}
}
//查找 递归
bool _FindR(Node* root, const K& key)
{
if(root == NULL)
return false;
if(root->_key > key)
_FindR(root->_right ,key);
else if(root->_key < key)
_FindR(root->_left ,key);
else
return true;
}
void _Inorder(Node* root)
{
if(root == NULL)
return;
_Inorder(root->_left );
cout<<root->_key <<" ";
_Inorder(root->_right );
}
private:
Node* _root;
};
int main()
{
int arr[10] = {5,2,4,6,9,0,3,7,8,1};
SearchBinaryTree<int> t(arr,10);
t.Inorder ();
t.Remove (0);
t.Remove (1);
t.Remove (2);
t.Remove (3);
t.Remove (4);
t.Remove (5);
t.Remove (6);
t.Remove (7);
t.Remove (8);
t.Remove (9);
t.Inorder ();
system("pause");
return 0;
}
二叉排序树的增删查操作解析
本文介绍了二叉排序树的概念及其特性,包括中序遍历得到有序序列,增、删、查三个基本操作的实现思路。在增加结点时,遵循比当前结点小插入左子树,大的插入右子树的原则。查找操作通过比较key值决定搜索方向。删除结点分为叶子结点、左子树或右子树为NULL、左右子树都不为NULL三种情况,后两种情况可能需要调整相邻结点的key值来保持排序性质。
4021

被折叠的 条评论
为什么被折叠?



