STL的容器大致分为两类,序列式容器和关联式容器,两者直接区分通过数据在容器中的排列来区分。
关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器内部存储的是键值对,数据检索时效率比序列式容器高。
元素的获取方式:
序列式容器:通过访问元素位置获取元素
关联式容器:通过key值访问读取元素。
标准STL序列式容器:vector,list,heap(算法),queue(适配器),stack(适配器),priority_queue(适配器)
标准STL关联式容器:map,multimap,set,multiset
键值对:键值对表示的是一种一一对应的结构,其中只包含两个成员变量,key,value;key为键值,value为对应key的信息;
树形关联式容器
STL根据场景不同设计了两种结构的关联式容器,树形结构和哈希结构。
树形结构的容器主要有四种,map,set,multimap.multiset
树形结构底层实现为平衡二叉搜索树(/红黑树)
哈希结构底层实现为哈希表(开散列/闭散列)
底层实现
BS_Tree
二叉搜索树(二叉排序树):
- 若左子树不为空,则左子树上的所有元素都小于根节点的值
- 若右子树不为空,则右子树上的所有元素都大于根节点的值
- 二叉搜索树的左右子树也为二叉搜索树
//模拟实现二叉搜索树
#include<iostream>
using namespace std;
template<class T>
//BSTNode 结点
struct BSTNode{
BSTNode<T>* _left;
BSTNode<T>* _right;
T _val;
BSTNode(const T& val = T())
:_left(nullptr)
,_right(nullptr)
,_val(val)
{}
};
template<class T>
class BSTree{
public:
typedef BSTNode<T> Node;
typedef BSTNode<T>* pNode;
BSTree()
:_root(nullptr)
{}
BSTree(const BSTree<T>& bst) //拷贝构造
{
_root = Copy(bst._root);
}
~BSTree() //析构函数
{
destory(_root);
}
BSTree<T>& operator=(const BSTree<T>& bst) //重载赋值运算符= //先清空原有的,再拷贝
{
if(_root != bst._root) //this != &bst 也可以
{
if(_root)
{
destory(_root);
}
_root = Copy(bst._root);
}
return *this;
}
pNode Copy(pNode root)
{
if(root == nullptr)
return root;
//按照先序遍历顺序进行拷贝
pNode newNode = new Node(root->_val);
newNode->_left = Copy(root->_left);
newNode->_right = Copy(root->_right);
return newNode;
}
void destory(pNode root)
{
if(root== nullptr)
return ;
//按照后序遍历的顺序进行删除
destory(root->_left);
destory(root->_right);
delete root;
root = nullptr;
}
//查找指定元素
pNode Find(const T& val)
{
if(_root == nullptr)
{
return nullptr;
}
pNode cur = _root;
while(cur)
{
if(cur->_val == val)
{
return cur;
}else if(cur->_val > val)
{
cur = cur->_left;
}else
{
cur = cur->_right;
}
}
return cur;
}
//插入指定元素
//保证不重复 查找重复则不插入
//找到插入位置
bool Insert(const T& val)
{
if(_root == nullptr)
{
_root = new Node(val);
return true;
}
pNode cur = _root;
pNode parent = nullptr;
while(cur)
{
if(cur->_val == val)
{
return false;
}
parent = cur;
if(cur->_val > val)
{
cur = cur->_left;
}else
{
cur = cur->_right;
}
}
pNode tmp = new Node(val);
//判断插入位置
if(parent->_val > val)
{
parent->_left = tmp;
}else
{
parent->_right = tmp;
}
return true;
}
//删除指定元素
//查找是否存在
//1.叶子结点
//2.左结点为空
//3.右结点为空
//4.左右结点都不为空
bool Erase(const T& val)
{
pNode cur = _root;
pNode parent = nullptr;
while(cur)
{
if(cur->_val == val)
{
break;
}
parent = cur;
if(cur->_val > val)
{
cur = cur->_left;
}else
{
cur = cur->_right;
}
}
if(cur->_left == nullptr && cur->_right == nullptr) //左右子树都为空
{
if(cur != _root)
{
if(parent->_left == cur)
{
parent->_left = nullptr;
}else
{
parent->_right = nullptr;
}
}else
{
_root = nullptr;
}
delete cur;
cur = nullptr;
}else if(cur->_left == nullptr) //左子树为空,将右子树挂在parent上
{
if(cur != _root)
{
if(parent->_left == cur)
{
parent->_left = cur->_right;
}else
{
parent->_right = cur->_right;
}
delete cur;
cur = nullptr;
}else
{
_root = cur->_right;
delete cur;
}
}else if(cur->_right == nullptr) //右子树为空,将左子树挂在parent上
{
if(cur != _root)
{
if(parent->_left == cur)
{
parent->_left = cur->_left;
}else
{
parent->_right = cur->_left;
}
delete cur;
}else
{
_root = cur->_left;
delete cur;
cur = nullptr;
}
}else //左右子树都不为空
{
//先找到左子树最大结点(左子树最右)/右子树最小结点(右子树最左)
//进行交换
//删除左子树最大/右子树最小结点
pNode next = cur->_left; //左子树最大结点
parent = cur;
while(next->_right != nullptr) //获取左子树最大结点
{
parent = next;
next = next->_right;
}
cur->_val = next->_val;
//无论next是否有右子树
if (parent->_left == next)
parent->_left = next->_left;
else
parent->_right = next->_left;
delete next;
next = nullptr;
}
return true;
}
void Inorder() //中序遍历
{
pNode root = _root;
_Inorder(root);
cout<<endl;
}
private:
pNode _root;
void _Inorder(pNode root)
{
if(root == nullptr)
return ;
_Inorder(root->_left);
cout<<root->_val<<" ";
_Inorder(root->_right);
}
};
二叉搜索树的查找,删除,插入的平均时间复杂度为O(logn)
当而插入搜索树插入数据接近有序时,会造成单链表,使得性能下降为O(n)。
平衡二叉搜索树:一种特殊的二叉搜索树,其对于深度平衡有条件,根据条件不同,性能有所不同。常见的平衡二叉搜索树有:AVL_Tree,RB_Tree
AVL_Tree
模拟实现AVL_Tree
/*
AVL树:左右子树高度相差小于等于1
二叉平衡搜索树
*/
#include<iostream>
#include<stdlib.h>
using namespace std;
template<class T>
struct AVLTNode{
AVLTNode(const T& data)
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _bf(0)
{}
AVLTNode<T>* _pLeft;
AVLTNode<T>* _pRight;
AVLTNode<T>* _pParent;
T _data;
int _bf; //平衡因子 = 右子树高度 - 左子树高度
};
template<class T>
class AVLTree{
public:
typedef AVLTNode<T> Node;
typedef Node* pNode;
AVLTree()
:_pRoot(nullptr)
{}
bool Find(const T& data)
{
pNode cur = _pRoot;
while (cur)
{
if (cur->_data == data)
return true;
else if (cur->_data > data)
{
cur = cur->_pLeft;
}
else
{
cur = cur->_pRight;
}
}
return false;
}
//1.按照二叉搜索树顺序将节点插入AVL树中
//2.检查平衡性(平衡因子),旋转调整
bool Insert(const T& data)
{
if (_pRoot == nullptr)
{
_pRoot = new Node(data);
return true;
}
pNode cur = _pRoot;
pNode parent = nullptr;
//检查重复
while (cur)
{
if (cur->_data == data)
return false;
parent = cur;
if (cur->_data > data)
{
cur = cur->_pLeft;
}
else
{
cur = cur->_pRight;
}
}
cur = new Node(data);
if (parent->_data > data)
{
parent->_pLeft = cur;
cur->_pParent = parent;
}
else
{
parent->_pRight = cur;
cur->_pParent = parent;
}
//检查平衡性调整
while (parent)
{
//更新双亲的平衡因子
if (parent->_pLeft == cur)
{
--parent->_bf;
}
else
{
++parent->_bf;
}
//检测平衡因子
if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
//继续更新向上结点
cur = parent;
parent = parent->_pParent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
if (parent->_bf == 2 && cur->_bf == 1) //单左旋
{
RotateLeft(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1) //单右旋
{
RotateRight(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) //先左旋再右旋
{
pNode subL = parent->_pLeft;
pNode subLR = subL->_pRight;
int bf = subLR->_bf;
RotateLeft(cur);
RotateRight(parent);
//修改平衡因子
if(bf == 1)
{
parent->_bf = 0;
subL->_bf = -1;
}else if(bf == -1)
{
parent->_bf = 1;
subL->_bf = 0;
}
}
else if (parent->_bf == 2 && cur->_bf == -1) //先右旋再左旋
{
pNode subR = parent->_pRight;
pNode subRL = subR->_pLeft;
int bf = subRL->_bf;
RotateRight(cur);
RotateLeft(parent);
//修改平衡因子
if(bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
}else if(bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
}
}
break;
}
}
return true;
}
//中序遍历
void Inorder()
{
if (_pRoot == nullptr)
return;
_Inorder(_pRoot);
cout << endl;
}
//验证是否为平衡树
bool IsBalance()
{
return _IsBalance(_pRoot);
}
private:
void _Inorder(pNode root)
{
if (root == nullptr)
return;
_Inorder(root->_pLeft);
cout << root->_data << " ";
_Inorder(root->_pRight);
}
int Height(pNode root)
{
if(root == nullptr)
return 0;
int leftH = Height(root->_pLeft);
int rightH = Height(root->_pRight);
return leftH>rightH? leftH+1 : rightH+1;
}
bool _IsBalance(pNode root)
{
if(root == nullptr)
return true;
int leftH = Height(root->_pLeft);
int rightH = Height(root->_pRight);
if(root->_bf != rightH - leftH)
{
root->_bf = rightH - leftH;
cout<<root->_data<<": 平衡结点数值错误"<<endl;
return false;
}
return true;
}
//旋转
//1.改变链接
//2。更新平衡因子
void RotateLeft(pNode root) //左旋
{
pNode subR = root->_pRight;
pNode subRL = subR->_pLeft;
//改变链接
subR->_pLeft = root;
root->_pRight = subRL;
if (subRL != nullptr)
{
subRL->_pParent = root;
}
if (root != _pRoot)
{
pNode gParent = root->_pParent;
if (gParent->_pLeft == root)
{
gParent->_pLeft = subR;
}
else
{
gParent->_pRight = subR;
}
subR->_pParent = gParent;
}
else
{
_pRoot = subR;
subR->_pParent = nullptr;
}
root->_pParent = subR;
//更新平衡因子
subR->_bf = root->_bf = 0;
}
void RotateRight(pNode root) //右旋
{
pNode subL = root->_pLeft;
pNode subLR = subL->_pRight;
//改变链接
subL->_pRight = root;
root->_pLeft = subLR;
if (subLR != nullptr)
{
subLR->_pParent = root;
}
if (root != _pRoot)
{
if (root->_pParent->_pLeft == root)
{
root->_pParent->_pLeft = subL;
}
else
{
root->_pParent->_pRight = subL;
}
subL->_pParent = root->_pParent;
}
else
{
_pRoot = subL;
subL->_pParent = nullptr;
}
root->_pParent = subL;
//更新平衡因子
subL->_bf = root->_bf = 0;
}
pNode _pRoot;
};
AVL_Tree中引用平衡因子来判断左右子树的高度差是否超过1,若超过1,根据相应情况进行调整
平衡因子为右子树高度减去左子树高度.
关于平衡因子问题,对于右左双旋和左右双旋时的平衡因子更新问题:(画的,嗯。。就看看)
RB_Tree
模拟实现RB_Tree
/*
红黑树:
接近平衡的二叉搜索树
规则:
根节点为黑色
叶节点为黑色
红色节点下必须为黑色结点
每条路径下黑色结点个数相同(保证了最长路径不超过最短路径的两倍)
默认插入结点为颜色红色,保证路径下黑色结点数目相同
*/
#include<iostream>
using namespace std;
enum Color{ RED, BLACK };
template<class T>
struct RBTreeNode{
RBTreeNode(const T& data = T())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _color(RED)
{}
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Color _color;
};
template<class T>
class RBTree{
public:
typedef RBTreeNode<T> Node;
typedef RBTreeNode<T>* pNode;
RBTree()
:_head(new Node())
{
_head->_parent = nullptr;
_head->_left = _head;
_head->_right = _head;
}
pNode& GetRoot()
{
return _head->_parent;
}
/*
首先保证红黑树中无重复元素
插入结点至合适位置,判断是否满足条件,进行相关调整
*/
bool find(const T& data)
{
pNode cur = _head->_parent;
while (cur)
{
if (cur->_data > data)
{
cur = cur->_left;
}
else if (cur->_data < data)
{
cur = cur->_right;
}
else
{
return false;
}
}
return true;
}
bool Insert(const T& data)
{
//如果为空树
if (_head->_parent == nullptr)
{
pNode root = new Node(data);
root->_parent = _head;
_head->_parent = root;
_head->_left = root;
_head->_right = root;
root->_color = BLACK;
return true;
}
//检查是否有重复结点
pNode cur = _head->_parent;
pNode parent = nullptr;
while (cur)
{
parent = cur;
if (cur->_data == data)
{
return false;
}
else if (cur->_data > data)
{
cur = cur->_left;
}
else
{
cur = cur->_right;
}
}
cur = new Node(data);
cur->_parent = parent;
if (parent->_data > data)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
//修改颜色旋转
//若cur为root结点则会影响边_head->left 和 _head->_right 的颜色
while (parent->_color == RED && cur != GetRoot())
{
pNode gParent = parent->_parent;
if (parent == gParent->_left) //左侧
{
pNode uncle = gParent->_right;
if (uncle != nullptr && uncle->_color == RED) //第一种情况 uncle 和 parent的颜色均为红色
{
uncle->_color = BLACK;
parent->_color = BLACK;
gParent->_color = RED;
cur = gParent;
parent = gParent->_parent;
}
else //uncle节点不存在或为黑色
{
if (cur == parent->_right) //先左旋再右旋 //第三种
{
RotateLeft(parent);
//旋转后parent 和 cur 的位置互换
swap(parent, cur);
}
//右旋 //第二种
gParent->_color = RED;
parent->_color = BLACK;
RotateRight(gParent);
break;
}
}
else //右侧
{
pNode uncle = gParent->_left;
if (uncle != nullptr && uncle->_color == RED) //第一种
{
uncle->_color = BLACK;
parent->_color = BLACK;
gParent->_color = RED;
cur = gParent;
parent = gParent->_parent;
}
else
{
if (cur == parent->_left) //先右旋再左旋 //第三种
{
RotateRight(parent);
//旋转后parent 和 cur 的位置互换
swap(parent, cur);
}
gParent->_color = RED; //第二种
parent->_color = BLACK;
RotateLeft(gParent);
break;
}
}
}
_head->_parent->_color = BLACK;
_head->_left = leftMost();
_head->_right = rightMost();
return true;
}
pNode leftMost()
{
pNode cur = _head->_parent;
while (cur && cur->_parent)
{
cur = cur->_left;
}
return cur;
}
pNode rightMost()
{
pNode cur = _head->_parent;
while (cur && cur->_parent)
{
cur = cur->_right;
}
return cur;
}
void InOrder()
{
_InOrder(_head->_parent);
cout << endl;
}
bool IsRBTree()
{
//判断红黑树
//1.中序遍历有序
//2.每条路径的黑色结点数相同
//3.红色节点不连续
pNode root = GetRoot();
if (root == nullptr)
return true;
//检测根节点情况
if (root->_color == RED)
{
cout << "根节点为黑色,违反条件" << endl;
return false;
}
//比较每条路径上的黑色节点个数
size_t blackCount = 0;
pNode cur = root;
while (cur)
{
if (cur->_color == BLACK)
blackCount++;
cur = cur->_left;
}
size_t k = 0; //记录路径上节点个数
return _IsRBTree(root, k, blackCount);
}
private:
pNode _head; //头节点:为了更好的实现迭代器
bool _IsRBTree(pNode root, size_t k, size_t blackCount)
{
if (root == nullptr)
{
if (k != blackCount)
{
cout << "每条路径上的黑色结点数不相同" << endl;
return false;
}
return true;
}
if (root->_color == BLACK)
{
++k;
}
else if (root->_parent->_color == RED) //检测结点和双亲是否同为红色
{
cout << "连续两个结点为红色" << endl;
return false;
}
return _IsRBTree(root->_left, k, blackCount) && _IsRBTree(root->_right, k, blackCount);
}
void _InOrder(pNode root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_data << " ";
_InOrder(root->_right);
}
void RotateRight(pNode root) //右旋
{
pNode subL = root->_left;
pNode subLR = subL->_right;
subL->_right = root;
root->_left = subLR;
if (subLR != nullptr)
{
subLR->_parent = root;
}
if (root != _head->_parent)
{
pNode parent = root->_parent;
if (parent->_left == root)
{
parent->_left = subL;
}
else
{
parent->_right = subL;
}
subL->_parent = parent;
}
else
{
_head->_parent = subL;
subL->_parent = _head;
}
root->_parent = subL;
}
void RotateLeft(pNode root) //左旋
{
pNode subR = root->_right;
pNode subRL = subR->_left;
subR->_left = root;
root->_right = subRL;
if (subRL != nullptr)
{
subRL->_parent = root;
}
if (root != _head->_parent)
{
pNode parent = root->_parent;
if (root == parent->_left)
{
parent->_left = subR;
}
else
{
parent->_right = subR;
}
subR->_parent = parent;
}
else
{
_head->_parent = subR;
subR->_parent = _head;
}
root->_parent = subR;
}
};
红黑树的调整问题:(画工不好见谅)