逛别人博客的时候,偶然看到这一算法题,顺便用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 }
结果输出为
[[email protected] MYJOURNEY]# g++ quick_select.cpp -std=c++11 [[email protected] MYJOURNEY]# ./a.out 1 2 3 5 4 4 [[email protected] MYJOURNEY]#
时间: 2024-10-06 20:21:22