1. 程式人生 > >[C++日常小題] 尋找第k大數字(numberk)

[C++日常小題] 尋找第k大數字(numberk)

Description

經過長時間的籌備工作,在Jourk,Ronny,Plipala,阿長,阿沈等人的努力下,DM實驗室建立起自己的系列網站,其中包括三個大板塊:DMOJ首頁、DMOJ論壇、DMOJ部落格。
作為一個大型的網站,資料的加工處理需要用到很多算法系統模組,通過這些模組對系統主資料庫進行修改。因此,在構建DMOJ系列網站的過程中,編寫了一個龐大的資料統計系統,其中包括一個尋找第k大數字的模組,對於一個數據庫來說,這樣一個模組的重要性不容置疑。但是,由於在修改網站的過程中,這個模組被不慎刪掉了,DMOJ找到了聰明的你,希望你能幫DMOJ重寫這個模組。
對於輸入資料,你將按資料要求找出n個數字中的第k大數字。
關於第k大數字的解釋:對於一個數字序列,計算該數字序列第k大數字,一種可行但效率不高的方法是,首先剔除數字序列中重複的數字,然後通過某種排序演算法,獲得剔除重複數字後的數字序列從大到小排序的數字序列,然後取從大到小排序的數字序列的第k位的數字。從另一個角度,題目中的第k大數字即是不計入數字序列中數字重複出現的部分,對於數字重複出現的情況,應該認為該數字只出現一次。
現在請聰明的你嘗試設計一種可行且效率更高的方法來解決這個問題。

Input

輸入資料一共有三行。
第一行有一個正整數n(n<=10000000),表示一共給出多少個數字。
第二行有一個正整數k(k<=10000000),表示要求你找出第k大數字。
第三行有n個整數,給出n個數字。對於每個整數i,均滿足-10000000<=i<=10000000。

Output

輸出檔案共一行,包含一個整數,表示n個數字中第k大數字是多少。

Sample Input

3
2
2006 2007 2008

Sample Output

2007

解題思路

一道經典的找第 k 大數字,但是這裡題目要求的是 對於數字重複出現的情況,應該認為該數字只出現一次 ,即先要保證儲存的數字是唯一的,我用STL裡的 bitset

來保證數字的唯一性。
開一個大小為 20000001 的 bitset,0 ~ 10000000 位表示數字 -10000000 ~ 0; 10000001 ~ 20000000 位表示數字 1 ~ 10000000。每輸入一個數字,用該數字加一個偏移量 10000000 的值,再用 bitset::test 來檢測是否存在,存在則什麼都不做,不存在就存到陣列 nums 中,用 size 來記錄儲存數字的數量。
剩下的就是找第 k 大數的問題。
主要函式為 int findk(int left, int right, int pos)
與快排不同的是快排是對劃分出的兩組都進行遞迴處理,而這道題只用對其中一組遞迴處理就可以了,每次遞迴都只對其中一組處理,可以很快的找到解。

我寫的程式碼是按從大到小劃分陣列,每次劃分好後,有三種情況:

注: 下面程式碼中主元的下標為 j,為第 j - left + 1 個元素

  • 若主元位置正好是第 k 個元素,則返回該值;
  • 若主元位置小於第 k 個元素,則在右邊一組找第 k - (j - left + 1) 大數字;
  • 若主元位置大於第 k 個元素,則在左邊一組找第 k 大數字;

Code

#include <bits/stdc++.h>
using namespace std;

int n, k, size = 0;
vector<int> nums(10000001);
bitset<20000001> exist(0);

int findk(int left, int right, int pos);
void exchange(int posA, int posB);

int main() {
    int tmp, bitpos;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &tmp);
        bitpos = tmp + 10000000;
        if (!exist.test(bitpos)) {
            exist.set(bitpos);
            nums[size++] = tmp;
        }
    }

    printf("%d\n", findk(0, size - 1, k));
    return 0;
}

void exchange(int posA, int posB) {
    int tmp = nums[posA];
    nums[posA] = nums[posB];
    nums[posB] = tmp;
}

int findk(int left, int right, int pos) {
    int main = nums[left];
    int i = left + 1, j = right;

    while (i <= j) {
        while (nums[i] > main) i++;
        while (nums[j] < main) j--;
        if (i < j) exchange(i, j);
    }

    exchange(left, j);
    int length = j - left + 1;
    if (length == pos) return nums[j];
    else if (length > pos) return findk(left, j - 1, pos);
    else return findk(j + 1, right, pos - length);
}

這麼寫程式碼的話,OJ 返回的 Run Time 為 2.62sec。
有大神只用了 0.82sec,只能膜了。

以上所有,如有錯誤,麻煩指出,我會及時更改的。

相關推薦

[C++日常] 尋找k數字(numberk)

Description 經過長時間的籌備工作,在Jourk,Ronny,Plipala,阿長,阿沈等人的努力下,DM實驗室建立起自己的系列網站,其中包括三個大板塊:DMOJ首頁、DMOJ論壇、DMOJ部落格。 作為一個大型的網站,資料的加工處理需要用到很多算

【演算法】尋找k的數

目錄: 1、引子 2、排序解決法 3、類快排解法 4、最小堆解法 1、引子 日常編碼中,常見遇到這樣的問題,“尋找最大的數”,此問題非常容易,可暴力直接遍歷找出,也可使用分冶策略找出最大值(詳見分冶演算法)。 本文中需要尋找第k大的數,筆者目前想到3個方法可解決它。 2、排序解決法

python:無序陣列中尋找K的元素

題目: 所謂“第(前)k大數問題”指的是在長度為n(n>=k)的亂序陣列中S找出從大到小順序的第(前)k個數的問題。 解法1:堆排序 採用元素下沉法,維護一個k大小的最小堆,對於陣列中的每一

[C++日常] 計算二叉查詢樹的高度

Description 給定一個二叉查詢樹,要求計算其高度,每個二叉查詢樹將給出先序與中序的遍歷。 例如:一個二叉查詢樹其先序遍歷為:16, 10, 4, 15, 23 ; 中序遍歷為 4, 10, 15, 16, 23,則其高度為2(假定空樹高度為-1,只

[程式設計]查詢K的元素

給定一個無序的整型陣列A[n],陣列大小大於等於3,允許有值相同的元素;請設計演算法找到該陣列排序後第三大的元素值並輸出. 輸入描

LeetCode 4. Median of Two Sorted Arrays (求兩個有序陣列k數字,分治法)

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complex

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

215. Kth Largest Element in an Array(leetcode) 快排思路求解,ac程式碼 class Solution { public: int partion(vector<int> &

尋找k的元素或者k的元素 -- O(n)

此題第一思路肯定是,排序後,直接輸出,但是時間複雜 度O(nlgn);那麼有沒有更快的呢?答案是肯定的。 在我們將利用快排的劃分法進行。其中的思路可參見演算法導論120頁,具體證明步驟也有。具體程式碼在下面: 再者此題也適合求最小的k個數,最大的k個數(只不過相當於求n-k

POJ 3662 Telephone Lines (二分+Dijkstra: 最k的值)

john tel 額外 距離 最優 cst print sig per 題意 Farmer John想從電話公司修一些電纜連接到他農場。已知N個電線桿編號為1,2,?N,其中1號已經連接電話公司,N號為農場,有P對電線桿可連接。 現給出P對電線桿距離Ai,Bi,Li表示Ai

線性基 - 尋找異或K

cpp several may between seve int first xor script XOR is a kind of bit operator, we define that as follow: for two binary base number A a

leetcode703+k數字,最堆使用

https://leetcode.com/problems/kth-largest-element-in-a-stream/description/ class KthLargest { public: int size; priority_queue<int, vec

無序數組求K/K的數

std name 根據 ide ace mat algorithm cout ios 方法一:quicksort 根據快排思想,從後往前找比基準數小的,交換位置。 從前往後找比基準數大的,交換位置。 最後安放基準數。 保證 l到p 是大數,若 p-l+1==k 那麽p就是第

POJ 2010 G - Moo University - Financial Aid(最K)

G - Moo University - Financial Aid 1.題目含義:    奶牛學校招生,c頭奶牛報名,要選n頭(n為奇數),學校是義務制,所以每頭奶牛的學費都由學校負責。每頭奶牛都由自己的考試分數和它需要花的學費,學校總共有f的資金,問合法招生方案中

c語言實現求元素的k

求n個元素序列中第k大的元素。 #include<stdio.h> #include<stdlib.h> #define MAX 100 int second(int t[],int left,int right,int p) { int

尋找給定的一個數組中k的一個數,或者是尋找k個數

這道題是一道常見的面試題,其實這道題可以用快速排序的思想來實現,而且求前k大個數和第k大的數,其實思路是一樣的,都是用快排的思想。只要不停遍歷,直到找到分界點,即該分界點的右邊的數都比該分界點大;該分界點左邊的數都比該分界點小。而且剛好該分界點即為第k大的數。 public stat

BFPRT尋找一群數中k的數

思路:用快排中partition函式的思想,一直partition到等於軸心數的區域包含arr[arrSize - k],軸心數的選取為比較特殊。由於保證了軸心數的品質,所以BFPRT演算法的時間複雜度是嚴格的O(n)。 軸心數的選取: 分組:把partition的區域,

C++ sort + vector 應用:求取陣列中K的數( LeetCode 215. Kth Largest Element in an Array )

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

POJ1442 根堆和根堆找k的數

題意: 給定M個數,每次可以插入序列一個數;再給N個數,表示在插入第幾個數後輸出一個數,第一次輸出序列中最小的,第二次輸出序列中第二小的……以此類推,直到輸出N個數。 第一次做只用一個優先數列超時了

Top K演算法和尋找K個最的數

關於Top K演算法和尋找第K個最小的數這種經典問題網上已經說的很詳細了,不過畢竟不是自己的,這裡自己總結一下,而且這兩個問題又稍稍有點區別。 1.Top K演算法:即尋找一列數中K個最小值或K個最大值,這裡僅以尋找K個最小值為例(演算法類似)。 (1)普通排序:最

C++實現】k元素 時間複雜度為O(n),空間複雜度為O(1)

解題思路: 二基準快速排序,在排序時判斷每次找到的標記點下標 p 與 n-k 的大小,若小於n-k,則只需在p的右側繼續遞迴,若大於 p 則只需在p 的左側遞迴,直至 p 與 n-k 相等 vs可執行程式碼 #include<ctime> #includ