一、简介
priority_queue 是 C++ STL 的一个容器,它中文名是优先队列,注意不是堆,优先队列是一种特殊的队列,每个元素都有一个优先级(一般为升序或降序,也可以按入队顺序,即普通队列)。在插入元素时,根据元素的优先级将其插入到合适的位置。优先队列可以使用多种数据结构实现,包括堆、有序数组、二叉搜索树等,在这里逐一介绍。
1. 有序数组
有序数组的定义很广泛,只按照一定顺序排列的数组,可以用排序算法实现:
template<typename T>
struct OrderedArray {
std::vector <T> data;
void push(T x){
data.push_back(x);
std::sort(data.begin(),data.end());
}
void pop(){
data.erase(data.begin());
}
T top(){
return data.front();
}
}
上文的插入的时间复杂度为 O(size log size) 也可以用冒泡排序实现 O(size)。pop 和 top 函数时间复杂度均为 O(1) 。
2.二叉搜索树
二叉搜索树(Binary Search Tree),(又名:二叉查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
在 STL 中(bits/stl_tree), 二叉搜索树使用的是红黑树。红黑树是一种特定类型的二叉树。红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。 由于每一棵红黑树都是一颗二叉排序树,因此,在对红黑树进行查找时,可以采用运用于普通二叉排序树上的查找算法,在查找过程中不需要颜色信息。
是的,STL 中自带二叉搜索树,不需要手写,但手写速度快(除非开O2),模板:
// 版权声明:本文遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
// 原文链接:https://blog.csdn.net/sqm_C/article/details/138462136
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 BST
{
typedef BSTreeNode<K> Node;
public:
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
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
{
return false;
}
}
cur = new Node(key);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
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 Erase(const K& key)
{
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)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else if (parent->_right == cur)
{
parent->_right = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else if (parent->_right == cur)
{
parent->_right = cur->_left;
}
}
delete cur;
}
//cur左右都不为空
else
{
Node* rightminparent = cur;
Node* rightmin = cur->_right;
while (rightmin->_left)
{
rightminparent = rightmin;
rightmin = rightmin->_left;
}
swap(rightmin->_key, cur->_key);
if(rightminparent->_left== rightmin)
rightminparent->_left = rightmin->_right;
else
rightminparent->_right = rightmin->_right;
delete rightmin;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << endl;
_InOrder(root->_right);
}
Node* _root = nullptr;
};
三、堆
STL 中 priority_queue 的实现就是用的堆,堆是最高效的优先队列,堆通常是一个可以被看作一棵完全二叉树的数组对象。
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
堆中某个结点的值总是不大于或不小于其父结点的值;
堆总是一棵完全二叉树。
根结点最大的堆叫做大根堆,根结点最小的堆叫做小根堆。特殊的堆有二叉堆、斐波那契堆等。
堆的物理结构本质上是顺序存储的,是线性的。但在逻辑上不是线性的,是完全二叉树的这种逻辑储存结构。 堆的这个数据结构,里面的成员包括一维数组,数组的容量,数组元素的个数,有两个直接后继。
若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列 {k1, k2, …,kn } 是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。(未完待续~~)