容器适配器(Container Adapter)是一种 C++ 中的抽象数据类型,它提供了一种在指定底层容器基础上进行封装,以实现特定功能的方式。容器适配器并不是独立的容器类型,而是建立在其他容器之上的封装,通过提供不同的接口或限制来满足特定需求。
priority_queue(优先级队列)就是一个容器适配器,是数据结构上的堆(Heap)的实现,库中的声明如下:
template <class T,
class Container = vector<T>,
class Compare = less<typename Container::value_type> >
class priority_queue;
基础容器可以是库里有的,也可以是自己实现的,但都应当满足以下要求:
本次模拟实现只是,简单的模拟实现,旨在加深对 priority_queue 的理解和了解和使用仿函数,主要参考 C++98 版本的 priority_queue。进行模拟实现。
priority_queue 类的大致框架如下:
template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
// priority_queue 该提供以下接口
priority_queue(const Container& ctnr = Container(), const Compare& comp = Compare());
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last);
bool empty() const;
size_t size() const;
const T& top() const;
void push(const T& x);
void pop();
private:
void adjust_up(size_t child);
void adjust_down(size_t parent);
private:
Container _c;
Compare _comp;
};
C++98版本的 priority_queue 的构造函数重载有两个版本,一个是全缺省的默认构造函数,另一个是迭代器区间构造函数。
// 全缺省默认构造
priority_queue(const Container& ctnr = Container(), const Compare& comp = Compare())
: _c(ctnr)
, _comp(comp)
{}
// 迭代器范围区间构造
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
// 先调用 Container 对象的 range constructor
: _c(first, last)
{
// 从最后一个非叶子结点开始向下调整成堆结构
int count = size();
int root = ((count - 2) >> 1);
for (; root >= 0; --root)
{
adjust_down(root);
}
}
这三个不是重点,且实现起来比较简单。
size_t size() const
{
return _c.size();
}
bool empty() const
{
return _c.empty();
}
// 堆顶数据不可被修改,堆顶元素被修改会破坏堆的特性
const T& top() const
{
return _c.front();
}
void push (const T& val);
形参 val 是待插入对象
val 类型是 const T&,引用传参是为了降低传参消耗。
void push(const T& x)
{
// 1. 对象插入数据
_c.push_back(x);
// 2. 向上调整
adjust_up(_c.size() - 1);
}
// 向上调整算法
void adjust_up(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (_c[parent] < _c[child])
{
swap(_c[parent], _c[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void pop();
void pop()
{
if (empty())
return;
// 交换堆顶和末尾
swap(_c[0], _c[_c.size() - 1]);
// 删除末尾位置元素
_c.pop_back();
// 堆顶位置向下调整
adjust_down(0);
}
// 向下调整算法
void adjust_down(size_t parent)
{
size_t child = (parent * 2) + 1;
while (child < _c.size())
{
if (child + 1 < _c.size() && _c[child] < _c[child + 1])
{
child += 1;
}
if (_c[parent] < _c[child])
{
swap(_c[parent], _c[child]);
parent = child;
child = (parent * 2) + 1;
}
else
{
break;
}
}
}
// adjust_up
if (_c[parent] < _c[child])
// adjust_down
if (child + 1 < _c.size() && _c[child] < _c[child + 1])
if (_c[parent] < _c[child])
这几句代码写死了,priority_queue 实现的堆只能是大堆,但是通过直接修改源代码的方式来进行大小堆的切换是不现实的,更推荐的做法是通过回调函数控制比较逻辑,C语言采用函数指针实现,C++更喜欢使用仿函数(函数对象)。
仿函数(Functor)是一种行为类似函数的对象,类中重载了函数调用运算符 operator(),通过这种方式,对象就可以像函数一样被调用。
class less
{
public:
bool operator()(int x, int y)
{
return x < y;
}
};
int main()
{
// 实例化对象 lessfunc
less lessfunc;
// 调用成员函数
cout << lessfunc(1, 2) << endl;
cout << lessfunc.operator()(1, 2) << endl;
return 0;
}
乍一看我们会认为lessfunc是函数名,但其实它是一个对象,它通过运算符重载让这个对象能够仿造函数的形式来使用。
现在有一个 A 类,类中有一个成员方法 func() 作用是比较两个整型数据的大小,func() 通过使用仿函数回调 less 类对象、greater 类对象中的方法来实现 func() 中比较逻辑的控制,做法如下:
class less
{
public:
bool operator()(int x, int y)
{
return x < y;
}
};
class greater
{
public:
bool operator()(int x, int y)
{
return x > y;
}
};
template<class Compare>
class A
{
public:
// 功能:比较两个int数据的大小,返回比较结果
void func(int xx, int yy)
{
Compare com;
cout << com(xx, yy) << endl;
}
};
int main()
{
A<less> a1;
a1.func(100, 200);
A<less> a2;
a2.func(100, 200);
return 0;
}
现在有一个 A 类,类中有一个成员方法 func() 作用是比较两个整型数据的大小,func() 通过函数指针回调全局函数 lessfc、greaterfc 来控制 func() 的比较逻辑,做法如下:
bool lessfc(int x, int y)
{
return x < y;
}
bool greaterfc(int x, int y)
{
return x > y;
}
// A 类回调 lessfc、greater
class A
{
public:
A(bool(*pf)(int, int))
:_pf(pf)
{}
// 控制 xx,yy 的比较逻辑
void func(int xx, int yy)
{
cout << _pf(xx, yy) << endl;
}
private:
bool(*_pf)(int, int);
};
int main()
{
A a(lessfc);
// 比较大小
cout << a.func(100, 200) << endl;
A a(greaterfc);
// 比较大小
cout << a.func(100, 200) << endl;
return 0;
}
相较于仿函数实现,函数指针实现有几个缺陷:
template<class T>
class less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
// push()...
// pop()...
private:
void adjust_up(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_c[parent] < _c[child])
if (_comp(_c[parent], _c[child]))
{
swap(_c[parent], _c[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void adjust_down(size_t parent)
{
size_t child = (parent * 2) + 1;
while (child < _c.size())
{
//if (child + 1 < _c.size() && _c[child] < _c[child + 1])
if (child + 1 < _c.size() && _comp(_c[child], _c[child + 1]))
{
child += 1;
}
//if (_c[parent] < _c[child])
if (_comp(_c[parent], _c[child]))
{
swap(_c[parent], _c[child]);
parent = child;
child = (parent * 2) + 1;
}
else
{
break;
}
}
}
private:
Container _c;
Compare _comp;
};
namespace ljh
{
#include
template<class T>
class less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
priority_queue(const Container& ctnr = Container(), const Compare& comp = Compare())
: _c(ctnr)
, _comp(comp)
{}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
// 先调用 Container 对象的 range constructor
: _c(first, last)
{
// 从最后一个非叶子结点开始向下调整成堆结构
int count = size();
int root = ((count - 2) >> 1);
for (; root >= 0; --root)
{
adjust_down(root);
}
}
bool empty() const
{
return _c.empty();
}
size_t size() const
{
return _c.size();
}
// 堆顶数据不可被修改,堆顶元素被修改会破坏堆的特性
const T& top() const
{
return _c.front();
}
void push(const T& x)
{
// 1. 对象插入数据
_c.push_back(x);
// 2. 向上调整
adjust_up(_c.size() - 1);
}
void pop()
{
if (empty())
return;
// 交换堆顶和末尾
swap(_c[0], _c[_c.size() - 1]);
// 删除末尾位置元素
_c.pop_back();
// 堆顶位置向下调整
adjust_down(0);
}
private:
void adjust_up(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
//if (_c[parent] < _c[child])
if (_comp(_c[parent], _c[child]))
{
swap(_c[parent], _c[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void adjust_down(size_t parent)
{
size_t child = (parent * 2) + 1;
while (child < _c.size())
{
//if (child + 1 < _c.size() && _c[child] < _c[child + 1])
if (child + 1 < _c.size() && _comp(_c[child], _c[child + 1]))
{
child += 1;
}
//if (_c[parent] < _c[child])
if (_comp(_c[parent], _c[child]))
{
swap(_c[parent], _c[child]);
parent = child;
child = (parent * 2) + 1;
}
else
{
break;
}
}
}
private:
Container _c;
Compare _comp;
};
}