数组------最小的k个数

问题描述: 求数组中的最小k个数。
分析:
<1>使用线性选择算法来快速实现(O(n)),具体来说就是借助快速排序的思想,把数组分为比主元v小的部分s1,和比主元v大的部分s2。
1.若k <= |s1|,则第k小的元素在s1,就递归的对s1进行相同的操作。
2.若k = |s1| + 1,那就表示主元就是第k小的元素,那就结束算法,此时数组的前k个元素就是要找的最小k个元素。
3.若k > |s1| + 1,则第k小的元素在s2中,那就递归的对s2进行相同操作。
4.若数组太短,就直接使用插入排序即可。

void QuickSelect(int arr[], int k, int left, int right)
{
    if(arr == NULL || left < 0 || right < left)
    {
        cout << "something error!" << endl;
        exit(1)
    }

    if(left <= right - 3)
    {
        int i, j;
        int mid = left + (right - left)/2 ;
        SortFirstMidLast(arr, left, mid, right);
        swap(arr[mid], arr[right - 1]);
        int index = right - 1;
        int indexValue = arr[index];
        i = left + 1;
        j = right - 2;
        for(;;)
        {
            while(arr[i] < indexValue)
                i += 1;
            while(arr[j] > indexValue)
                j -= 1;
            if(i < j)
                swap(arr[i], arr[j]);
            else 
                break;
        }
        swap(arr[i], arr[index]);
        if(k <= i)
            QuickSelect(arr, k, left, i - 1);
        else if(k > i + 1)
            QuickSelect(arr, k, i + 1, right);
    }
    else
        InsertSort(arr, left, right);
}

void SortFirstMidLast(int arr[], int first, int mid, int end)
{
    if(arr[first] > arr[mid])
        swap(arr[first], arr[mid]);
    if(arr[mid] > arr[end])
        swap(arr[mid], arr[end]);
    if(arr[first] > arr[mid])
        swap(arr[first], arr[mid]);
}

void InsertSort(int arr[], int left, int right)
{
    for(int un = left + 1; un <= right; un++)
    {
        int tmp = arr[un];
        int lcoal = un;
        while(local > 0 && arr[local - 1] > tmp)
        {
            arr[local] = arr[local - 1];
            local--;
        }
        arr[local] = tmp;
    }
}

<2> 上述方法需要改变原来的数组,若要使得原来数组不变,则可以使用一个大小为k的最大堆,先取数组前k个数入堆并排序,然后从数组的k+1个元素开始,每次与堆顶元素p进行比较,若arr[k+1] >= p,则继续比较下一个元素。若k[k+1] < p,则删除堆顶元素,并将arr[k+1]插入堆中并将堆排序,然后继续比较arr[k+2]…(此方法不会改变原始数组,并且适合处理海量数据。其时间复杂度为O(nlogk)。//实现时,可以用一个长为k的数组来实现一个最大堆)

void SortByHeap(int *arr, int *heap, int len)
{
    if(len < N)
    {
        cout << "the array too short!" << endl;
        return;
    }
    for(int i = 0; i < N; i++)  //将数组前N个元素放入堆中
        heap[i] = arr[i];

    for(int index = N/2; index >= 0; index--)  //堆排序
    {
        heapRebuild(index, heap, N);
    }
    for(int i = N; i < len; i++)  //从第N+1个元素开始一一对比
    {
        if(arr[i] < heap[0])
        {
            heap[0] = arr[i];
            for(int index = N/2; index >= 0; index--)  //堆排序
            {
                heapRebuild(index, heap, N);
            }
        }
    }
 }

void heapRebuild(int rootIndex, int *arr, int count) //创建大顶堆
{
    if(rootIndex * 2 + 1 <= count - 1)  //有左孩子结点
    {
        int largerChildIndex = rootIndex * 2 + 1;
        if(largerChildIndex + 1 <= count - 1)  //有右孩子结点
        {//获得孩子结点中的较大的那个的下标
            int rightIndex = largerChildIndex + 1;
            if(arr[rightIndex] > arr[largerChildIndex])
                largerChildIndex = rightIndex;  
        }
        if(arr[rootIndex] < arr[largerChildIndex])
        {//若孩子结点中较大的那个大于了根节点,那就交换它们,并递归的对每一层进行比较
            swap(arr[rootIndex], arr[largerChildIndex]);
            heapRebuild(largerChildIndex, arr, count);   //对每一层都进行相同操作
        }
    }
}

<3> 使用基于红黑树的multiset来实现。

typedef multiset<int, greate<int> > intSet;
typedef multiset<int, greate<int> >::iterator setIterator;

void getLeastNumbers(const vector<int>& data, intSet& leastNumbers, int k){
    leastNumbers.clear();
    if(k < 1 || data.size() < k)
        return ;
    vector<int>::const_iterator iter = data.begin();
    for(; iter != data.end(); ++iter){
        if(leastNumbers.size() < k){
            leastNumbers.insert(*iter);
        }else{
            setIterator iterGreat = leastNumbers.begin();
            if(*iter < *(leastNumbers.begin())){
                leastNumber.erase(iterGreat);//删除iterGreat位置上的元素
                leastNumber.insert(*iter);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值