快速排序与查找第K大的数
今天种树的任务是复习一下快排,顺便实现了一下查找第K大的数的算法。
快速排序
非常常见也非常经典的算法,随便给你们找一篇。
https://www.cnblogs.com/ayqy/p/3862938.html
快速排序C++实现
我觉得我写的又短又好理解又好记忆(自恋ing~)
#include <iostream>
#include <stack>
#include <time.h>
#include <stdlib.h>
using namespace std;
int arrs[] = { 23, 476, 65, 12, 3, 925, 8, 98, 76, 345, 90, 21, 762, 75, 34, 123, 61 };
int length = sizeof(arrs) / sizeof(arrs[0]);
void QuickSort(int* istart,int* iend)
{
int* left = istart;
int* right = iend;
while(left != right)
{
while(*left <= *right && left < right)
right--;
swap(*left, *right);
while(*left <= *right && left < right)
left++;
swap(*left, *right);
}
if (left > istart)
QuickSort(istart, left - 1);
if (left < iend)
QuickSort(left + 1, iend);
}
int main()
{
QuickSort(arrs, arrs + length - 1);
for(int i = 0;i<length;i++)
cout<<arrs[i]<<" ";
return 0;
}
查找第K大的数
今天看书说快排可以实现查找第K大的数,突然觉得很牛b。赶紧来试试。贴两篇博文
https://www.cnblogs.com/zhjp11/archive/2010/02/26/1674227.html(摘要如下)
所谓“第(前)k大数问题”指的是在长度为n(n>=k)的乱序数组中S找出从大到小顺序的第(前)k个数的问题。
解法1: 我们可以对这个乱序数组按照从大到小先行排序,然后取出前k大,总的时间复杂度为O(nlogn + k)。
解法2: 利用选择排序或交互排序,K次选择后即可得到第k大的数。总的时间复杂度为O(nk)
解法3: 利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
1. Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
2. Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)
解法4: 二分[Smin,Smax]查找结果X,统计X在数组中出现,且整个数组中比X大的数目为k-1的数即为第k大数。时间复杂度平均情况为O(nlogn)
解法5:用O(4n)的方法对原数组建最大堆,然后pop出k次即可。时间复杂度为O(4n + klogn)
解法6:维护一个k大小的最小堆,对于数组中的每一个元素判断与堆顶的大小,若堆顶较大,则不管,否则,弹出堆顶,将当前值插入到堆中。时间复杂度O(n * logk)
解法7:利用hash保存数组中元素Si出现的次数,利用计数排序的思想,线性从大到小扫描过程中,前面有k-1个数则为第k大数,平均情况下时间复杂度O(n)
作者给出7种方法,并计算其时间复杂度。明显看到第3种快排和第7种计数排序的时间复杂度”较低“。
对于快排,平均情况应该是每次舍去一半的数组,所以时间复杂度应该是n+n/2+n/4+n/8…也就是O(2n)。
而所谓的第7种方法选择排序,时间复杂度为O(N+M),M为数组元素的取值范围,对于通常情况下这并不是一个解决第K大问题的好方法。
再来一篇博文
https://blog.csdn.net/apolo_/article/details/50829683
解决思路是从始至终只对最大的K个数进行排序,与全排序相比,减少了很多无用操作。
第二种解法是先把前k个元素读入到数组并对齐排序(递减的循序),接着将剩下的元素在逐个读入。最后在遍历完数组后直接输出第k个元素。
这算法依赖于K的大小,当k很小时,性能优于快排。当你不知道K的取值范围时,快排是更好的选择。
快排思想实现查找第K大的数
利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于X,Sb中元素小于X。这时有三种情况:
1. Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
2. Sa中元素的个数大于k,则返回Sa中的第k大数。时间复杂度近似为O(n)
3. X排序等于K,返回X
C++实现
只需要在上面快排的算法上做一点小小的改动
#include <iostream>
#include <stack>
#include <time.h>
#include <stdlib.h>
using namespace std;
int arrs[] = { 23, 476, 65, 12, 3, 925, 8, 98, 76, 345, 90, 21, 762, 75, 34, 123, 61 };
int length = sizeof(arrs) / sizeof(arrs[0]);
int TopKSort(int* istart,int* iend, int k)
{
int* left = istart;
int* right = iend;
while(left != right)
{
while(*left >= *right && left < right)
right--;
swap(*left, *right);
while(*left >= *right && left < right)
left++;
swap(*left, *right);
}
if (left - istart + 1 == k)
return *left;
if (left - istart + 1 > k)
return TopKSort(istart, left - 1, k);
if (left - istart + 1 < k)
return TopKSort(left + 1, iend, k - (left - istart + 1));
}
int main()
{
for(int i = 0;i<length;i++)
cout<<TopKSort(arrs, arrs + length - 1, i + 1)<<" ";
return 0;
}
运行结果:
925 762 476 345 123 98 90 76 75 65 61 34 23 21 12 8 3
学算法有瘾,为什么我以前没发现这么快乐的事情呢❓呜呜,种树+4