Cpp 二叉树
二叉搜索树
左边子树的节点小于根节点右边子树的节点大于跟节点
方便 搜索树的节点
搜索二叉树 也叫做 排序二叉树
代码实现
#pragma once
#include <iostream>
using namespace std;
namespace chen
{
template<class K>
struct BTreeNode{ //binary Search Tree
BTreeNode(const K& val) {
_val = val;
_left = nullptr;
_right = nullptr;
}
BTreeNode<K> *_left;
BTreeNode<K> *_right;
K _val;
};
template <class K>
class BSTree{
typedef BTreeNode<K> Node;
public:
bool Insert(const K& key)
{
if(_root == nullptr)
{
_root = new Node(key);
return true;
}
Node * Cur = _root;
Node * Parents;
while(Cur != nullptr)
{
Parents = Cur;
if(Cur ->_val < key)
{
Cur = Cur-> _right;
}
else if(Cur -> _val > key)
{
Cur = Cur->_left;
}
else return false;
}
Node *Curd = new Node(key); // 找到空节点
if(Parents->_val > key)
{
Parents->_left = Curd;
}else{
Parents->_right = Curd;
}
return true;
}
void InOrder(){
_InOrder(_root);
cout << endl;
}
void _InOrder(Node *root)
{
if(root == nullptr)return;
_InOrder(root->_left);
cout << root->_val << " ";
_InOrder(root->_right);
}
bool Find(const K& val){ // 虽然设计使用递归也可以实现 但是能用循环就用循环
Node *temp = _root;
while(temp){
if(temp->_val == val){
return true;
}
else if(temp->_val < val)
{
temp = temp->_right;
}else{
temp = temp->_left;
}
}
return false;
}
bool Erase(const K& val)
{
if(_root == nullptr)return false;
Node * Cur = _root; // 要寻找的节点
Node * Parent = _root; // 节点的父节点
while(Cur)
{
if(val == Cur->_val){ // 此时Cur 为要删除的节点
break;
}else if(val < Cur->_val)
{
Parent = Cur; // 走之前记录一下父节点
Cur = Cur->_left;
}else{
Parent = Cur; // 走之前记录一下父节点
Cur = Cur->_right;
}
}
if(Cur == nullptr)return false; // 没有找到返回 False
// 此时应已经找到要删除的节点
if(Cur->_left == nullptr){ //这边要分情况 字节点在左右的情况
// 子树左字树为null
if(Parent->_right == Cur)
Parent->_right = Cur->_right;
else
Parent->_left = Cur -> _right;
delete Cur;
}else if(Cur->_right == nullptr){ //这边要分情况 字节点在左右的情况
// 子树右字树为null
if(Parent->_right == Cur)
Parent->_right = Cur->_left;
else
Parent->_left = Cur -> _left;
delete Cur;
}else{
// 子树两边子树都不为空 imp
Node * RightMinParent = Cur; //此时 Cur 为 要删除的节点
Node * RightMin = Cur->_right; // 找到Cur右边最小的节点
while(RightMin->_left)
{
RightMinParent = RightMin;
RightMin = RightMin->_left;
}
// 交换值
Cur->_val = RightMin->_val;
if( RightMinParent->_left == RightMin)
{ // 父节点的左边 为 RightMin
// RightMin 为 左为空 让父亲指向其右
RightMinParent->_left = RightMin->_right;
}else
{
//
RightMinParent ->_right = RightMin->_right;
}
delete RightMin;
}
return true;
}
private:
Node *_root = nullptr;
};
void testTree()
{
BSTree<int> root;
int arr[] = {5,3,4,1,7,3,2,2};
for(auto i:arr)
{
// cout << (int )root.Insert(i) <<" ";
root.Insert(i) ;
}
root.InOrder();
// root.Erase(1);
root.Erase(7);
root.Erase(1);
root.InOrder();
}
}
二叉树的逻辑模型
K模型 只有Key作为关键码,结构中只需要存储Key 即可,关键码即为搜索到的值. K 就为值 上面的代码为K 模型 使用场景:判断在不在
KV 模型: 每一个关键码Key 都有与之对应的Value 即<Key , Value>的键值对 这种方法在现实生活中非常常见 使用场景 通过Key 寻找对应的Value
在K 模型上进行修改为K/V 模型
#pragma once
#include <iostream>
using namespace std;
namespace chen
{
template<class K,class Value>
struct BTreeNode{ //binary Search Tree
BTreeNode(const K& Key,const Value& val) {
_val = val;
_left = nullptr;
_right = nullptr;
_Key = Key;
}
BTreeNode<K,Value> *_left;
BTreeNode<K,Value> *_right;
Value _val;
K _Key;
};
template <class K,class Value>
class BSTree{
typedef BTreeNode<K,Value> Node;
public:
bool Insert(const K& key,const Value& value )
{
if(_root == nullptr)
{
_root = new Node(key,value);
return true;
}
Node * Cur = _root;
Node * Parents = nullptr;
while(Cur != nullptr)
{
Parents = Cur;
if(Cur ->_Key< key)
{
Cur = Cur-> _right;
}
else if(Cur -> _Key > key)
{
Cur = Cur->_left;
}
else return false;
}
Node *Curd = new Node(key,value); // 找到空节点
if(Parents->_Key > key)
{
Parents->_left = Curd;
}else{
Parents->_right = Curd;
}
return true;
}
void InOrder(){
_InOrder(_root);
cout << endl;
}
void _InOrder(Node *root)
{
if(root == nullptr)return;
_InOrder(root->_left);
cout << root->_Key << " " << root->_val << endl;
_InOrder(root->_right);
}
Value* Find(const K& Key){ // 虽然设计使用递归也可以实现 但是能用循环就用循环
Node *temp = _root;
while(temp){
if(temp->_Key == Key){
return temp;
}
else if(temp->_Key < Key)
{
temp = temp->_right;
}else{
temp = temp->_left;
}
}
return nullptr;
}
// 删除节点
bool Erase(const K& key){
if(_root == nullptr) return false; // 根为空
if(_root->_left == nullptr && _root->_right == nullptr&&key == _root->_val)
//删除1个节点并且为根 情况4
{
_root = nullptr;
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{
break;
}
}
// 没有找到节点
if(Cur == nullptr){return false;}
//处理删除的节点
// 情况1 : 左子树为空
if(Cur->_left == nullptr){
if(Parent->_left == Cur)
{
Parent->_left = Cur->_right;
} else{
Parent->_right = Cur->_right;
}
delete Cur;
}
// 情况2: 右子树为空
else if(Cur->_right == nullptr)
{
if(Parent->_left == Cur)
{
Parent->_left = Cur->_left;
} else{
Parent->_right = Cur->_left;
}
delete Cur;
}
// 情况3: 左右子树都不为空
else
{
Node* rightMinParent = Cur;
Node* rightMin = Cur->_right;
while(rightMin->_left) // 找到最左边
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
Cur->_Key = rightMin->_Key;
Cur->_val = rightMin->_val;
// 删除右边子树的最小节点
if(rightMinParent->_left == rightMin){
rightMinParent->_left = rightMin->_right;
}else{
rightMinParent->_right= rightMin->_right;
}
delete rightMin;
}
return true;
}
private:
Node *_root = nullptr;
};
void testTree() {
// BSTree<int, string> root;
BSTree<int,int> root;
int keys[] = {5, 3, 4, 1, 7, 6, 8, 2};
// string values[] = {"five", "three", "four", "one", "seven", "six", "eight", "two"};
for (int i = 0; i < 8; i++) {
root.Insert(keys[i], keys[i]);
}
// root.Insert(7,1);
root.InOrde r();
root.Erase(5);
root.Erase(1);
root.InOrder();
}
}
oid testTree() {
BSTree<int, string> root;
// BSTree<int,int> root;
int keys[] = {5, 3, 4, 1, 7, 6, 8, 2};
string values[] = {"five", "three", "four", "one", "seven", "six", "eight", "two"};
for (int i = 0; i < 8; i++) {
root.Insert(keys[i], values[i]);
}
// root.Insert(7,1);
root.InOrder();
root.Erase(5);
root.Erase(1);
root.InOrder();
}
/home/chen/CLionProjects/untitled/cmake-build-debug/untitled 1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 eight
2 two 3 three 4 four 6 six 7 seven 8 eight
Process finished with exit code 0
二叉树的性能分析
搜索效率 :
插入数据是有序的 搜索树的效率完全没有办法被保障
所以搜索树的效率为 (O(N))
如何解决 ? 平衡树
AVL 树
红黑树
搜索🌲中的key 是不允许修改的
只能修改 key
Map _ Set
容器:
序列式容器: vector / list / string / deque
关联式容器: map / set / unordered_map / unordered_set
map / multimp /set / multiset
其都是按照二叉搜索树的搜索顺序来实现的 假如往树中插入的顺序是按照二叉搜索树来实现的,但是二叉树都有自身的缺陷,加入往树中插入的元素有序或者接近有序就会退化成 O(n),因此 map set 等关联式容器对二叉树进行平衡处理
AVLTree 高度平衡二叉搜索树
二叉搜索树
二叉搜索树的左子树和右子树的高度差不大于1
实现 使用平衡因子 (平衡因子 = 左子树高度 - 右子树的高度) 将高度控制在 O(logn)
AVL树的插入
AVL 树就是在二叉搜索树的基础上引入了平衡因子,因此AVL 树也是二叉搜索树 .
AVL 树的插入分为两步
按照二叉搜索树的方式插入新节点
调整节点的平衡因子
当插入节点后会破坏AVL树的平衡性 需要对树进行旋转
AVL树的旋转
AVL 中 旋转部分是最难以理解的
我细分成 四个部分 (。♥‿♥。)
主要是像是一个臣篡位
安排小人
将自己左边/右边子树 安排在皇帝 右/左边
如果有小人 小人的上司就是 皇帝
起义
让皇帝成为臣子
自己代替他的位置
黄袍加身
如果皇帝上面没老人 直接登基
如果左/右 有 同化为自己的老人
让旧皇帝服从新皇帝
制定法则
从0开始接手旧皇的bf
实现AVL🌲的代码
#pragma once
#include <iostream>
using namespace std;
namespace chen{
template< class K,class V>
struct avlTree {
// 构造函数
avlTree(const pair<K,V>& val)
:_parent(nullptr),_left(nullptr),_right(nullptr),_bf(0),_Data(val)
{}
avlTree<K,V> * _parent;
avlTree<K,V> * _left;
avlTree<K,V> * _right;
pair<K,V> _Data;
int _bf;
};
template<class K,class V>
class aAVLTree{
typedef avlTree<K,V> Node;
public:
bool insert(const pair<K,V> & data)
{
// root is null
if(!_root)
{
_root = new Node(data);
return true;
}
Node * Cur = _root;
Node * parent = nullptr;
while(Cur)
{
if(Cur->_Data.first < data.first)
{
parent = Cur;
Cur = Cur->_right;
}else if(Cur->_Data.first > data.first)
{
parent = Cur;
Cur = Cur->_left;
}else{
// 找到重复项
return false;
}
}
// 此时找到 Cur 是 新节点要插入的位置
Cur = new Node(data);
Cur->_parent = parent;
// 定Cur 位置
if(data.first > parent->_Data.first)
{
parent->_right = Cur;
}else{
parent->_left = Cur;
}
// 更新平衡因子
while(parent){
if (Cur == parent->_left)
{
parent->_bf --;
}else{
parent->_bf ++;
}
if(parent->_bf == 0) // 平衡因子为0 // 平衡状态不需要调整
{
break;
}
else if(parent->_bf == 1||parent->_bf == -1)
{
Cur = parent;
parent = parent->_parent; // 向上回溯
}
else{
// 需要进行旋转操作 恢复平衡
if(parent->_bf == 2)
{
if(Cur->_bf == 1)
{
rotateL(parent);
}else{
rotateRL(parent);
}
}else if(parent->_bf == -2){
if(Cur->_bf == -1)
{
rotateR(parent);
}else{
rotateLR(parent);
}
}else
break;
}
}
return true;
}
void InOrder()
{
_InOrder(_root);
}
int TreeHight()
{
return _Height(_root);
}
~aAVLTree() {
_Destroy(_root);
}
private:
void _InOrder(Node * root)
{
if(!root)return ;
_InOrder(root->_left);
cout << root->_Data.first <<" : " << root->_Data.second <<endl;
_InOrder(root->_right);
}
// 此为左旋
void rotateL(Node* parent)
{
cout <<"--L--" << endl;
Node * subR = parent->_right;
// 开始旋转
// 安排小人在皇帝身边
parent->_right = subR->_left;
if(subR->_left){ // subL 不为空
subR->_left->_parent = parent;
}
// 起义
subR->_left = parent;
subR->_parent = parent->_parent;
// 黄袍加身
if(parent->_parent == nullptr){
_root = subR;
}else if(parent->_parent->_left == parent)
{
parent->_parent->_left = subR;
}else {
parent->_parent->_right = subR;
}
parent->_parent = subR;
// 制定法则
parent->_bf = subR->_bf = 0;
}
void rotateR(Node * parent)
{
Node * subL = parent->_left;
cout << "--R--"<<endl;
// 安排小人
parent->_left = subL->_right;
if(subL->_right) {
subL->_right->_parent = parent;
}
//起义
subL->_right = parent;
subL->_parent = parent->_parent;
// 黄袍加身
if(parent->_parent == nullptr) {
_root = subL;
}else if(parent->_parent->_left == parent){
parent->_parent->_left = subL;
}else {
parent->_parent->_right = subL;
}
parent->_parent = subL;
//制定法则
parent->_bf = subL->_bf = 0;
}
void rotateLR(Node * parent)
{
cout <<"--------" << endl;
rotateL(parent->_left);
rotateR(parent);
cout <<"--------" << endl;
}
void rotateRL(Node * parent)
{
cout <<"--------" << endl;
rotateR(parent->_right);
rotateL(parent);
cout <<"--------" << endl;
}
int _Height(Node* root) {
if (!root) return 0;
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
void _Destroy(Node* root) {
if (root) {
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
}
Node * _root = nullptr;
};
void testAVLTree() {
aAVLTree<int, int> tree;
tree.insert({1, 1});
tree.insert({2, 2});
tree.insert({3, 3});
tree.insert({4, 4});
tree.insert({5, 5});
tree.insert({6, 6});
tree.insert({-1, -1});
tree.insert({-2, -2});
tree.insert({-3, -3});
tree.insert({0, 0});
tree.InOrder();
cout << "树的高度是: " << tree.TreeHight() << endl;
}
};
## AVL 树的删除
AVL 树的删除实际上和插入后的逻辑差不多
右边插入 ,父亲的平衡因子++ 左边插入父亲的平衡因子--
右边删除,父亲的平衡因子--,左边删除父亲的平衡因子++
插入后 父亲 的平衡因子变成0 说明父亲所在的高度不变,更新结束
删除后,父亲 的平衡因子变成0 说明父亲所在的树高度变了,继续往上更新
插入后 父亲 的平衡因子变成1/-1说明父亲所在的高度变了 继续往上更新
删除后 父亲的平衡因子变成1/-1 说明父亲所在的树高度不变,更新 结束
插入 删除后 父亲的平衡因子 变成 2/-2 说明不平衡 旋转处理
bool Earse(const K& key)
{
Node * Cur = _root;
Node * parent = nullptr; // 要删除节点的父节点
while(Cur)
{
parent = Cur;
if(Cur->_Data.first == key)
{
break;
}else if(Cur->_Data.first < key){
Cur = Cur->_left;
}else{
Cur = Cur->_right;
}
}
if(Cur == nullptr) return false;
Node * delNode = Cur; // 记录要删除的节点
//分明三种情况
if(Cur->_right && Cur->_left){
Node * rep = Cur->_right;
//两个 取出右树最小的节点进行替换
while(rep->_left)
{
rep = rep->_left;
}
Cur->_Data = rep->_Data;
delNode = rep;
parent = delNode->_parent;
Cur = delNode->_right;
}else{
if(Cur->_left == nullptr){
Cur = Cur->_right;
}else{
Cur = Cur->_left;
}
}
if(parent){
if(parent->_left == delNode) {
parent->_left = Cur;
if(Cur)Cur ->_parent = parent;
parent->_bf++;
}else {
parent->_right = Cur;
if(Cur)Cur->_parent = parent;
parent->_bf --;
}
}else
{
_root = Cur;
if(Cur)Cur->_parent = nullptr;
}
delete delNode;
// 向上 回溯
while(parent) {
if(parent->_bf == 2) {
Node * pR = parent->_right;
if(pR->_bf >= 0) {
rotateL(parent);
if(pR->_bf == 0)break;
}else {
rotateRL(parent);
}
}else if(parent->_bf == -2) {
Node * pL = parent->_left;
if(pL->_bf >= 0) {
rotateR(parent);
if(pL->_bf == 0)break;
}else {
rotateLR(parent);
}
}else if(parent->_bf == 0) {
Cur = parent;
parent = parent->_parent;
}
else break; // 平衡因子 高 为 1/ -1 高度没变化
}
return true;
}
补充函数 find 和 二叉搜索树的逻辑差不多
Node * find(const V& value) { Node * Cur = _root; while(Cur) { if(Cur->_Data.first == value) { return Cur; }else if(Cur->_Data.first < value){ Cur = Cur->_left; }else{ Cur = Cur->_right; } } if(Cur == nullptr) return nullptr; }