真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

【C++】優(yōu)先級隊列、仿函數(shù)和反向迭代器-創(chuàng)新互聯(lián)

?🌠作者:@阿亮joy.
🎆專欄:《吃透西嘎嘎》
🎇座右銘:每個優(yōu)秀的人都有一段沉默的時光,那段時光是付出了很多努力卻得不到結果的日子,我們把它叫做扎根
在這里插入圖片描述

天津ssl適用于網(wǎng)站、小程序/APP、API接口等需要進行數(shù)據(jù)傳輸應用場景,ssl證書未來市場廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書合作)期待與您的合作!

目錄
    • 👉priority_queue 的介紹👈
    • 👉priority_queue 的使用👈
    • 👉仿函數(shù)👈
    • 👉priority_queue 的模擬實現(xiàn)👈
    • 👉反向迭代器👈
    • 👉總結👈

👉priority_queue 的介紹👈
  1. 優(yōu)先隊列是一種容器適配器,根據(jù)嚴格的弱排序標準,它的第一個元素總是它所包含的元素中大的。
  2. 此上下文類似于堆,在堆中可以隨時插入元素,并且只能檢索大堆元素(優(yōu)先隊列中位于頂部的元素)。
  3. 優(yōu)先隊列被實現(xiàn)為容器適配器,容器適配器即將特定容器類封裝作為其底層容器類.priority_queue 提供一組特定的成員函數(shù)來訪問其元素,元素從特定容器的尾部彈出,其稱為優(yōu)先隊列的頂部。
  4. 底層容器可以是任何標準容器類模板,也可以是其他特定設計的容器類。容器應該可以通過隨機訪問迭代器訪問,并支持以下操作:
    • empty():檢測容器是否為空
    • size():返回容器中有效元素個數(shù)
    • front():返回容器中第一個元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():刪除容器尾部元素
  5. 標準容器類 vector 和 deque 滿足這些需求。默認情況下,如果沒有為特定的 priority_queue 類實例化指定容器類,則使用 vector。
  6. 需要支持隨機訪問迭代器,以便始終在內部保持堆結構。容器適配器通過在需要時自動調用算法函數(shù) make_heap、push_heap 和 pop_heap 來自動完成此操作。
👉priority_queue 的使用👈

優(yōu)先級隊列默認使用 vector 作為其底層存儲數(shù)據(jù)的容器。在 vector 上又使用了向上調整算法和向下調整算法將 vector 中元素構造成堆的結構,因此 priority_queue 就是堆。所有需要用到堆的地方,都可以考慮使用priority_queue。注意:默認情況下 priority_queue 是大堆。

函數(shù)聲明接口說明
priority_queue()構造一個空的優(yōu)先級隊列
priority_queue(first, last)迭代器區(qū)間構造優(yōu)先級隊列
empty( )檢測優(yōu)先級隊列是否為空,是返回 true,否則返回 false
top( )返回優(yōu)先級隊列中大元素(最小元素),即堆頂元素
push(x)在優(yōu)先級隊列中插入元素 x
pop()刪除優(yōu)先級隊列中大元素(最小元素),即堆頂元素
size()返回優(yōu)先級隊列中元素的個數(shù)

代碼示例:

#include// 優(yōu)先級隊列priority的頭文件
#includeusing namespace std;
#include// greater算法的頭文件

int main()
{// 默認情況下,創(chuàng)建的是大堆,其底層是按照小于號比較的
	priority_queuepq1; 
	pq1.push(3);
	pq1.push(2);
	pq1.push(8);
	pq1.push(5);
	pq1.push(1);

	while (!pq1.empty())
	{cout<< pq1.top()<< " ";
		pq1.pop();
	}
	cout<< endl;

	// 如果要創(chuàng)建小堆,將第三個模板參數(shù)換成greater比較方式,其底層是按照大于號比較的
	priority_queue, greater>pq2;
	pq2.push(3);
	pq2.push(2);
	pq2.push(8);
	pq2.push(5);
	pq2.push(1);

	while (!pq2.empty())
	{cout<< pq2.top()<< " ";
		pq2.pop();
	}
	cout<< endl;

	return 0;
}

在這里插入圖片描述

如果在priority_queue中放自定義類型的數(shù)據(jù),用戶需要在自定義類型中提供 >或者< 的重載。

#include// 優(yōu)先級隊列priority的頭文件
#include// greater算法的頭文件
#includeusing namespace std;

class Date
{public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{return (_year< d._year) ||
			(_year == d._year && _month< d._month) ||
			(_year == d._year && _month == d._month && _day< d._day);
	}
	bool operator>(const Date& d)const
	{return (_year >d._year) ||
			(_year == d._year && _month >d._month) ||
			(_year == d._year && _month == d._month && _day >d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{_cout<< d._year<< "-"<< d._month<< "-"<< d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestPriorityQueue()
{// 大堆,需要用戶在自定義類型中提供<的重載
	priority_queueq1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	while (!q1.empty())
	{cout<< q1.top()<< "  ";
		q1.pop();
	}
	cout<< endl;

	// 如果要創(chuàng)建小堆,需要用戶提供>的重載
	priority_queue, greater>q2;
	q2.push(Date(2018, 10, 29));
	q2.push(Date(2018, 10, 28));
	q2.push(Date(2018, 10, 30));
	while (!q2.empty())
	{cout<< q2.top()<< "  ";
		q2.pop();
	}
	cout<< endl;
}

int main()
{TestPriorityQueue();
	return 0;
}

在這里插入圖片描述

優(yōu)先級的隊列使用起來沒有什么難的,接下來我們來做一道題目,順便回顧一下堆。

數(shù)組中的第K個大元素

給定整數(shù)數(shù)組 nums 和整數(shù) k,請返回數(shù)組中第 k 個大的元素。

請注意,你需要找的是數(shù)組排序后的第 k 個大的元素,而不是第 k 個不同的元素。

你必須設計并實現(xiàn)時間復雜度為 O(n) 的算法解決此問題。

在這里插入圖片描述

解決這道題有兩種思路,第一種就是用數(shù)組的元素建大堆,然后 pop 掉 k - 1 個元素,此時堆頂?shù)脑鼐褪堑?k 大的數(shù)。這種思路的時間復雜度為O(N + k * lgN),當 N 很大時,就會需要非常多的空間。這時候,我們可以考慮第二種思路就是用前 k 個數(shù)建只包含 k 個數(shù)的小堆,再遍歷數(shù)組剩下的元素,比堆頂元素大則替換掉堆頂元素,再向下調整堆。這種思路的時間復雜度為O(k + (N - k) * lgk)注:頂元素不允許修改,因為堆頂元素修改可以會破壞堆的特性。

class Solution 
{public:
    int findKthLargest(vector& nums, int k) 
    {// 建n個數(shù)的大堆
        priority_queuepq(nums.begin(), nums.end());
        // pop掉前k-1個大的數(shù)
        while(--k)  pq.pop();
        return pq.top();
    }
};

在這里插入圖片描述

class Solution 
{public:
    int findKthLargest(vector& nums, int k) 
    {// 建k個數(shù)的小堆,從下標k開始遍歷數(shù)組
        // 如果num[i]大于堆頂?shù)臄?shù)據(jù),那么nums[i]入堆
        // 遍歷結束,堆頂?shù)臄?shù)就是第k大的數(shù)
        priority_queue, greater>pq(nums.begin(), nums.begin() + k);
        for(size_t i = k; i< nums.size(); ++i)
        {if(nums[i] >pq.top())
            {pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};

在這里插入圖片描述

👉仿函數(shù)👈

仿函數(shù)也稱為函數(shù)對象,它是具有operator()的類對象,可以想函數(shù)一樣來使用它。

#includeusing namespace std;

// 仿函數(shù)/函數(shù)對象
templateclass Less
{public:
	templatebool operator()(const T& left, const T& right)
	{return left< right;
	}
};

templateclass Greater
{public:
	templatebool operator()(const T& left, const T& right)
	{return left >right;
	}
};

int main()
{// 仿函數(shù)用起來就像是函數(shù)
	// 有名對象
	LesslessFunc;
	cout<< lessFunc(1, 2)<< endl;
	// lessFunc(1, 2)<==>lessFunc.operator()(1, 2);
	
	// 匿名對象
	cout<< Greater()(1, 2)<< endl;

	return 0;
}

在這里插入圖片描述

這樣一看,好像仿函數(shù)也沒有特別大的又是,和函數(shù)指針也沒有什么大的區(qū)別嘛!其實不然,函數(shù)指針需要寫函數(shù)的參數(shù)和返回值類型。所以仿函數(shù)用起來還是比函數(shù)指針舒服的。

#includeusing namespace std;

// 仿函數(shù)/函數(shù)對象
templateclass Less
{public:
	templatebool operator()(const T& left, const T& right)
	{return left< right;
	}
};

templateclass Greater
{public:
	templatebool operator()(const T& left, const T& right)
	{return left >right;
	}
};

templatevoid BubbleSort(T* a, int n, Compare com)
{for (int i = 0; i< n - 1; ++i)
	{int exchange = 0;
		for (int j = 1; j< n - i; ++j)
		{	// if (a[i]< a[i - 1])
			if (com(a[j], a[j - 1]))
			{		swap(a[j], a[j - 1]);
				exchange = 1;
			}
		}

		if (exchange == 0)
			break;
	}
}

int main()
{LesslessFunc;
	int arr[] = {2, 7,3, 1, 5, 9 ,10,20 };
	BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), lessFunc);
	for (auto e : arr)
		cout<< e<< " ";
	cout<< endl;

	BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), Greater());
	for (auto e : arr)
		cout<< e<< " ";
	cout<< endl;

	return 0;
}

在這里插入圖片描述

注:沒有成員變量的類的大小是 1 個字節(jié),所以傳參時加不加引用都沒有關系。如果參數(shù)用 const 修飾了,那么仿函數(shù)也要用 const 來修飾。

如果庫提供的仿函數(shù)不符合我們的需求,我們可以自己寫!

#include// 優(yōu)先級隊列priority的頭文件
#include// greater算法的頭文件
#includeusing namespace std;

class Date
{public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{return (_year< d._year) ||
			(_year == d._year && _month< d._month) ||
			(_year == d._year && _month == d._month && _day< d._day);
	}
	bool operator>(const Date& d)const
	{return (_year >d._year) ||
			(_year == d._year && _month >d._month) ||
			(_year == d._year && _month == d._month && _day >d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{_cout<< d._year<< "-"<< d._month<< "-"<< d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

struct PDateLess
{bool operator()(const Date* d1, const Date* d2)
	{return *d1< *d2;
	}
};

struct PDateGreater
{bool operator()(const Date* d1, const Date* d2)
	{return *d1 >*d2;
	}
};

void TestPriorityQueue()
{priority_queue, PDateLess>q3;
	q3.push(new Date(2018, 10, 29));
	q3.push(new Date(2018, 10, 28));
	q3.push(new Date(2018, 10, 30));
	while (!q3.empty())
	{cout<< *q3.top()<< "  ";
		q3.pop();
	}
	cout<< endl;

	priority_queue, PDateGreater>q4;
	q4.push(new Date(2018, 10, 29));
	q4.push(new Date(2018, 10, 28));
	q4.push(new Date(2018, 10, 30));
	while (!q4.empty())
	{cout<< *q4.top()<< "  ";
		q4.pop();
	}
	cout<< endl;
}

int main()
{TestPriorityQueue();
	return 0;
}

在這里插入圖片描述

現(xiàn)在學習到的仿函數(shù)還只是冰山一角,以后還會有更多的仿函數(shù)要學?。。?/p>👉priority_queue 的模擬實現(xiàn)👈

// PriorityQueue.h
#pragma once

namespace Joy
{// 仿函數(shù)
	templatestruct less
	{bool operator()(const T& left, const T& right)
		{	return left< right;
		}
	};

	templatestruct greater
	{bool operator()(const T& left, const T& right)
		{	return left >right;
		}
	};

	template, class Compare = less>class priority_queue
	{public:

		priority_queue()
			: _con()
		{}

		templatepriority_queue(InputIterator first, InputIterator last)
			: _con(first, last)
		{	// 注意:這里要使用int!如果使用size_t,如果只有一個元素會出現(xiàn)Bug
			int size = _con.size();
			for (int i = (size - 2) / 2; i >= 0; --i)
				adjust_down(i);
		}

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

		void pop()
		{	//swap(_con[0], _con[_con.size() - 1]);
			swap(_con.front(), _con.back());
			_con.pop_back();
			adjust_down(0);
		}

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

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

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

	private:
		Container _con;	// 底層容器

		// 向上調整算法
		void adjust_up(size_t child)
		{	size_t parent = (child - 1) / 2;

			while (child >0)
			{		
				//if(_con[parent]< _con[child])
				if (Compare()(_con[parent], _con[child]))	// 匿名對象
				{swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
					break;
			}
		}

		void adjust_down(size_t parent)
		{	size_t child = 2 * parent + 1;	
			size_t size = _con.size();
			
			while (child< size)
			{		// 找出較大的孩子
				if (child + 1< size && Compare()(_con[child], _con[child + 1]))
					++child;

				if (Compare()(_con[parent], _con[child]))
				{swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
					break;
			}
		}
	};
}

// Test.cpp
#include#includeusing namespace std;
#include "PriorityQueue.h"

int main()
{//priority_queuepq; 默認是大堆
	//Joy::priority_queuepq;
	Joy::priority_queue, greater>pq;
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);
	pq.push(1);

	while (!pq.empty())
	{cout<< pq.top()<< " ";
		pq.pop();
	}
	cout<< endl;

	return 0;
}

在這里插入圖片描述

優(yōu)先級隊列的實現(xiàn)就不講解了,和用C語言大的區(qū)別就是不需要自己造輪子了,直接使用 vector 適配出優(yōu)先級隊列。還有就是使用了仿函數(shù),可以根據(jù)傳入的仿函數(shù)生成大堆還是小堆。如果還有相關的實現(xiàn)細節(jié)不了解的話,可以看這篇文章:堆的實現(xiàn)!

👉反向迭代器👈

方向迭代器其實也是一種適配器,它可以適配出各種容器的反向迭代器,其主要的是將正向迭代器進行了封裝,反向迭代器 ++ 就復用正向迭代器的 - -,反向迭代器 - - 就復用正向迭代器的 ++。

#pragma once

templateclass ReverseInterator
{public:
	typedef ReverseInteratorSelf;

	ReverseInterator(Interator it)
		: _it(it)
	{}

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

	Self operator++(int)
	{auto tmp = _it;
		--_it;
		return tmp;
	}

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

	Self operator--(int)
	{auto tmp = _it;
		++_it;
		return tmp;
	}

	Ref operator*()
	{auto tmp = _it;
		return *(--tmp);
	}
	
	// 返回當前對象的地址
	Ptr operator->()
	{return &(operator*());
	}

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

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

private:
	Interator _it;	// 對正向迭代器進行封裝
};

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述
因為rbegin()是用end()來構造的,rend()使用begin()來構造的,所以反向迭代器的解引用需要創(chuàng)建一個臨時對象tmp,再返回*(--tmp)。

測試反向迭代器

在這里插入圖片描述

在這里插入圖片描述

注:以上的反向迭代器是用我們自己模擬實現(xiàn)的 list 來測試的!

👉總結👈

本篇博客主要講解了什么是優(yōu)先級隊列、優(yōu)先級隊列的使用和模擬實現(xiàn)、仿函數(shù)以及反向迭代器的實現(xiàn)等等。那么以上就是本篇博客的全部內容了,如果大家覺得有收獲的話,可以點個三連支持一下!謝謝大家!💖💝??

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


網(wǎng)站標題:【C++】優(yōu)先級隊列、仿函數(shù)和反向迭代器-創(chuàng)新互聯(lián)
當前鏈接:http://weahome.cn/article/csoehd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部