求一无序数组中第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 }

结果输出为

[[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

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

无序数组中第Kth大的数

题目:找出无序数组中第Kth大的数,如{63,45,33,21},第2大的数45. 输入: 第一行输入无序数组,第二行输入K值. 该是内推滴滴打车时(2017.8.26)的第二题,也是<剑指offer>上最小k个数的变形.当时一看到题,这个不是用快排吗?然后就写了,结果始终没有通过,遗憾的超时提交了.错误点主要在于,这里求的是第K大的数,而若是我们使用K去判断快排得到的下标,得到的是第K个数(等同于排序以后从左往右下标为K-1),而题中隐藏的意思等同于排序以后从 右往左数第K个数.所写在写代码

[LeetCode] Kth Largest Element in an Array 数组中第k大的数字

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element. For example, Given [3,2,1,5,6,4] and k = 2, return 5. Note: You may assume k is always valid, 1 ≤ k ≤ array'

查找无序数组中第K大的数

思路: 利用快速排序的划分思想 可以找出前k大数,然后不断划分 直到找到第K大元素 代码: #include <iostream> #include <algorithm> #include <cstdio>5 using namespace std; int findK(int left, int right, int arr[], int k) { if(left >= right) return arr[left]; int first = left, la

快速查找无序数组中的第K大数?

1.题目分析: 查找无序数组中的第K大数,直观感觉便是先排好序再找到下标为K-1的元素,时间复杂度O(NlgN).在此,我们想探索是否存在时间复杂度 < O(NlgN),而且近似等于O(N)的高效算法. 还记得我们快速排序的思想麽?通过“partition”递归划分前后部分.在本问题求解策略中,基于快排的划分函数可以利用“夹击法”,不断从原来的区间[0,n-1]向中间搜索第k大的数,大概搜索方向见下图: 2.参考代码: 1 #include <cstdio> 2 3 #define sw

[经典算法题]寻找数组中第K大的数的方法总结

[经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26 字体:[大 中 小] 打印复制链接我要评论 今天看算法分析是,看到一个这样的问题,就是在一堆数据中查找到第k个大的值. 名称是:设计一组N个数,确定其中第k个最大值,这是一个选择问题,当然,解决这个问题的方法很多,本人在网上搜索了一番,查找到以下的方式,决定很好,推荐给大家. 所谓"第(前)k大数问题"指的是在长度为n(n>=k)的乱序数组中S找出从大到小顺序的第(前)k个数的问题.

在O(n)时间复杂度内求无序数组中任意两个元素的最大差值,以及存在的组数

题目描述: 求无序数组中任意两个元素的最大差值,以及存在最大差值的组别数. 输入: 输入包含两行,第一行输入一个整数n:第二行n个正整数,用空格隔开. 输出: 输出为一行,包含最大差值,以及存在组别数. 样例输入: 4 4  1  2  1 输出: 3  2 一种实现代码如下(Java版): 1 import java.util.Scanner; 2 /** 3 * 在O(n)时间复杂度内求无序数组中任意两个元素的最大差值,以及存在的组数 4 * @author JiaJoa 5 * 6 */

小米笔试题:无序数组中最小的k个数

题目描述 链接:https://www.nowcoder.com/questionTerminal/ec2575fb877d41c9a33d9bab2694ba47?source=relative 来源:牛客网 无序数组中最小的k个数 对于一个无序数组,数组中元素为互不相同的整数,请返回其中最小的k个数,顺序与原数组中元素顺序一致. 给定一个整数数组A及它的大小n,同时给定k,请返回其中最小的k个数. 测试样例: [1,2,4,3],4,2 返回:[1,2] 代码 需要保留K个较小的元素,可以删

找一个数组中第K大的数

快排思想,选取序列的一个key进行划分,小于key的分在左边,大于key的在右边,key的位置-low+1就是最后一个元素是key的序列中元素的数量,当元素数量大于K时,就在左半部分递归找,等于时 arr[key]就是第K 大的元素,小于K时,在右边递归找第k-num大的元素 /** * 文件名:FindK.java * 时间:2014年11月7日上午11:23:43 * 作者:修维康 */ package chapter7; /** * 类名:FindK 说明:找到一个数组中第K大的元素 */

挑战面试编程:查找数组中第k大的数

查找数组中第k大的数 问题: 查找出一给定数组中第k大的数.例如[3,2,7,1,8,9,6,5,4],第1大的数是9,第2大的数是8-- 思路: 1. 直接从大到小排序,排好序后,第k大的数就是arr[k-1]. 2. 只需找到第k大的数,不必把所有的数排好序.我们借助快速排序中partition过程,一般情况下,在把所有数都排好序前,就可以找到第k大的数.我们依据的逻辑是,经过一次partition后,数组被pivot分成左右两部分:S左.S右.当S左的元素个数|S左|等于k-1时,pivo