目录
前言
学习二叉搜索树是为了后面的map与set作铺垫有助于更好理解其特性~
一. 二叉搜索树实现
1.1 二叉搜索树概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
通俗来讲就是中序遍历是排好序的~
接下来我们就以该图进行二叉树的实现原理~
1.2 二叉搜索树的查找
二叉树的查找很简单,我们只需要利用二叉搜索树的特性即可:
我们要找的值比8小,则去左子树找(因为左子树都是比8小的)。比3大则去右子树找,比6大继续去右子树找。直到找到或为空为止~
非递归:
template<class K> struct BSTreeNode { typedef BSTreeNode<K> Node; Node* _left; Node* _right; K _key; BSTreeNode(const K& key) :_left(nullptr) , _right(nullptr) , _key(key) {} }; template<class K> class BSTree { typedef BSTreeNode<K> Node; public: //二叉树查找 bool Find(const K& key) { Node* cur = _root; while (cur) { if (key > cur->_key) { cur = cur->_right; } else if (key < cur->_key) { cur = cur->_left; } else { return true; } } return false; } private: Node* _root = nullptr; };
递归;
bool FindR(const K& key) { return _FindR(_root, key); } //寻找递归版本 bool _FindR(Node* root, const K& key) { if (root == nullptr) { return false; } if (key > root->_key) { return _FindR(root->_right, key); } else if (key < root->_key) { return _FindR(root->_left, key); } else { return true; } }
1.3 二叉搜索树的插入
插入我们只需要继续查找的套路就行了,只不过在最后是为空节点的时候插入~
非递归:
template<class K> struct BSTreeNode { typedef BSTreeNode<K> Node; Node* _left; Node* _right; K _key; BSTreeNode(const K& key) :_left(nullptr) , _right(nullptr) , _key(key) {} }; template<class K> class BSTree { typedef BSTreeNode<K> Node; public: //二叉树查找 bool Find(const K& key) { Node* cur = _root; while (cur) { if (key > cur->_key) { cur = cur->_right; } else if (key < cur->_key) { cur = cur->_left; } else { return true; } } return false; } //二叉树插入 bool Insert(const K& key) { if (_root == nullptr) { _root = new Node(key); return true; } Node* cur = _root; Node* parent = nullptr; while (cur) { if (key > cur->_key) { parent = cur; cur = cur->_right; } else if (key < cur->_key) { parent = cur; cur = cur->_left; } else { return false; } } //cur指向空节点时开始插入 cur = new Node(key); //如果父节点的左节点存在则插入其右节点 if (key>parent->_key) { parent->_right = cur; } if (key < parent->_key) { parent->_left = cur; } return true; } //中序遍历 void Inorder() { _Inorder(_root); cout << endl; } void _Inorder(Node* root) { if (root == nullptr) { return; } _Inorder(root->_left); cout << root->_key << " "; _Inorder(root->_right); } private: Node* _root = nullptr; };
ps:插入有几点细节需要注意
- 开始如果是空树需要特殊判断
- 最后cur找到空节点后还需要与父节点链接起来,所以记得记录cur的父节点
- 我们编写中序遍历时在类外是取不到_root的,所以需要套一层外壳~递归都需要套一层壳~
递归:
bool InsertR(const K& key) { return _InsertR(_root, key); } //插入递归版本 bool _InsertR(Node*& root, const K& key) { if (root == nullptr) { root = new Node(key); return true; } if (key > root->_key) { return _InsertR(root->_right, key); } else if (key < root->_key) { return _InsertR(root->_left, key); } else { return false; } }
ps:不用去担心插入后的节点链接问题~引用帮我们解决了一切,这也是引用在递归中很精妙的写法~
1.4 二叉搜索树的删除
非递归:
删除的情况很复杂,我们先对删除划分情况
- 要删除的结点无孩子结点或只有一个孩子节点
- 要删除的结点有左、右孩子结点
我们先来重点讨论第一种划分:
除了考虑寻常节点外,我们还得考虑删除根节点的情况~
最后再来讨论第二种划分:
这里解释一下,因为rightmin已经是最左节点,那么其左孩子一定是空节点,只要跟着这个线索就可以找到rightmin节点了~本质其实就是找到可以替换的节点,然后数据交换,最后删除节点~
至此所有情况全都说明完毕,其实只是在两种划分考虑正常节点的基础上再考虑根节点的情况而言
bool Erase(const K& key)
{
if (_root == nullptr)
{
return false;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//找到目标节点
else
{
//开始划分情况
//目标节点没有左节点,有右节点
if (cur->_left == nullptr)
{ //如果删除的是根节点
if (cur == _root)
{
cur = _root->_right;
}
//正常节点
else
{
//看删除节点是父节点的左还是右
if (cur == parent->_right)
{
//让父节点链接删除节点的右节点
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
return true;
}
//目标节点有左节点,没有右节点
else if (cur->_right == nullptr)
{
//如果删除的是根节点
if (cur == _root)
{
_root = _root->_left;
}
//正常节点
else
{
//看删除节点是父节点的左还是右
if (cur == parent->_right)
{
//让父节点链接删除节点的左节点
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
return true;
}
//如果目标节点有两个孩子节点
//替换法
else
{
Node* rightminparent = cur;
Node* rightmin = cur->_right;
while (rightmin->_left)
{
rightminparent = rightmin;
rightmin = rightmin->_left;
}
cur->_key = rightmin->_key;
if (rightmin == rightminparent->_left)
{
rightminparent->_left = rightmin->_right;
}
else
{
rightminparent->_right = rightmin->_right;
}
delete rightmin;
return true;
}
}
}
return false;
}
递归:
//删除递归版本
bool EraseR(const K& key)
{
return _EraseR(_root,key);
}
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->_right == nullptr)
{
root = root->_left;
}
else if (root->_left == nullptr)
{
root = root->_right;
}
else
{
Node* rightMin = root->_right;
while (rightMin->_left)
{
rightMin = rightMin->_left;
}
swap(root->_key, rightMin->_key);
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
这里同样用引用解决了删除节点后剩下的链接问题~
1.5 拷贝构造
// 写了拷贝构造就不会有默认构造了 强制生成默认构造
BSTree() = default;
//拷贝构造
BSTree(const BSTree<K>& t)
{
_root = Copy(t._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;
}
1.6 运算符=
//赋值 现代写法
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
1.7 析构函数
~BSTree()
{
Destroy(_root);
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
析构函数正常用后续遍历删除节点即可~
二.KV模型二叉树
我们上述实现的就是K模型二叉树,而kv模型二叉树就是多传了一个value值而已~
拿商场停车场作类比~
- 进入商场停车场(记录k值车牌号,记录v值停车时间)
- 离开商场停车场(查看k值车牌号,查看v值停车时间,根据时间收费)
这里我们用kv模型来实现英汉词典
英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;
namespace key_value
{
template<class K, class V>
struct BSTreeNode
{
typedef BSTreeNode<K, V> Node;
Node* _left;
Node* _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:
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->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (parent->_key < 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->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
return true;
}
else
{
// 替换法
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMin == rightMinParent->_left)
rightMinParent->_left = rightMin->_right;
else
rightMinParent->_right = rightMin->_right;
delete rightMin;
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 << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
其实kv模型也就是在插入的时候添加了value值而已,然后我们通过查找key值可以映射对应的value值。
三.全部代码
//BinarySearchTree.h
#pragma once
template<class K>
struct BSTreeNode
{
typedef BSTreeNode<K> Node;
Node* _left;
Node* _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(const BSTree<K>& t)
{
_root = Copy(t._root);
}
//赋值 现代写法
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destroy(_root);
}
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 FindR(const K& key)
{
return _FindR(_root, key);
}
//寻找递归版本
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (key > root->_key)
{
return _FindR(root->_right, key);
}
else if (key < root->_key)
{
return _FindR(root->_left, key);
}
else
{
return true;
}
}
//二叉树查找
/*bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
cur = cur->_right;
}
else if (key < cur->_key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}*/
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
//插入递归版本
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (key > root->_key)
{
return _InsertR(root->_right, key);
}
else if (key < root->_key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
//二叉树插入
//bool Insert(const K& key)
//{
// if (_root == nullptr)
// {
// _root = new Node(key);
// return true;
// }
// Node* cur = _root;
// Node* parent = nullptr;
// while (cur)
// {
// if (key > cur->_key)
// {
// parent = cur;
// cur = cur->_right;
// }
// else if (key < cur->_key)
// {
// parent = cur;
// cur = cur->_left;
// }
// else
// {
// return false;
// }
// }
// //cur指向空节点时开始插入
// cur = new Node(key);
// //如果父节点的左节点存在则插入其右节点
// if (key>parent->_key)
// {
// parent->_right = cur;
// }
// if (key < parent->_key)
// {
// parent->_left = cur;
// }
// return true;
//}
//中序遍历
void Inorder()
{
_Inorder(_root);
cout << endl;
}
//中序遍历
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_key << " ";
_Inorder(root->_right);
}
//删除递归版本
bool EraseR(const K& key)
{
return _EraseR(_root,key);
}
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->_right == nullptr)
{
root = root->_left;
}
else if (root->_left == nullptr)
{
root = root->_right;
}
else
{
Node* rightMin = root->_right;
while (rightMin->_left)
{
rightMin = rightMin->_left;
}
swap(root->_key, rightMin->_key);
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
//bool Erase(const K& key)
//{
// if (_root == nullptr)
// {
// return false;
// }
// Node* parent = nullptr;
// Node* cur = _root;
// while (cur)
// {
// if (key > cur->_key)
// {
// parent = cur;
// cur = cur->_right;
// }
// else if (key < cur->_key)
// {
// parent = cur;
// cur = cur->_left;
// }
// //找到目标节点
// else
// {
// //开始划分情况
// //目标节点没有左节点,有右节点
// if (cur->_left == nullptr)
// { //如果删除的是根节点
// if (cur == _root)
// {
// cur = _root->_right;
// }
// //正常节点
// else
// {
// //看删除节点是父节点的左还是右
// if (cur == parent->_right)
// {
// //让父节点链接删除节点的右节点
// parent->_right = cur->_right;
// }
// else
// {
// parent->_left = cur->_right;
// }
// }
// delete cur;
// return true;
// }
// //目标节点有左节点,没有右节点
// else if (cur->_right == nullptr)
// {
// //如果删除的是根节点
// if (cur == _root)
// {
// _root = _root->_left;
// }
// //正常节点
// else
// {
// //看删除节点是父节点的左还是右
// if (cur == parent->_right)
// {
// //让父节点链接删除节点的左节点
// parent->_right = cur->_left;
// }
// else
// {
// parent->_left = cur->_left;
// }
// }
// delete cur;
// return true;
// }
// //如果目标节点有两个孩子节点
// //替换法
// else
// {
// Node* rightminparent = cur;
// Node* rightmin = cur->_right;
// while (rightmin->_left)
// {
// rightminparent = rightmin;
// rightmin = rightmin->_left;
// }
// cur->_key = rightmin->_key;
// if (rightmin == rightminparent->_left)
// {
// rightminparent->_left = rightmin->_right;
// }
// else
// {
// rightminparent->_right = rightmin->_right;
// }
// delete rightmin;
// return true;
// }
//
// }
// }
// return false;
//}
private:
Node* _root = nullptr;
};
namespace key_value
{
template<class K, class V>
struct BSTreeNode
{
typedef BSTreeNode<K, V> Node;
Node* _left;
Node* _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:
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->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (parent->_key < 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->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
return true;
}
else
{
// 替换法
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMin == rightMinParent->_left)
rightMinParent->_left = rightMin->_right;
else
rightMinParent->_right = rightMin->_right;
delete rightMin;
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 << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include "BinarySearchTree.h"
//int main()
//{
// /*BSTree<int> t;
// int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//
// for (auto e : a)
// {
// t.InsertR(e);
// }
// t.Inorder();
//
//
// BSTree<int> t1;
// t1 = t;
// t1.Inorder();
// t1.Inorder();*/
//
// /*BSTree<int> t1(t);
// t1.Inorder();*/
// /*for (auto e : a)
// {
// t.EraseR(e);
// t.Inorder();
// }*/
//
// //BSTree<int> t;
//
// /*t.Inorder();
// BSTree<int> t;
//
//
// cout << t.Find(8) << endl;
// cout << t.Find(14) << endl;
// cout << t.Find(15) << endl;*/
//
//
// key_value::BSTree<string,string> t;
//
//
//
//
// return 0;
//
//}
int main()
{
key_value::BSTree<string, string> dict;
dict.Insert("apple", "苹果");
dict.Insert("keep", "坚持");
dict.Insert("good", "好棒");
dict.Insert("sleep", "睡觉");
string str;
while (cin >> str)
{
auto ret = dict.Find(str);
if (ret)
{
cout << ret->_value << endl;
}
else
{
cout << "找不到" << endl;
}
}
return 0;
}