目录
一 二叉搜索树 (KEY)
1 二叉搜索树
1.1 概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
①若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
②若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
③它的左右子树也分别为二叉搜索树。
1.2 基本结构
#pragma once
#include<iostream>
using namespace std;
//二叉搜索树的KEY模型
namespace KEY
{
//树节点
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
//二叉搜索树
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
//BSTree() = default;
//构造函数
BSTree()
:_root(nullptr)
{}
private:
Node* _root = nullptr;
};
}
2 二叉搜索树的查找
2.1 思路
① 若根节点为空:返回flase,若不为空:
②如果根节点key==查找key,返回true
③如果根节点key >查找key,在其左子树查找。如果根节点key <查找key,在其右子树查找。否则返回false
2.2 代码实现
//搜索存储在树中的数据
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
//要搜索的值比较大,就向右边找
if (cur->_key < key)
{
cur = cur->_right;
}
//要搜索的值比较小,就向左边找
else if (cur->_key > key)
{
cur = cur->_left;
}
//找到了,返回位置
else
{
return cur;
}
}
//没找到返回空
return nullptr;
}
3 二叉搜索树的插入
3.1 思路
插入的具体过程如下: ①树为空,则直接插入。②树不空,按二叉搜索树性质查找插入位置,插入新节点。
3.2 代码实现
//插入数据
bool Insert(const K& key)
{
//如果树为空,开一个节点,直接将数据插入
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
//不为空
//查找要插入的位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//要插入的值比当前节点值大,就往右找
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
//要插入的值比当前节点值小,就往左找
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
//如果有相等的值,就不能插入,插入失败
else
{
return false;
}
}
//找到之后,开新节点插入数据,将节点连接起来
cur = new Node(key);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
4 二叉搜索树的删除
4.1 分情况处理
①:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
②:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
③:在它的右子树中寻找中序下的第一个结点
(
关键码最小
)
,用它的值填补到被删除节点中,
再来处理该结点的删除问题
4.2 代码实现
//删除树中的数据
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
//找到后,开始删除
else
{
//情况一:需要删除的结点左子树为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//情况二:需要删除的结点右子树为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
情况三:需要删除的结点左右子树都不为空
else
{
// 找到右树最小节点去替代删除
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRight == minRightParent->_left)
minRightParent->_left = minRight->_right;
else
minRightParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
二 二叉搜索树(KEY_VALUE)
1 KV模型基本概念
每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文<word, chinese>就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
2 代码实现
2.1 基本结构
namespace KEY_VALUE
{
//树结点
template<class K, class V>
struct BSTreeNode
{
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
: _left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
//树
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
private:
Node* _root = nullptr;
};
}
2.2 数据插入
//插入
pair<Node*, bool> Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return make_pair(_root, true);
}
// 查找要插入的位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(cur, false);
}
}
cur = new Node(key, value);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return make_pair(cur, true);
}
2.3 查找
//查找
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
2.4删除
//删除
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 删除
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
else
{
// 找到右树最小节点去替代删除
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRight == minRightParent->_left)
minRightParent->_left = minRight->_right;
else
minRightParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
三 二叉搜索树的性能
1. 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有
n
个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树。
2.
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2N(以2为底,N的的对数)
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2;
四 完整代码
1 BinarySearchTree.h
#pragma once
#include<iostream>
using namespace std;
//二叉搜索树的KEY模型
namespace KEY
{
//树节点
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
//二叉搜索树
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
//BSTree() = default;
//构造函数
BSTree()
:_root(nullptr)
{}
//BSTree(const BSTree<K>& t)
//拷贝构造函数
BSTree(const BSTree& t)
{
_root = _Copy(t._root);
}
// t1 = t2
//赋值运算符重载
BSTree<K>& operator=(BSTree<K> t)
{
std::swap(_root, t._root);
return *this;
}
//析构函数
~BSTree()
{
_Destroy(_root);
}
//插入数据
bool Insert(const K& key)
{
//如果树为空,开一个节点,直接将数据插入
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
//不为空
//查找要插入的位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
//要插入的值比当前节点值大,就往右找
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
//要插入的值比当前节点值小,就往左找
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
//如果有相等的值,就不能插入,插入失败
else
{
return false;
}
}
//找到之后,开新节点插入数据,将节点连接起来
cur = new Node(key);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
//搜索存储在树中的数据
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
//要搜索的值比较大,就向右边找
if (cur->_key < key)
{
cur = cur->_right;
}
//要搜索的值比较小,就向左边找
else if (cur->_key > key)
{
cur = cur->_left;
}
//找到了,返回位置
else
{
return cur;
}
}
//没找到返回空
return nullptr;
}
//删除树中的数据
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
//找到后,开始删除
else
{
//情况一:需要删除的结点左子树为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//情况二:需要删除的结点右子树为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
情况三:需要删除的结点左右子树都不为空
else
{
// 找到右树最小节点去替代删除
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRight == minRightParent->_left)
minRightParent->_left = minRight->_right;
else
minRightParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
//中序遍历二叉搜索树(升序)
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//递归版本
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
Node* FindR(const K& key)
{
return _FindR(_root, key);
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
Node* _Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* newRoot = new Node(root->_key);
newRoot->_left = _Copy(root->_left);
newRoot->_right = _Copy(root->_right);
return newRoot;
}
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (root->_key < key)
{
return _EraseR(root->_right, key);
}
else if (root->_key > key)
{
return _EraseR(root->_left, key);
}
else
{
// 删除
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
// 替代法删除
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
root->_key = minRight->_key;
// 转换成递归在右子树种删除最小节点
return _EraseR(root->_right, minRight->_key);
}
delete del;
return true;
}
}
Node* _FindR(Node* root, const K& key)
{
if (root == nullptr)
return nullptr;
if (root->_key < key)
return _FindR(root->_right, key);
else if (root->_key > key)
return _FindR(root->_left, key);
else
return root;
}
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
else
{
if (root->_key < key)
{
return _InsertR(root->_right, key);
}
else if (root->_key > key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
//二叉搜索树的KEY_VALUE模型
namespace KEY_VALUE
{
//树结点
template<class K, class V>
struct BSTreeNode
{
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
: _left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
//树
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
V& operator[](const K& key)
{
pair<Node*, bool> ret = Insert(key, V());
return ret.first->_value;
}
//插入
pair<Node*, bool> Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return make_pair(_root, true);
}
// 查找要插入的位置
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(cur, false);
}
}
cur = new Node(key, value);
if (parent->_key < cur->_key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return make_pair(cur, true);
}
//查找
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//删除
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
// 删除
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
else
{
// 找到右树最小节点去替代删除
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRight == minRightParent->_left)
minRightParent->_left = minRight->_right;
else
minRightParent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
2 BinarySearchTree.cpp
#include "BinarySearchTree.h"
void TestBSTree1()
{
int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
KEY::BSTree<int> t;
for (auto e : a)
{
//t.Insert(e);
t.InsertR(e);
}
t.InOrder();
KEY::BSTree<int> copy = t;
copy.InOrder();
t.Erase(7);
t.InOrder();
t.Erase(5);
t.InOrder();
for (auto e : a)
{
//t.Erase(e);
t.EraseR(e);
t.InOrder();
}
}
void TestBSTree2()
{
KEY_VALUE::BSTree<string, string> dict;
dict.Insert("sort", "排序");
dict.Insert("insert", "插入");
dict.Insert("tree", "树");
dict.Insert("right", "右边");
// ...
string str;
while (cin >> str)
{
if (str == "Q")
{
break;
}
else
{
auto ret = dict.Find(str);
if (ret == nullptr)
{
cout << "拼写错误,没找到" << endl;
}
else
{
cout << ret->_key << "->" << ret->_value << endl;
}
}
}
}
void TestBSTree3()
{
// 统计字符串出现次数
string str[] = { "sort", "sort","insert", "tree", "sort", "insert","sort", "insert", "sort", "tree", "sort", "test", "sort" };
KEY_VALUE::BSTree<string, int> countTree;
for (auto& e : str)
{
countTree[e]++;
}
countTree.InOrder();
}
int main()
{
//TestBSTree1();
//TestBSTree2();
TestBSTree3();
return 0;
}