【剑指offer】之最小的K个数-C++

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

题解

  1. 利用最大堆(时间复杂度nlogk)来解决,维护一个包含4个元素的最大堆,首先取待处理数组的前k个元素构成最大堆,然后遍历数组的剩余元素,若当前元素比最大堆的堆顶(最大的元素)小,则将当前元素与最大堆的堆顶互换,再重新生成新的最大堆。遍历完所有元素后所得的最大堆即为数组中最小的k个数。

代码1:不用algorithm中已有的堆处理算法

	#include<vector>
	using namespace std;
    void mySwap(int *pa, int *pb)//交换两个数
    {
        int tmp = *pa;
        *pa = *pb;
        *pb = tmp;
    }
    void downToMaxHeap(vector<int> &arr, int bgn, int end){//自顶向下移动构成局部最大堆,bgn表示当前父节点的位置
        int child;//子节点在数组中的索引
        int parent = bgn;//父节点在数组中的索引
        while((child = parent * 2 + 1) < end){//保证父节点的值大于子节点
            if((child < end - 1) && (arr[child] < arr[child+1]))
                ++child;//转移至右子节点
            if(arr[child] > arr[parent])
                mySwap(&arr[child], &arr[parent]);//若子节点的最大值大于父节点的值,将该子节点与父节点交换
            else
                break;
            parent = child;//父节点转移至与其交换的子节点位置
        }
    }
    void buildMaxHeap(vector<int> &arr, int bgn, int end){//将数组构造为最大堆
        if(bgn >= end - 1)
            return;
        int parent = end / 2 - 1;//父节点的最大索引   (right-1)/2,right是数组最后一个元素(最深层最右边的叶子节点)的索引
        while(parent >= 0){//父节点向前移动直至根节点
            downToMaxHeap(arr, parent, end);//对每个父节点构造局部最大堆
            --parent;
        }
    }
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
    {
        int length = input.size();
        vector<int> result;
        if(length <=0 || k <= 0 || length < k)
            return result;
        result.assign(input.begin(), input.begin()+k);//取输入数组的前k个元素
        buildMaxHeap(result, 0, result.size());//构造最大堆
        for(int i = k; i < length; ++i){
            if(input[i] < result[0]){//若该元素小于堆顶,则与堆顶交换
                mySwap(&input[i], &result[0]);
                downToMaxHeap(result, 0, result.size());//从堆顶自上而下构造新的最大堆
            }
        }
        return result;
    }

代码2:利用algorithm中已有的堆排序算法

	#include<vector>
	#include<algorithm>
	using namespace std;
	vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        int length = input.size();
        vector<int> result;
        if(length <=0 || k <= 0 || length < k)
            return result;
        result.assign(input.begin(), input.begin()+k);
        //for(int i = 0; i < k; ++i)
        //    result.push_back(input[i]);
        make_heap(result.begin(), result.end());
        for(int i = k; i < length; ++i){
            if(input[i] < result[0]){
                pop_heap(result.begin(), result.end());
                result.pop_back();
                result.push_back(input[i]);
                push_heap(result.begin(), result.end());
            }
        }
        sort_heap(result.begin(), result.end());
        return result;
    }
  1. 利用快速排序的思想,使得比第k个元素小的元素都位于其左边,比第k个元素大的元素都位于其右边,则数组的前k个数即为最小的k个数

代码:

	#include<vector>
	using namespace std;
    int Partition(vector<int> &input, int begin, int end){
        int low = begin;
        int high = end;

        int pivot = input[low];//选第一个元素为基准
        while(low < high){
            while(low < high && pivot <= input[high])
                --high;
            input[low] = input[high];
            while(low < high && pivot >= input[low])
                ++low;
            input[high] = input[low];
        }
        input[low] = pivot;
        return low;//返回移动后基准的下标
    }
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k){
        int length = input.size();
        //vector<int> result;
        if(length <= 0 || k <= 0 || length < k)
            return vector<int>();
        int start = 0;
        int end = length - 1;
        int index = Partition(input, start, end);

        while(index != (k-1)){
            if(index > k - 1){
                end = index - 1;
                index = Partition(input, start, end);
            }
            else{
                start = index + 1;
                index = Partition(input, start, end);
            }
        }
        vector<int> result(input.begin(), input.begin()+k);
        return result;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值