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

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

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

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

 
  
  1 #include <vector>
  2 #include <iostream>
  3 #include <stdexcept>
  4 #include <cstdio>
  5 
  6 const int QS_EERRMSG_LEN = 256;
  7 
  8 
  9 /**
 10  * 快速选择求无序数组中第n大的数字
 11  * 因为select返回的是数组中对象的引用,
 12  * 所以错误处理选择了异常
 13  */
 14 template <typename T>
 15 class QuickSelect
 16 {
 17 public:
 18     typedef typename std::vector<T>::size_type size_type;
 19 
 20     /**
 21      * 构造函数
 22      * @brief QuickSelect
 23      * @param array - vector类型的数组
 24      */
 25     QuickSelect(std::vector<T>& array)
 26         :m_array(array)
 27     {}
 28 
 29     /**
 30      * 选择第nth大的元素,
 31      * 失败抛出std::out_of_range异常,
 32      * 成功返回得到的元素
 33      * @brief select
 34      * @param nth
 35      * @return
 36      */
 37     const T&
 38     select(size_type nth) throw(std::out_of_range)
 39     {
 40         //s_pos即第n大在排序之后数组中的下标
 41         size_type s_pos = m_array.size() - nth;
 42 
 43         if(s_pos > m_array.size()){
 44             char errmsg[QS_EERRMSG_LEN];
 45 
 46             std::snprintf(errmsg, QS_EERRMSG_LEN, "Array access violation:{access:%ld range_length:%ld begin:%ld}.", nth, m_array.size(), 0);
 47             std::out_of_range oor(errmsg);
 48             throw oor;
 49         }
 50 
 51         return this->select_pos(s_pos, 0, m_array.size());
 52     }
 53 
 54     /**
 55      * @brief select
 56      * @param nth
 57      * @param begin
 58      * @param end
 59      * @return
 60      */
 61     const T&
 62     select(size_type nth, size_type begin, size_type end) throw(std::out_of_range)
 63     {
 64         size_type array_size = m_array.size();
 65 
 66         if(begin > array_size || end > array_size || end < begin){
 67             char errmsg[QS_EERRMSG_LEN];
 68 
 69             std::snprintf(errmsg, QS_EERRMSG_LEN, "The begin or end are not correct:{array_size:%ld begin:%ld end:%ld}.", array_size, begin, end);
 70             std::out_of_range oor(errmsg);
 71             throw oor;
 72         }
 73 
 74         //要查找范围的长度
 75         size_type range_length = end - begin + 1;
 76 
 77         //要查找的第n大的元素在当前范围内的位置
 78         size_type s_pos = range_length - nth;
 79 
 80         if(s_pos > range_length){
 81             char errmsg[QS_EERRMSG_LEN];
 82 
 83             std::snprintf(errmsg, QS_EERRMSG_LEN, "Array access violation:{access:%ld range_length:%ld begin:%ld}.", nth, range_length, begin);
 84             std::out_of_range oor(errmsg);
 85             throw oor;
 86         }
 87         else
 88 
 89         return this->select_pos(s_pos, begin, end);
 90     }
 91 private:
 92     /**
 93      * pos表示的是元素从begin开始的位置
 94      * @brief select_pos
 95      * @param pos
 96      * @param begin
 97      * @param end
 98      * @return
 99      */
100     const T& select_pos(size_type pos, size_type begin, size_type end)
101     {
102         T& pivot = m_array[begin];
103         size_type swap_pos = begin;
104 
105         if(begin == end){
106             return m_array[pos];
107         }
108 
109         //进行一次快速排序
110         for(size_type i = begin + 1;i < end;i ++){
111             if(m_array[i] < pivot){
112                 std::swap(m_array[i], m_array[swap_pos ++]);
113             }
114         }
115 
116         //数组元素个数为奇数时多交换一次
117         if(m_array.size() % 2 != 0){
118             if(m_array[end - 1] < pivot){
119                 std::swap(m_array[end - 1], m_array[swap_pos ++]);
120             }
121         }
122 
123         if(swap_pos - begin == pos){
124             return m_array[swap_pos];
125         }
126         else if(swap_pos - begin < pos){
127             return this->select_pos(pos, swap_pos + 1, end);
128         }
129         else{
130             return this->select_pos(pos, begin, swap_pos);
131         }
132     }
133 
134     /**
135      * @brief m_array
136      */
137     std::vector<T>& m_array;
138 };
 
  

 

 
 

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

 1 namespace std{
 2 
 3 template <typename T>
 4 std::ostream& operator <<(std::ostream& out, const std::vector<T>& array)
 5 {
 6     for(auto e:array){
 7         out << e <<" ";
 8     }
 9     out <<std::endl;
10 
11     return out;
12 }
13 
14 }

下面是测试用例

 1 int main()
 2 {
 3     std::vector<int> some{1,2,3,5,4};
 4 
 5     std::cout <<some;
 6 
 7     QuickSelect<int> qs(some);
 8 
 9     std::cout <<qs.select(2)<<std::endl;
10 
11     return 0;
12 }

结果输出为

[root@localhost MYJOURNEY]# g++ quick_select.cpp -std=c++11
[root@localhost MYJOURNEY]# ./a.out 
1 2 3 5 4 
4
[root@localhost MYJOURNEY]#

 



 

转载于:https://www.cnblogs.com/araraloren/p/4436274.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值