1. 程式人生 > >第k大(小)數,尋找最小的k個數(進一步要求順序與原陣列中元素順序一致)

第k大(小)數,尋找最小的k個數(進一步要求順序與原陣列中元素順序一致)

215. Kth Largest Element in an Array(leetcode)

快排思路求解,ac程式碼

class Solution {
public:

    int partion(vector<int> &nums, int left, int right)
    {
        int pivot = nums[left];
        while (left < right){
            while (left < right && nums[right] >= pivot)
                --right;
            nums[left] = nums[right];

            while
(left < right && nums[left] < pivot) ++left; nums[right] = nums[left]; } nums[left] = pivot; return left; } int qsort(vector<int> &nums, int left, int right, int k){ if (left > right) return
-1; int pos = partion(nums, left, right); int leftSize = pos - left + 1; if (leftSize == k){ return nums[pos]; } else if (leftSize < k){ return qsort(nums, pos + 1, right, k - leftSize); } else{ return qsort(nums, left, pos - 1
, k); } } int findKthLargest(vector<int>& nums, int k) { int len = nums.size(); return qsort(nums, 0, len - 1, len - k + 1); // 第len - k + 1 從小到大 } };

我的ac程式碼:

class Solution {
public:

    void down2up(vector<int> &bigheap, int no,int val)
    {
        bigheap[no] = val;

        // 從根節點調整到 子節點
        int tmpno = no;
        while (tmpno > 1){

            int fa = tmpno / 2;
            if (fa >= 1)
            {
                if (bigheap[tmpno] > bigheap[fa])
                {
                    int tmpVal = bigheap[tmpno];
                    bigheap[tmpno] = bigheap[fa];
                    bigheap[fa] = tmpVal;

                    tmpno = fa;
                }
                else{
                    break;
                }
            }
            else{
                break;
            }
        }
    }

    void up2downAdjust(vector<int> &nums,int n) // 下標範圍是 1 - n 
    {
        int fa = 1;
        while (fa <= n){

            int lson = 2 * fa;
            int rson = 2 * fa + 1;

            if (lson > n && rson > n){
                return;
            }
            else if (lson <= n && rson > n)
            {
                if (nums[lson] > nums[fa]){
                    int tmp = nums[fa];
                    nums[fa] = nums[lson];
                    nums[lson] = tmp;

                    fa = lson;
                }
                else{
                    return;
                }
            }
            else if (lson > n && rson <= n)
            {
                if (nums[rson] > nums[fa]){
                    int tmp = nums[fa];
                    nums[fa] = nums[rson];
                    nums[rson] = tmp;

                    fa = rson;
                }
                else{
                    return;
                }
            }
            else{

                if (nums[fa] >= nums[lson] && nums[fa] >= nums[rson])
                    return;
                else{

                    if (nums[lson] >= nums[rson]){
                        int tmp = nums[fa];
                        nums[fa] = nums[lson];
                        nums[lson] = tmp;

                        fa = lson;
                    }
                    else{
                        int tmp = nums[fa];
                        nums[fa] = nums[rson];
                        nums[rson] = tmp;

                        fa = rson;
                    }
                }
            }

        }
    }

    // O(nlogk)的時間複雜度
    int findKthLargest(vector<int>& nums, int k) {
        int len = nums.size();

        int n = len - k + 1; // 第len - k + 1 從小到大 構建大根堆

        // 建立大根堆
        vector<int> bigheap(n + 1);
        for (int i = 0; i < n; i++)
        {
            down2up(bigheap, i + 1, nums[i]);
        }

        for (int i = n; i < len; i++) //後續元素組個比較
        {
            if (nums[i] < bigheap[1]){  // 比堆頂小 與棧頂交換 然後調整堆
                bigheap[1] = nums[i]; 
                up2downAdjust(bigheap, n);
            }
        }

        return bigheap[1];  //返回堆頂
    }
};

分治思想,借用快速排序思路

一趟排序,num個最小的數 在做部分
* 如果 num = k , 則結束
* 如果 num < k , 則在右部分找到 k - num個最小的數
* 如果 num > k , 則繼續在左部分找這k個數

/**************************************
    尋找最小的k個數
**************************************/
#include <cstdio>
#include <iostream>
using namespace std;

int partion(int a[], int low, int high)
{
    int pivot = a[low];
    while (low < high)
    {
        while (low < high && a[high] >= pivot)
            --high;
        a[low] = a[high];
        while (low < high && a[low] < pivot)
            ++low;
        a[high] = a[low];
    }
    a[low] = pivot;  // 此時low 就是pivot 最終的位置
    return low; // 返回最終確定的位置
}

/* 將k個最小的數放到 a[0]-a[k-1]中 */
void select_k_min(int a[], int k , int left , int right)
{
    if(left >= right)
        return;
    if(k <= 0 || k > right - left)
        return;
    int pos = partion(a, left, right);
    int num = pos - left + 1; // 左部分的個數,此時得到了最小的num個數
    if(num == k)    // 找到
        return;
    else if(num < k) {  //需要在右部分,尋找最小的k-num個數
        select_k_min(a, k - num , pos + 1, right);
    }else{  // 需要繼續左部分操作,找到最小的k個數
        select_k_min(a, k, left, pos -1 );
    }
}

int main()
{
    int a[10] = {3,10,9,8,5,2,6,7,4,1 };
    int n = 10;
    int k = 6;  
    int left = 1;
    int right = n -1;
    select_k_min(a, k , left , right);
    for (int i = left; i < left + k - 1; i++)
        printf("%d ", a[i]);
    printf("%d\n", a[left + k - 1]);
    return 0;
}

注: 以上方法是不穩定的,即選擇的最下的k個數不一定保持原來陣列中的相對順序。

堆排序

堆排序是不穩定的排序
每次調整的時間複雜度是O(h)
堆排序的事件複雜度在最好,平均,最壞的情況下都是O(nlogn)

利用堆排序可以求出最小的k個數

class KthNumbers {
public:

    void BuildHeap(vector<int> &v, int n)
    {
        if (n <= 0)
            return;

        int index = n / 2;
        for (int i = index; i >= 1; i--) // 從i到1依次調整
            AdjustHeap(v, i, n);
    }

    //調整最大堆    
    void AdjustHeap(vector<int> &v, int i, int n)
    {
        while (i < n)
        {
            int iVal = v[i - 1];
            int left = 2 * i - 1;
            int right = 2 * i + 1 - 1;

            // 取得左右孩子的值,因為v中的數不等,所以初始就為iVal
            int leftVal = iVal;
            if (left < n)
                leftVal = v[left];
            int rightVal = iVal;
            if (right < n)
                rightVal = v[right];

            if (leftVal >= iVal && rightVal >= iVal)
                return;

            if (leftVal == iVal && rightVal == iVal) 
            {
                return;
            }
            else if (leftVal == iVal)
            {
                // 右邊有效
                if (rightVal < iVal){
                    swap(v[i-1], v[right]);

                    i = right + 1;
                }
            }
            else if (rightVal == iVal)
            {
                // 左邊有效
                if (leftVal < iVal){
                    swap(v[i-1], v[left]);

                    i = left + 1;
                }
            }
            else{
                if (leftVal < rightVal)
                {
                    if (leftVal < iVal){ //調左邊
                        swap(v[i-1], v[left]);

                        i = left + 1;
                    }
                }
                else{
                    if (rightVal < iVal){ // 調右邊
                        swap(v[i-1], v[right]);

                        i = right + 1;
                    }
                }
            }

        }
    }

    vector<int> findKthNumbers(vector<int> A, int n, int k) {
        // write code here
        if (k <= 0)
            return vector<int>();
        if (k >= n || n < 1) {
            return A;
        }

        BuildHeap(A, n);

        vector<int> rs;
        for (int i = 0; i < k; i++)
        {
            int val = A[0];
            A[0] = A[n - 1 - i];
            A[n - 1 - i] = val;

            AdjustHeap(A, 1, n - 1 - i);
            printf("%d,", val);
            rs.push_back(val);
        }

        return rs;
    }

};

尋找最小的k個數(進一步要求順序與原陣列中元素順序一致)

題目描述

對於一個無序陣列,陣列中元素為互不相同的整數,請返回其中最小的k個數,順序與原陣列中元素順序一致。
給定一個整數陣列A及它的大小n,同時給定k,請返回其中最小的k個數。
測試樣例:
[1,2,4,3],4,2
返回:[1,2]

在上面的基礎上,利用pair< int,int> 儲存結構後,再按照次序排序,得到最終的結果

利用堆排序,ac程式碼,同理可用快排思路

class KthNumbers {
public:

    void BuildHeap(vector<pair<int, int>> &vp, int n)
    {
        if (n <= 0)
            return;

        int index = n / 2;
        for (int i = index; i >= 1; i--) // 從i到1依次調整
            AdjustHeap(vp , i, n);
    }

    //調整最大堆    
    void AdjustHeap(vector<pair<int, int>> &vp, int i, int n)
    {
        while (i < n)
        {
            int iVal = vp[i - 1].first;
            int left = 2 * i - 1;
            int right = 2 * i + 1 - 1;

            // 取得左右孩子的值,因為v中的數不等,所以初始就為iVal
            int leftVal = iVal;
            if (left < n)
                leftVal = vp[left].first;
            int rightVal = iVal;
            if (right < n)
                rightVal = vp[right].first;

            if (leftVal >= iVal && rightVal >= iVal)
                return;

            if (leftVal == iVal && rightVal == iVal)
            {
                return;
            }
            else if (leftVal == iVal)
            {
                // 右邊有效
                if (rightVal < iVal){
                    swap(vp[i - 1], vp[right]);

                    i = right + 1;
                }
            }
            else if (rightVal == iVal)
            {
                // 左邊有效
                if (leftVal < iVal){
                    swap(vp[i - 1], vp[left]);

                    i = left + 1;
                }
            }
            else{
                if (leftVal < rightVal)
                {
                    if (leftVal < iVal){ //調左邊
                        swap(vp[i - 1], vp[left]);

                        i = left + 1;
                    }
                }
                else{
                    if (rightVal < iVal){ // 調右邊
                        swap(vp[i - 1], vp[right]);

                        i = right + 1;
                    }
                }
            }

        }
    }

    vector<int> findKthNumbers(vector<int> A, int n, int k) {
        // write code here
        if (k <= 0)
            return vector<int>();
        if (k >= n || n < 1) {
            return A;
        }

        vector<pair<int, int>> vp(n);// 構造<數字,下標>的pair陣列,方便結果輸出
        for (int i = 0; i < n; i++)
        {
            pair<int, int> pairtmp(A[i], i);
            vp[i] = pairtmp;
        }

        BuildHeap(vp, n); //建立堆

        vector<pair<int, int>> rs;   //依次取出最小的k個數
        for (int i = 0; i < k; i++)
        {
            pair<int,int> val = vp[0];
            vp[0] = vp[n - 1 - i];
            vp[n - 1 - i] = val;

            AdjustHeap(vp, 1, n - 1 - i);
            //printf("%d,", val.first);
            rs.push_back(val);
        }

        // 按照原始序號對結果排序
        sort(rs.begin(), rs.end(), 
            [](pair<int, int> p1, pair<int, int> p2){ // c++11 , lambda
            return p1.second < p2.second;
        });
        vector<int> ans;
        for (pair<int, int> pt : rs)    //c++11 for range
            ans.push_back(pt.first);
        return ans;
    }

};