快速排序与查找第K大的数

快速排序与查找第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(n
k)
解法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(4
n)的方法对原数组建最大堆,然后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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值