由Topk到STL源码(快排优化)

一切从一个力扣题开始:最小的k个数

TopK:输入整数数组 arr ,找出其中最小的 k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

看到有人说STL提供了算法可以解决这个题,自己实现了一下,于是有了这两种解法。

秒杀法

调用STL提供的nth_element方法,该方法让第 n 个位置上的元素就位,且所有在位置 n 之前的元素都小于或等于它,所有在位置 n 之后的元素都大于或等于它。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(!k){
            arr.resize(0);
            return arr;
        }
        
        nth_element(arr.begin(),arr.begin()+k,arr.end());

        arr.resize(k);

        return arr;
    }
};

在这里插入图片描述

手写法

感觉时间还可以提,于是决定手写nth_element方法(但是最终用时和内存消耗都大于秒杀法)。

nth_element方法的主要思想是快排划分+三数取中+插排,原始的快排每次快排后都要分别地对左右两个区间进行快排,但是此题只要找前k个数,于是只要把第k个排好即可,因此我们可以略去某些区间的排序。

三数取中是对快排的优化,取当前区间的左、右、中三个数的中位数,把它作为基准元素快排,目的是让左右两个区间尽量均衡,减小最坏情况的用时,让快排的耗时更趋向平均。

插排也是对快排的优化,快排当区间小到一定程度的时候用插入排序会快一些。

此方法使时间复杂度趋于o(n)

class Solution {
public:
    typedef vector<int>::iterator vii;
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(!k){
            arr.resize(0);
            return arr;
        }

        my_nth_element(arr.begin(),arr.begin()+k,arr.end()-1);
        arr.resize(k);

        return arr;
    }

    void my_nth_element(vii left,vii nth,vii right){
        while(right-left>32){
            vii cur=quickSort(left,right,*mid(left,left+(right-left)/2,right-1));

            if(cur<=nth){
                left=cur;
            }else{
                right=cur;
            }
        }
        
        insertSort(left,right);
    }

    vii quickSort(vii left,vii right,int pivot){
        while(1){
            while(*left<pivot)left++;
            
            while(pivot<*right)right--;

            if(!(left<right))return left;
            swap(left,right);
            ++left;
        }

    }

    void swap(vii left,vii right){
        *left=(*left)^(*right);
        *right=(*left)^(*right);
        *left=(*left)^(*right);
    }

    vii mid(vii left,vii m,vii right){
        if(*left<*m){
            if(*right<*m){
                if(*left<*right)return right;//left right m
                return left;//right left m
            }
            return m;
        }else{//m left
            if(*right>*m){
                if(*right<*left)return right;//m right left
                return left;//m left right
            }
            return m;
        }
    }

    void insertSort(vii left,vii right){
        while(left<=right){
            vii temp=left+1;
            while(temp!=right+1){
                if(*temp<*left){
                    swap(temp,left);
                }
                temp++;
            }

            left++;
        }
    }
};

在这里插入图片描述
总结:虽然最后没有打过STL提供的方法,但是我对快排的理解更深了一步,能自己实现还是很开心的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值