一、什么是KV结构搜索二叉树
二叉搜索树,又称二叉排序树,是二叉树的一种变种,抽象结构与二叉树基本相同,但又有着以下性质:
若它的左子树不为空,则左子树上的所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上的所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树。
简单的来说,就是左节点比根节点小,右节点比根节点大,图如下:
而所谓的KV结构搜索二叉树,K是指Key,即用来比较的关键字,V是指Value,即保存的值。就如上图一样,唯一不同的是,节点结构体中多出了一个新的变量Value用来保存别的数据。
在存取时更加的多样化。
二、KV结构二叉树的基本功能
1.二叉搜索树的查找
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。
2.二叉搜索二叉树的插入
插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质向左或向右查找插入位置,插入新节点
3.搜索二叉树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
情况d:在它的左子树中找到最大的节点或在右子树找到最小的节点,用来填补到被删除 节点中,再来处理该结点的删除问题--替换法删除
三、构建实现
了解之后,下面便是实现过程与测试用例。
1.实现代码
#pragma once
#include<iostream>
#include<string>
using namespace std;
namespace lxycc
{
template<class K, class V>
struct BSTNode
{
BSTNode(const K& key = K(), const V& value = V())
: _pLeft(nullptr), _pRight(nullptr), _key(key), _value(value)
{}
BSTNode<K,V>* _pLeft;
BSTNode<K,V>* _pRight;
K _key;
V _value;
};
template<class K, class V>
class BSTree
{
typedef BSTNode<K, V> Node;
public:
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)//如果根为空,则创建根
{
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_pRight;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_pLeft;
}
else//如果相等便不再插入
{
return false;
}
}
cur = new Node(key, value);
//链接
if (parent->_key > key)
{
parent->_pLeft = cur;
}
else
{
parent->_pRight = cur;
}
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_pLeft;
}
else if (cur->_key < key)
{
cur = cur->_pRight;
}
else
{
return cur;
}
}
return nullptr;//虽然可以返回cur,但是返回nullptr更保险
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)//安排好cur和parent
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_pLeft;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_pRight;
}
else
{
//1.左为空
if (cur->_pLeft == nullptr)
{
if (cur == _root)
{
_root = cur->_pRight;
}
else
{
if (parent->_pLeft == cur)
{
parent->_pLeft = cur->_pRight;
}
else
{
parent->_pRight = cur->_pRight;
}
}
delete cur;
}
else if (cur->_pRight == nullptr)//2.右为空
{
if (cur == _root)
{
_root = cur->_pLeft;
}
else
{
if (parent->_pLeft == cur)
{
parent->_pLeft = cur->_pLeft;
}
else
{
parent->_pRight = cur->_pLeft;
}
}
delete cur;
}
else//3.左右孩子都存在,找左树最大节点或右树的最小节点
{
Node* pchild = cur;
Node* child = cur->_pLeft;
while (child->_pRight)
{
pchild = child;
child = child->_pRight;
}
cur->_key = child->_key;
cur->_value = child->_value;
if (pchild->_pLeft == cur)//防止左右树只有一个节点无法进入循环
{
pchild->_pLeft = child->_pLeft;
}
else
{
pchild->_pRight = child->_pLeft;
}
delete child;
}
return true;
}
}
return false;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_pLeft);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_pRight);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* _root = nullptr;
};
2.测试用例
void TestBSTree()
{
BSTree<string, string> dict;
dict.Insert("insert", "插入");
dict.Insert("erase", "删除");
dict.Insert("left", "左边");
dict.Insert("string", "字符串");
string str;
while (cin >> str)
{
if (str=="quit")
{
break;
}
auto ret = dict.Find(str);
if (ret)
{
cout << str << ":" << ret->_value << endl;
}
else
{
cout << "单词拼写错误" << endl;
}
}
string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
// 统计水果出现的次
BSTree<string, int> countTree;
for (auto str : strs)
{
auto ret = countTree.Find(str);
if (ret == NULL)
{
countTree.Insert(str, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
}
int main()
{
TestBSTree();
return 0;
}
运行结果: