求一无序数组中第n大的数字 - 快速选择算法

时间:2023-12-23 10:41:50

逛别人博客的时候,偶然看到这一算法题,顺便用C++实现了一下。

最朴素的解法就是先对数组进行排序,返回第n个数即可。。

下面代码中用的是快速选择算法(不晓得这名字对不对)


 #include <vector>
#include <iostream>
#include <stdexcept>
#include <cstdio> const int QS_EERRMSG_LEN = ; /**
* 快速选择求无序数组中第n大的数字
* 因为select返回的是数组中对象的引用,
* 所以错误处理选择了异常
*/
template <typename T>
class QuickSelect
{
public:
typedef typename std::vector<T>::size_type size_type; /**
* 构造函数
* @brief QuickSelect
* @param array - vector类型的数组
*/
QuickSelect(std::vector<T>& array)
:m_array(array)
{} /**
* 选择第nth大的元素,
* 失败抛出std::out_of_range异常,
* 成功返回得到的元素
* @brief select
* @param nth
* @return
*/
const T&
select(size_type nth) throw(std::out_of_range)
{
//s_pos即第n大在排序之后数组中的下标
size_type s_pos = m_array.size() - nth; if(s_pos > m_array.size()){
char errmsg[QS_EERRMSG_LEN]; std::snprintf(errmsg, QS_EERRMSG_LEN, "Array access violation:{access:%ld range_length:%ld begin:%ld}.", nth, m_array.size(), );
std::out_of_range oor(errmsg);
throw oor;
} return this->select_pos(s_pos, , m_array.size());
} /**
* @brief select
* @param nth
* @param begin
* @param end
* @return
*/
const T&
select(size_type nth, size_type begin, size_type end) throw(std::out_of_range)
{
size_type array_size = m_array.size(); if(begin > array_size || end > array_size || end < begin){
char errmsg[QS_EERRMSG_LEN]; std::snprintf(errmsg, QS_EERRMSG_LEN, "The begin or end are not correct:{array_size:%ld begin:%ld end:%ld}.", array_size, begin, end);
std::out_of_range oor(errmsg);
throw oor;
} //要查找范围的长度
size_type range_length = end - begin + ; //要查找的第n大的元素在当前范围内的位置
size_type s_pos = range_length - nth; if(s_pos > range_length){
char errmsg[QS_EERRMSG_LEN]; std::snprintf(errmsg, QS_EERRMSG_LEN, "Array access violation:{access:%ld range_length:%ld begin:%ld}.", nth, range_length, begin);
std::out_of_range oor(errmsg);
throw oor;
}
else return this->select_pos(s_pos, begin, end);
}
private:
/**
* pos表示的是元素从begin开始的位置
* @brief select_pos
* @param pos
* @param begin
* @param end
* @return
*/
const T& select_pos(size_type pos, size_type begin, size_type end)
{
T& pivot = m_array[begin];
size_type swap_pos = begin; if(begin == end){
return m_array[pos];
} //进行一次快速排序
for(size_type i = begin + ;i < end;i ++){
if(m_array[i] < pivot){
std::swap(m_array[i], m_array[swap_pos ++]);
}
} //数组元素个数为奇数时多交换一次
if(m_array.size() % != ){
if(m_array[end - ] < pivot){
std::swap(m_array[end - ], m_array[swap_pos ++]);
}
} if(swap_pos - begin == pos){
return m_array[swap_pos];
}
else if(swap_pos - begin < pos){
return this->select_pos(pos, swap_pos + , end);
}
else{
return this->select_pos(pos, begin, swap_pos);
}
} /**
* @brief m_array
*/
std::vector<T>& m_array;
};


然后为了测试方便,实现vector输出,很简单的输出

 namespace std{

 template <typename T>
std::ostream& operator <<(std::ostream& out, const std::vector<T>& array)
{
for(auto e:array){
out << e <<" ";
}
out <<std::endl; return out;
} }

下面是测试用例

 int main()
{
std::vector<int> some{,,,,}; std::cout <<some; QuickSelect<int> qs(some); std::cout <<qs.select()<<std::endl; return ;
}

结果输出为

[root@localhost MYJOURNEY]# g++ quick_select.cpp -std=c++
[root@localhost MYJOURNEY]# ./a.out [root@localhost MYJOURNEY]#