C++ 容器的两把利器之优先级队列与反向迭代器实现原理解析

 更新时间:2026年01月26日 14:37:37   作者:蓝羊羊不蓝  
本文介绍了反向迭代器和优先级队列的实现原理,反向迭代器通过适配器模式包装正向迭代器,实现反向遍历,优先级队列基于堆结构,通过仿函数自定义比较规则,支持最大堆和最小堆,文章详细解释了这两种数据结构的内部实现逻辑,感兴趣的朋友跟随小编一起看看吧

-------------反向迭代器------------

1、适配器模式

要实现反向迭代器,就不得不提到适配器模式

在上一篇内容中,我们学习的 stackqueue 就是典型的容器适配器;而今天要讲的反向迭代器,则是一种迭代器适配器

2、反向迭代器原理

反向迭代器是一个迭代器适配器,它包装了一个正向迭代器,把 ++ 操作映射成原迭代器的 --,把 -- 操作映射成原迭代器的 ++,从而实现反向遍历的效果

反向迭代器在容器中的指向:

3、反向迭代器的实现

namespace ljh
{
	//typedef iterator<iterator, T& , T*> iterator;
	//typedef iterator<iterator, const T& , const T*> iterator;
	template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> Self;
		Iterator _it;
		ReverseIterator(Iterator it)
			:_it(it)
		{
		}
		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}
		Ptr operator->()
		{
			return &(operator*());
		}
		Self& operator++()
		{
			--_it;
			return *this;
		}
		//由于没写析构函数,所以不用担心拷贝构造是浅拷贝
		Self operator++(int)
		{
			Self tmp(*this); 
			--_it;//正向迭代器--,反向迭代器++
			return tmp;
		}
		Self& operator--()
		{
			++_it;
			return *this;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			++_it;
			return tmp;
		}
		bool operator!=(const Self& s) const
		{
			return _it != s._it;
		}
	};
    typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef ReverseIterator<const_iterator, const T&, const T*const_reverse_iterator;
    /*===================反向迭代器=====================*/
	reverse_iterator rbegin()
	{
		return reverse_iterator(end());
	}
	reverse_iterator rend()
	{
		return reverse_iterator(begin());
	}
	const_reverse_iterator rbegin()const
	{
		return reverse_iterator(end());
	}
	const_reverse_iterator rend() const
	{
		return reverse_iterator(begin());
	}
}

3.1 模板参数解析

template<class Iterator, class Ref, class Ptr>
struct ReverseIterator

Iterator被适配的正向迭代器类型(比如 vector::iterator

Ref迭代器取值(*it)时返回的引用类型(比如 T&const T&

Ptr迭代器箭头访问(it->)时返回的指针类型(比如 T*const T*

3.2 类型别名与成员变量

typedef ReverseIterator<Iterator, Ref, Ptr> Self;
Iterator _it;

Self简化自身类型的书写,避免重复写长模板名

_it内部持有的正向迭代器,是反向迭代器的 “核心数据”

3.3 构造函数

ReverseIterator(Iterator it) : _it(it) {}

用一个正向迭代器来初始化反向迭代器,把传入的迭代器保存到 _it 中。

3.4 取值运算符operator*

Ref operator*()
{
    Iterator tmp = _it;
    return *(--tmp);
}

这是反向迭代器的核心适配逻辑。

它先拷贝一份内部迭代器 _it,对拷贝做 -- 移动到前一个位置,再取值返回。

这样保证了 rbegin() 能正确指向容器的最后一个元素。

3.5 箭头运算符operator->

Ptr operator->()
{
    return &(operator*());
}

复用 operator* 的结果,取其地址返回,支持 it->member 这样的指针访问语法。

3.6 前置 ++ 运算符operator++

Self& operator++()
{
    --_it;
    return *this;
}

反向迭代器的 ++ 对应内部正向迭代器的 --,实现 “向后移动”(在反向遍历中是向前走)。

3.7 后置 ++ 运算符operator++(int)

Self operator++(int)
{
    Self tmp(*this);
    --_it;
    return tmp;
}

先创建一个当前对象的副本,再移动内部迭代器,最后返回副本。

这是后置 ++ 的标准实现,保证返回的是 “移动前” 的迭代器。

3.8 前置 -- 运算符operator--

Self& operator--()
{
    ++_it;
    return *this;
}

反向迭代器的 -- 对应内部正向迭代器的 ++,实现 “向前移动”(在反向遍历中是向后退)。

3.9 后置 -- 运算符operator--(int)

Self operator--(int)
{
    Self tmp(*this);
    ++_it;
    return tmp;
}

逻辑同后置 ++,先拷贝再移动,返回移动前的迭代器。

3.10 不等比较运算符operator!=

bool operator!=(const Self& s) const
{
    return _it != s._it;
}
bool operator==(const Self& s) const
{
    return _it == s._it; 
}

直接比较两个反向迭代器内部持有的正向迭代器,判断它们是否指向不同位置。

// 先定义反向迭代器的类型别名(依赖之前的 ReverseIterator 适配器)
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
// 1. 普通版 rbegin():可读写反向迭代器起点
reverse_iterator rbegin()
{
    // 核心:用正向迭代器的 end() 初始化反向迭代器,指向容器最后一个元素
    return reverse_iterator(end());
}
// 2. 普通版 rend():可读写反向迭代器终点
reverse_iterator rend()
{
    // 核心:用正向迭代器的 begin() 初始化反向迭代器,指向第一个元素之前
    return reverse_iterator(begin());
}
// 3. const版 rbegin() const:只读反向迭代器起点
const_reverse_iterator rbegin() const
{
    // 核心:逻辑同普通版,但返回 const 版本,仅支持读操作
    return const_reverse_iterator(end());
}
// 4. const版 rend() const:只读反向迭代器终点
const_reverse_iterator rend() const
{
    // 核心:逻辑同普通版,但返回 const 版本,仅支持读操作
    return const_reverse_iterator(begin());
}

-------------优先级队列------------

1、仿函数

仿函数(Functor)是 C++ 中一种特殊的类对象,核心特点是重载(重载)了 operator() 运算符,使得对象可以像函数一样被调用(用 对象名(参数) 的形式)。

简单说:仿函数是 “像函数的对象”,本质是带 operator() 的类实例。

template<class T>
class small
{
public:
	bool operator()(const T& x,const T& y)
	{
         return x < y;
	}
};
template<class T>
class big
{
public:
	bool operator()(const T& x,const T& y)
	{
		return x > y;
	}
};
int main()
{ 
    small<int> s;
    big<int> b;
    cout << s(1,2) << endl;
	cout << b(1, 2) << endl;
	return 0;
}

对初次接触的读者来说,仿函数的调用方式(比如 s(1,2))确实显得有些陌生,和我们熟悉的 += 等运算符重载的写法很不一样。你可能暂时会疑惑它的实际价值,这很正常 —— 当我们后续用它来定制优先级队列的比较规则时,这种设计的灵活性和必要性就会清晰地展现出来。

2、优先级队列介绍

priority_queue 是 C++ 标准库中的一个容器适配器,底层默认用 vector 存储数据,内部维护一个堆结构,确保队首元素始终是优先级最高的(默认是最大值)。

核心特点

只能访问队首的最大元素,不能遍历或随机访问其他元素。

插入新元素时,会自动调整堆结构以维持优先级顺序。

弹出队首元素后,剩余元素也会自动重新调整。

底层原理:通过调用 make_heappush_heappop_heap 等算法函数来维护堆的特性。

默认行为:默认是大顶堆(最大元素优先),可以通过传入仿函数(如 greater<T>)改为小顶堆。

3、优先级队列的实现

namespace ljh
{
    // 仿函数/函数对象
    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
	{
	private:
		//默认建大堆
		void AdjustDown(int parent)
		{
			Compare com;//仿函数对象
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
					//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
				{
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		//向上调整算法
		void AdjustUp(int child)
		{
			Compare com;//仿函数对象
			int parent = (child - 1)/2;
			while (child > 0)
			{
				if (com(_con[parent],_con[child]))
				{
					swap(_con[parent],_con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
	public:
        //默认构造
		priority_queue()
		{
		}
		//范围构造
		template<class InputIterator>
		priority_queue(InputIterator first,InputIterator last)
		{
			while (first!=last)
			{
				_con.push_back(*first);
				first++;
			}
			//建堆-向下调整建堆(默认建立大堆)
            //N
			for (int i = (_con.size() - 2) / 2; i >= 0; i--)
			{
				//向下调整算法
				AdjustDown(i);
			}
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size() - 1);
		}
		const T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

3.1 类模板定义

template<class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue

这是整个优先级队列的模板定义,有三个模板参数:

T队列中存储的元素类型。

Container底层存储数据的容器,默认用 vector,也可以换成 deque 等支持随机访问的容器。

Compare比较规则的仿函数,默认是 Less<T>(大顶堆),可以换成 Greater<T> 实现小顶堆。

3.2AdjustDown向下调整算法

void AdjustDown(int parent)
{
    Compare com;
    int child = parent * 2 + 1;
    while (child < _con.size())
    {
        if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
        {
            ++child;
        }
        if (com(_con[parent], _con[child]))
        {
            swap(_con[child], _con[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

这是维护堆结构的核心函数,用来在堆顶元素被移除后,把新的堆顶向下调整到正确位置:

先创建一个比较规则的仿函数对象 con

parent 的左孩子 child = parent*2+1 开始。

先在左右孩子中,用 com 比较出优先级更高的那个,作为真正要交换的 child

然后用 com 比较父节点和这个孩子节点,如果父节点优先级更低,就交换它们,并继续向下调整。

如果父节点优先级已经更高,说明调整完成,直接跳出循环。

3.3AdjustUp向上调整算法

void AdjustUp(int child)
{
    Compare com;
    int parent = (child - 1) / 2;
    while (child > 0)
    {
        if (com(_con[parent], _con[child]))
        {
            swap(_con[child], _con[parent]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }
    }
}

这个函数用来在新元素插入堆尾后,把它向上调整到正确位置:

创建比较规则的仿函数对象 con

计算当前 child 节点的父节点 parent = (child-1)/2

con 比较父节点和孩子节点,如果父节点优先级更低,就交换它们,并继续向上调整。

如果父节点优先级已经更高,说明调整完成,跳出循环。

3.4 默认构造函数

priority_queue()
{}

空的默认构造函数,底层容器 _con 会自己调用默认构造,不需要额外操作。

3.5 范围构造函数

template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
    while (first != last)
    {
        _con.push_back(*first);
        first++;
    }
    for (int i = (_con.size() - 2) / 2; i >= 0; i--)
    {
        AdjustDown(i);
    }
}

这个构造函数可以用一段迭代器区间来初始化队列:

先把区间里的所有元素都插入到底层容器 _con 中。

然后从最后一个非叶子节点开始,依次调用 AdjustDown,把整个容器调整成一个合法的堆结构。

3.6 pop弹出堆顶元素

void pop()
{
    swap(_con[0], _con[_con.size() - 1]);
    _con.pop_back();
    AdjustDown(0);
}

弹出堆顶元素的步骤:

先把堆顶元素(_con[0])和堆尾元素交换。

然后删除堆尾元素(也就是原来的堆顶)。

最后对新的堆顶元素调用 AdjustDown,重新维护堆的结构。

3.7push插入新元素

void push(const T& x)
{
    _con.push_back(x);
    AdjustUp(_con.size() - 1);
}

插入新元素的步骤:

先把新元素插入到底层容器的尾部。

然后对这个新插入的元素调用 AdjustUp,把它向上调整到正确的位置,以维持堆的性质。

3.8top获取堆顶元素

const T& top()
{
    return _con[0];
}

直接返回底层容器的第一个元素,也就是堆顶元素。因为堆顶始终是优先级最高的元素。

3.9empty判断队列是否为空

bool empty()
{
    return _con.empty();
}

直接调用底层容器的 empty() 方法,判断队列是否为空。

3.10size获取队列元素个数

size_t size()
{
    return _con.size();
}

直接返回底层容器的大小,也就是队列中元素的个数。

到此这篇关于C++ 容器的两把利器之优先级队列与反向迭代器实现原理解析的文章就介绍到这了,更多相关C++ 优先级队列与反向迭代器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言深入了解自定义数据类型的使用

    C语言深入了解自定义数据类型的使用

    这篇文章主要给大家介绍了关于C语言自定义数据类型的结构体、枚举和联合的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • iOS锁屏音频播放控制及音频信息设置

    iOS锁屏音频播放控制及音频信息设置

    这篇文章主要为大家详细介绍了iOS锁屏音频播放控制及音频信息设置,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • C语言各种变量的初始化方式

    C语言各种变量的初始化方式

    这篇文章主要介绍了C语言各种变量的初始化方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • C语言实现折半查找法(二分法)

    C语言实现折半查找法(二分法)

    这篇文章主要为大家详细介绍了C语言实现折半查找法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • C++常用的#include头文件总结

    C++常用的#include头文件总结

    这篇文章主要介绍了C++常用的#include头文件,对初学者理解C++程序设计大有好处的相关资料
    2014-07-07
  • C语言实现随机抽奖程序

    C语言实现随机抽奖程序

    这篇文章主要为大家详细介绍了C语言实现随机抽奖程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • C语言编程递归算法实现汉诺塔

    C语言编程递归算法实现汉诺塔

    递归,大家都了解,著名的斐波那契数,就为该知识点的经典例题。今天来看看更为经典的递归题汉诺塔不过这其实是数学问题,先来看看汉诺塔
    2021-09-09
  • C++继承详细介绍

    C++继承详细介绍

    我们都知道面向对象语言的三大特点是:**封装,继承,多态。**之前在类和对象部分,我们提到了C++中的封装,那么今天呢,我们来学习一下C++中的继承
    2022-10-10
  • C++读取配置文件的示例代码

    C++读取配置文件的示例代码

    这篇文章主要介绍了C++读取配置文件的示例代码,帮助大家更好的理解和学习C++开发,感兴趣的朋友可以了解下
    2020-08-08
  • C++设计模式之外观模式

    C++设计模式之外观模式

    这篇文章主要介绍了C++设计模式之外观模式,本文详细讲解了C++中的Facade模式,并给出了实例代码,需要的朋友可以参考下
    2014-10-10

最新评论