Bagaimana menemukan elemen terbesar k dalam array panjang yang tidak disortir n di O (n)?

220

Saya percaya ada cara untuk menemukan elemen kth terbesar dalam array panjang yang tidak disortir n di O (n). Atau mungkin itu "diharapkan" O (n) atau sesuatu. Bagaimana kita bisa melakukan ini?

MrDatabase
sumber
49
Omong-omong, hampir semua algoritma yang dijelaskan di sini berubah menjadi O (n ^ 2) atau O (n log n) ketika k == n. Artinya, saya tidak berpikir satu pun dari mereka adalah O (n) untuk semua nilai k. Saya termotivasi untuk menunjukkan hal ini tetapi saya pikir Anda harus tahu juga.
Kirk Strauser
19
Algoritma seleksi dapat berupa O (n) untuk nilai tetap k. Yaitu, Anda dapat memiliki algoritma pemilihan untuk k = 25 yaitu O (n) untuk nilai n apa pun, dan Anda dapat melakukan ini untuk nilai k tertentu yang tidak terkait dengan n. Kasus di mana algoritma tidak lagi O (n) adalah ketika nilai k memiliki ketergantungan pada nilai n, seperti k = n atau k = n / 2. Namun ini tidak berarti bahwa jika Anda menjalankan k = 25 algoritma pada daftar 25 item yang tiba-tiba tidak lagi O (n) karena notasi-O menggambarkan properti dari algoritma, bukan khusus jalankan itu.
Tyler McHenry
1
Saya ditanya pertanyaan ini dalam wawancara amazon sebagai kasus umum untuk menemukan elemen terbesar kedua. Omong-omong pewawancara memimpin wawancara saya tidak bertanya apakah saya bisa menghancurkan array asli (yaitu menyortir), jadi saya datang dengan solusi yang rumit.
Sambatyon
4
Ini adalah Pertanyaan 9 di Kolom 11 (Penyortiran) Pemrograman Mutiara oleh Jon Bentley.
Qiang Xu
3
@KirkStrauser: Jika k == n atau k == n-1 maka itu menjadi sepele. Kita bisa mendapatkan maks atau maks 2 dalam traversal tunggal. Jadi algoritma yang disediakan di sini akan secara praktis digunakan untuk nilai k yang bukan milik {1,2, n-1, n}
Aditya Joshee

Jawaban:

173

Ini disebut menemukan statistik urutan ke-k . Ada algoritma acak yang sangat sederhana (disebut quickselect ) yang mengambil O(n)waktu rata - rata, waktu O(n^2)terburuk, dan algoritma non-acak yang cukup rumit (disebut introseluler ) mengambil O(n)waktu terburuk. Ada beberapa info di Wikipedia , tetapi tidak terlalu bagus.

Semua yang Anda butuhkan ada di slide powerpoint ini . Hanya untuk mengekstrak algoritma dasar dari algoritma kasus O(n)terburuk (introselect):

Select(A,n,i):
    Divide input into ⌈n/5⌉ groups of size 5.

    /* Partition on median-of-medians */
    medians = array of each group’s median.
    pivot = Select(medians, ⌈n/5⌉, ⌈n/10⌉)
    Left Array L and Right Array G = partition(A, pivot)

    /* Find ith element in L, pivot, or G */
    k = |L| + 1
    If i = k, return pivot
    If i < k, return Select(L, k-1, i)
    If i > k, return Select(G, n-k, i-k)

Ini juga sangat rinci dalam buku Pengantar Algoritma oleh Cormen et al.

eladv
sumber
6
Terima kasih untuk slidenya.
Kshitij Banerjee
5
Mengapa harus bekerja dalam ukuran 5? Mengapa tidak bisa bekerja dengan ukuran 3?
Joffrey Baratheon
11
@eladv Tautan slide rusak :(
Misha Moroshko
7
@eladv Harap perbaiki tautan yang rusak.
Maksx777
1
Tautan @MishaMoroshko diperbaiki
alfasin
118

Jika Anda menginginkan O(n)algoritma yang benar , sebagai lawan dari O(kn)atau sesuatu seperti itu, maka Anda harus menggunakan quickselect (itu pada dasarnya quicksort tempat Anda membuang partisi yang tidak Anda minati). Prof saya memiliki luncuran yang bagus, dengan analisis runtime: ( referensi )

Algoritme QuickSelect dengan cepat menemukan elemen terkecil k-th dari array elemen yang tidak disortir n. Ini adalah Algoritma Acak , jadi kami menghitung waktu berjalan terburuk yang diharapkan .

Di sini adalah algoritma.

QuickSelect(A, k)
  let r be chosen uniformly at random in the range 1 to length(A)
  let pivot = A[r]
  let A1, A2 be new arrays
  # split into a pile A1 of small elements and A2 of big elements
  for i = 1 to n
    if A[i] < pivot then
      append A[i] to A1
    else if A[i] > pivot then
      append A[i] to A2
    else
      # do nothing
  end for
  if k <= length(A1):
    # it's in the pile of small elements
    return QuickSelect(A1, k)
  else if k > length(A) - length(A2)
    # it's in the pile of big elements
    return QuickSelect(A2, k - (length(A) - length(A2))
  else
    # it's equal to the pivot
    return pivot

Berapa waktu berjalan dari algoritma ini? Jika musuh membalik koin untuk kita, kita mungkin menemukan bahwa pivot selalu merupakan elemen terbesar dan kselalu 1, memberikan waktu berjalan

T(n) = Theta(n) + T(n-1) = Theta(n2)

Tetapi jika pilihannya memang acak, waktu berjalan yang diharapkan diberikan oleh

T(n) <= Theta(n) + (1/n) ∑i=1 to nT(max(i, n-i-1))

di mana kami membuat asumsi yang tidak sepenuhnya masuk akal bahwa rekursi selalu mendarat di yang lebih besar dari A1atau A2.

Mari kita tebak T(n) <= anuntuk beberapa a. Lalu kita dapatkan

T(n) 
 <= cn + (1/n) ∑i=1 to nT(max(i-1, n-i))
 = cn + (1/n) ∑i=1 to floor(n/2) T(n-i) + (1/n) ∑i=floor(n/2)+1 to n T(i)
 <= cn + 2 (1/n) ∑i=floor(n/2) to n T(i)
 <= cn + 2 (1/n) ∑i=floor(n/2) to n ai

dan sekarang entah bagaimana kita harus mendapatkan jumlah yang menghebohkan di sebelah kanan tanda plus untuk menyerapnya cndi sebelah kiri. Jika kita hanya mengikatnya , kita mendapatkan secara kasar . Tapi ini terlalu besar - tidak ada ruang untuk memeras tambahan . Jadi mari kita perluas penjumlahan menggunakan rumus seri aritmatika:2(1/n) ∑i=n/2 to n an2(1/n)(n/2)an = ancn

i=floor(n/2) to n i  
 = ∑i=1 to n i - ∑i=1 to floor(n/2) i  
 = n(n+1)/2 - floor(n/2)(floor(n/2)+1)/2  
 <= n2/2 - (n/4)2/2  
 = (15/32)n2

di mana kita mengambil keuntungan dari n menjadi "cukup besar" untuk mengganti floor(n/2)faktor-faktor buruk dengan yang lebih bersih (dan lebih kecil) n/4. Sekarang kita bisa melanjutkan

cn + 2 (1/n) ∑i=floor(n/2) to n ai,
 <= cn + (2a/n) (15/32) n2
 = n (c + (15/16)a)
 <= an

disediakan a > 16c.

Ini memberi T(n) = O(n). Sudah jelas Omega(n), jadi kita dapatkan T(n) = Theta(n).

Ying Xiao
sumber
12
Pilihan cepat hanya O (n) dalam kasus rata-rata. Algoritma median-of-median dapat digunakan untuk memecahkan masalah dalam waktu O (n) dalam kasus terburuk.
John Kurlak
Apa artinya k > length(A) - length(A2)?
WoooHaaaa
ini bukan O (n), Anda memanggil fungsi lagi sebagai rekursif, T (n). Sudah ada O (n) di sana di dalam fungsi rekursif T (n), jadi jelas tanpa berpikir, kompleksitas keseluruhan akan lebih besar dari O (n).
user1735921
3
@MRROY Karena kami berpisah Ake dalam A1dan di A2sekitar poros, kami tahu itu length(A) == length(A1)+length(A2)+1. Jadi, k > length(A)-length(A2)sama dengan k > length(A1)+1, yang benar ketika kada di suatu tempat di A2.
Filipe Gonçalves
@ FilipeGonçalves, ya jika tidak ada elemen duplikat di pivot. len (A1) + len (A2) + duplikat K = len (A)
d1val
16

Google cepat tentang itu ('kth element array array') mengembalikan ini: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17

"Make one pass through tracking the three largest values so far." 

(itu khusus untuk 3d terbesar)

dan jawaban ini:

Build a heap/priority queue.  O(n)
Pop top element.  O(log n)
Pop top element.  O(log n)
Pop top element.  O(log n)

Total = O(n) + 3 O(log n) = O(n)
warren
sumber
15
baik, itu sebenarnya O (n) + O (k log n) yang tidak mengurangi nilai signifikan K
Jimmy
2
Tetapi menemukan titik penyisipan dalam daftar yang ditautkan dua kali lipat adalah O (k).
Kirk Strauser
1
Dan jika k diperbaiki, O (k) = O (1)
Tyler McHenry
1
@warren: Big-O mendekati, tetapi Anda selalu over-perkiraan. Quicksort sebenarnya O (n ^ 2), misalnya, karena itu adalah kasus terburuk. yang ini O (n + k log n).
Claudiu
1
Anda tidak dapat memperlakukan k sebagai konstanta. Ada kemungkinan bahwa k = n dalam hal ini kompleksitas waktu adalah O (nlogn)
sabbir
11

Anda suka quicksort. Pilih elemen secara acak dan dorong semuanya lebih tinggi atau lebih rendah. Pada titik ini Anda akan tahu elemen mana yang sebenarnya Anda pilih, dan jika itu adalah elemen k yang Anda lakukan, jika tidak Anda ulangi dengan bin (lebih tinggi atau lebih rendah), bahwa elemen k akan jatuh. Secara statistik, waktu yang diperlukan untuk menemukan elemen k tumbuh dengan n, O (n).

bau
sumber
2
Inilah yang dimaksud quickselect, FWIW.
rogerdpack
6

Companion Programmer untuk Analisis Algoritma memberikan versi yang adalah O (n), meskipun penulis menyatakan bahwa faktor konstan begitu tinggi, Anda mungkin akan lebih suka naif semacam-the-daftar-kemudian-pilih metode.

Saya menjawab surat pertanyaan Anda :)

Jimmy
sumber
2
Tidak benar dalam semua kasus. Saya telah menerapkan median-of-median dan membandingkannya dengan metode Sort bawaan di .NET dan solusi kustom benar-benar berjalan lebih cepat berdasarkan urutan besarnya. Sekarang pertanyaan sebenarnya adalah: apakah itu penting bagi Anda dalam keadaan tertentu. Menulis dan men-debug 100 baris kode dibandingkan dengan satu liner terbayar hanya jika kode itu akan dieksekusi berkali-kali sehingga pengguna mulai memperhatikan perbedaan waktu berjalan dan merasa tidak nyaman menunggu operasi selesai.
Zoran Horvat
5

Pustaka standar C ++ memiliki fungsi panggilan yang hampir persis seperti itu nth_element, meskipun itu memodifikasi data Anda. Ia mengharapkan run-time linier, O (N), dan juga melakukan pengurutan parsial.

const int N = ...;
double a[N];
// ... 
const int m = ...; // m < N
nth_element (a, a + m, a + N);
// a[m] contains the mth element in a
David Nehme
sumber
1
Tidak, ini memiliki runtime O (n) rata-rata yang diharapkan . Misalnya, quicksort adalah O (nlogn) rata-rata dengan kasus terburuk O (n ^ 2). Wow, ada sesuatu yang salah sebenarnya!
Kirk Strauser
5
Tidak, sebenarnya tidak ada yang salah dengan jawaban ini. Ini bekerja dan standar C ++ membutuhkan waktu menjalankan linier yang diharapkan.
David Nehme
Saya diminta dalam wawancara untuk mengasumsikan ketersediaan ruang O (k) dan 'n' sangat besar. Saya tidak bisa memberitahunya O (n) solusi karena saya pikir nth_element akan membutuhkan ruang o (n). Apakah saya salah? Bukankah algoritma yang mendasari adalah quicksort berbasis untuk nth_element?
Manish Baphna
4

Meskipun tidak terlalu yakin tentang O (n) kompleksitas, tetapi akan pasti antara O (n) dan nLog (n). Juga pasti lebih dekat ke O (n) daripada nLog (n). Fungsi ditulis dalam Java

public int quickSelect(ArrayList<Integer>list, int nthSmallest){
    //Choose random number in range of 0 to array length
    Random random =  new Random();
    //This will give random number which is not greater than length - 1
    int pivotIndex = random.nextInt(list.size() - 1); 

    int pivot = list.get(pivotIndex);

    ArrayList<Integer> smallerNumberList = new ArrayList<Integer>();
    ArrayList<Integer> greaterNumberList = new ArrayList<Integer>();

    //Split list into two. 
    //Value smaller than pivot should go to smallerNumberList
    //Value greater than pivot should go to greaterNumberList
    //Do nothing for value which is equal to pivot
    for(int i=0; i<list.size(); i++){
        if(list.get(i)<pivot){
            smallerNumberList.add(list.get(i));
        }
        else if(list.get(i)>pivot){
            greaterNumberList.add(list.get(i));
        }
        else{
            //Do nothing
        }
    }

    //If smallerNumberList size is greater than nthSmallest value, nthSmallest number must be in this list 
    if(nthSmallest < smallerNumberList.size()){
        return quickSelect(smallerNumberList, nthSmallest);
    }
    //If nthSmallest is greater than [ list.size() - greaterNumberList.size() ], nthSmallest number must be in this list
    //The step is bit tricky. If confusing, please see the above loop once again for clarification.
    else if(nthSmallest > (list.size() - greaterNumberList.size())){
        //nthSmallest will have to be changed here. [ list.size() - greaterNumberList.size() ] elements are already in 
        //smallerNumberList
        nthSmallest = nthSmallest - (list.size() - greaterNumberList.size());
        return quickSelect(greaterNumberList,nthSmallest);
    }
    else{
        return pivot;
    }
}
prithvi zankat
sumber
Pengodean yang bagus, +1. Tetapi tidak perlu menggunakan ruang ekstra.
Hengameh
4

Saya menerapkan menemukan kth minimal dalam n elemen yang tidak disortir menggunakan pemrograman dinamis, khususnya metode turnamen. Waktu eksekusi adalah O (n + klog (n)). Mekanisme yang digunakan tercantum sebagai salah satu metode pada halaman Wikipedia tentang Algoritma Pemilihan (seperti yang ditunjukkan dalam salah satu posting di atas). Anda dapat membaca tentang algoritma dan juga menemukan kode (java) di halaman blog saya Finding Kth Minimum . Selain itu logika dapat melakukan pemesanan parsial dari daftar - mengembalikan K min (atau maks) pertama dalam waktu O (klog (n)).

Meskipun kode memberikan hasil kth minimum, logika yang sama dapat digunakan untuk menemukan maksimum kth di O (klog (n)), mengabaikan pra-kerja yang dilakukan untuk membuat pohon turnamen.

Malkit S. Bhasin
sumber
3

Anda dapat melakukannya di O (n + kn) = O (n) (untuk k konstan) untuk waktu dan O (k) untuk ruang, dengan melacak elemen k terbesar yang pernah Anda lihat.

Untuk setiap elemen dalam array, Anda dapat memindai daftar k terbesar dan mengganti elemen terkecil dengan yang baru jika lebih besar.

Solusi tumpukan prioritas Warren lebih rapi.

Rob Walker
sumber
3
Ini akan memiliki kasus terburuk O (n ^ 2) di mana Anda diminta untuk item terkecil.
Elie
2
"Item terkecil" berarti k = n, jadi k tidak lagi konstan.
Tyler McHenry
Atau mungkin menyimpan tumpukan (atau tumpukan terbalik, atau pohon seimbang) dari k terbesar yang pernah Anda lihat sejauh ini O(n log k)... masih merosot menjadi O (nlogn) jika k besar. Saya akan berpikir itu akan bekerja dengan baik untuk nilai-nilai kecil k namun ... mungkin lebih cepat daripada beberapa algoritma lain yang disebutkan di sini [???]
rogerdpack
3

Pilih cepat seksi dengan Python

def quickselect(arr, k):
    '''
     k = 1 returns first element in ascending order.
     can be easily modified to return first element in descending order
    '''

    r = random.randrange(0, len(arr))

    a1 = [i for i in arr if i < arr[r]] '''partition'''
    a2 = [i for i in arr if i > arr[r]]

    if k <= len(a1):
        return quickselect(a1, k)
    elif k > len(arr)-len(a2):
        return quickselect(a2, k - (len(arr) - len(a2)))
    else:
        return arr[r]
hoder
sumber
Solusi yang bagus, kecuali bahwa ini mengembalikan elemen terkecil k dalam daftar yang tidak disortir. Membalikkan operator pembanding dalam daftar pemahaman, a1 = [i for i in arr if i > arr[r]]dan a2 = [i for i in arr if i < arr[r]], akan mengembalikan elemen terbesar k .
Intisari
Dari benchmark kecil, bahkan pada array besar, itu lebih cepat untuk semacam (dengan numpy.sortuntuk numpy arrayatau sorteduntuk daftar) daripada menggunakan implementasi manual ini.
Næreen
2

Temukan median array dalam waktu linier, lalu gunakan prosedur partisi persis seperti dalam quicksort untuk membagi array menjadi dua bagian, nilai di sebelah kiri rata-rata lebih rendah (<) daripada rata-rata dan ke kanan lebih besar dari (>) median , itu juga dapat dilakukan dalam waktu lineat, sekarang, pergi ke bagian array dimana elemen kth terletak, Sekarang perulangan menjadi: T (n) = T (n / 2) + cn yang memberi saya O (n) overal.

pranjal
sumber
Tidak perlu mencari median. tanpa median pendekatan Anda masih baik-baik saja.
Hengameh
2
Dan bagaimana Anda menemukan median dalam waktu linier, saya berani bertanya? ... :)
rogerdpack
2

Di bawah ini adalah tautan untuk implementasi penuh dengan penjelasan yang cukup luas bagaimana algoritma untuk menemukan elemen Kth dalam algoritma yang tidak disortir bekerja. Ide dasarnya adalah mempartisi array seperti di QuickSort. Tetapi untuk menghindari kasus-kasus ekstrem (misalnya ketika elemen terkecil dipilih sebagai pivot di setiap langkah, sehingga algoritma berubah menjadi O (n ^ 2) waktu berjalan), pemilihan pivot khusus diterapkan, yang disebut algoritma median-of-median. Seluruh solusi berjalan dalam waktu O (n) dalam kondisi terburuk dan rata-rata.

Berikut ini tautan ke artikel lengkap (ini tentang menemukan elemen terkecil Kth , tetapi prinsipnya sama untuk menemukan Kth terbesar ):

Menemukan Kth Elemen Terkecil dalam Array yang Tidak Disortir

Zoran Horvat
sumber
2

Sesuai makalah ini Menemukan item terbesar K dalam daftar n item , algoritma berikut akan memakan O(n)waktu dalam kasus terburuk.

  1. Bagilah array menjadi n / 5 daftar masing-masing 5 elemen.
  2. Temukan median di setiap sub array 5 elemen.
  3. Secara rekursif menemukan median dari semua median, sebut saja M
  4. Mempartisi array menjadi dua sub-array Sub-array 1 berisi elemen-elemen yang lebih besar dari M, katakanlah sub-array ini adalah a1, sedangkan sub-array lainnya mengandung elemen-elemen yang lebih kecil dari M., kita sebut sub-array ini a2.
  5. Jika k <= | a1 |, kembalikan pilihan (a1, k).
  6. Jika k− 1 = | a1 |, kembalikan M.
  7. Jika k> | a1 | +1, pilihan kembali (a2, k −a1 - 1).

Analisis: Seperti yang disarankan dalam makalah asli:

Kami menggunakan median untuk mempartisi daftar menjadi dua bagian (babak pertama, jika k <= n/2, dan babak kedua sebaliknya). Algoritma ini membutuhkan waktu cnpada level rekursi pertama untuk beberapa konstanta c, cn/2pada level berikutnya (karena kita berulang dalam daftar ukuran n / 2), cn/4pada level ketiga, dan seterusnya. Total waktu yang diambil adalah cn + cn/2 + cn/4 + .... = 2cn = o(n).

Mengapa ukuran partisi diambil 5 dan bukan 3?

Sebagaimana disebutkan dalam makalah asli :

Membagi daftar dengan 5 menjamin pembagian kasus terburuk 70 - 30. Setidaknya setengah dari median lebih besar dari median-of-median, maka setidaknya setengah dari blok n / 5 memiliki minimal 3 elemen dan ini memberikan 3n/10pemisahan, yang berarti partisi lainnya adalah 7n / 10 dalam kasus terburuk. Itu memberi T(n) = T(n/5)+T(7n/10)+O(n). Since n/5+7n/10 < 1, waktu berjalan terburuk adalah O(n).

Sekarang saya telah mencoba mengimplementasikan algoritma di atas sebagai:

public static int findKthLargestUsingMedian(Integer[] array, int k) {
        // Step 1: Divide the list into n/5 lists of 5 element each.
        int noOfRequiredLists = (int) Math.ceil(array.length / 5.0);
        // Step 2: Find pivotal element aka median of medians.
        int medianOfMedian =  findMedianOfMedians(array, noOfRequiredLists);
        //Now we need two lists split using medianOfMedian as pivot. All elements in list listOne will be grater than medianOfMedian and listTwo will have elements lesser than medianOfMedian.
        List<Integer> listWithGreaterNumbers = new ArrayList<>(); // elements greater than medianOfMedian
        List<Integer> listWithSmallerNumbers = new ArrayList<>(); // elements less than medianOfMedian
        for (Integer element : array) {
            if (element < medianOfMedian) {
                listWithSmallerNumbers.add(element);
            } else if (element > medianOfMedian) {
                listWithGreaterNumbers.add(element);
            }
        }
        // Next step.
        if (k <= listWithGreaterNumbers.size()) return findKthLargestUsingMedian((Integer[]) listWithGreaterNumbers.toArray(new Integer[listWithGreaterNumbers.size()]), k);
        else if ((k - 1) == listWithGreaterNumbers.size()) return medianOfMedian;
        else if (k > (listWithGreaterNumbers.size() + 1)) return findKthLargestUsingMedian((Integer[]) listWithSmallerNumbers.toArray(new Integer[listWithSmallerNumbers.size()]), k-listWithGreaterNumbers.size()-1);
        return -1;
    }

    public static int findMedianOfMedians(Integer[] mainList, int noOfRequiredLists) {
        int[] medians = new int[noOfRequiredLists];
        for (int count = 0; count < noOfRequiredLists; count++) {
            int startOfPartialArray = 5 * count;
            int endOfPartialArray = startOfPartialArray + 5;
            Integer[] partialArray = Arrays.copyOfRange((Integer[]) mainList, startOfPartialArray, endOfPartialArray);
            // Step 2: Find median of each of these sublists.
            int medianIndex = partialArray.length/2;
            medians[count] = partialArray[medianIndex];
        }
        // Step 3: Find median of the medians.
        return medians[medians.length / 2];
    }

Demi penyelesaian, algoritma lain menggunakan Antrian Prioritas dan membutuhkan waktu O(nlogn).

public static int findKthLargestUsingPriorityQueue(Integer[] nums, int k) {
        int p = 0;
        int numElements = nums.length;
        // create priority queue where all the elements of nums will be stored
        PriorityQueue<Integer> pq = new PriorityQueue<Integer>();

        // place all the elements of the array to this priority queue
        for (int n : nums) {
            pq.add(n);
        }

        // extract the kth largest element
        while (numElements - k + 1 > 0) {
            p = pq.poll();
            k++;
        }

        return p;
    }

Kedua algoritma ini dapat diuji sebagai:

public static void main(String[] args) throws IOException {
        Integer[] numbers = new Integer[]{2, 3, 5, 4, 1, 12, 11, 13, 16, 7, 8, 6, 10, 9, 17, 15, 19, 20, 18, 23, 21, 22, 25, 24, 14};
        System.out.println(findKthLargestUsingMedian(numbers, 8));
        System.out.println(findKthLargestUsingPriorityQueue(numbers, 8));
    }

Output yang diharapkan adalah: 18 18

akhil_mittal
sumber
@rogerdpack Saya telah memberikan tautan yang telah saya ikuti.
akhil_mittal
2

Bagaimana dengan pendekatan yang seperti ini

Pertahankan a buffer of length kdan a tmp_max, mendapatkan tmp_max adalah O (k) dan dilakukan n kali jadi sepertiO(kn)

masukkan deskripsi gambar di sini

Apakah benar atau saya kehilangan sesuatu?

Meskipun tidak mengalahkan rata-rata kasus pemilihan cepat dan terburuk dari metode statistik median tetapi cukup mudah dipahami dan diimplementasikan.

Aishwat Singh
sumber
1
Saya suka, lebih mudah dimengerti. Padahal kompleksitasnya adalah O (nk) seperti yang Anda tunjukkan.
Hajjat
1

beralih melalui daftar. jika nilai saat ini lebih besar dari nilai terbesar yang disimpan, simpan sebagai nilai terbesar dan benturkan 1-4 ke bawah dan 5 turun dari daftar. Jika tidak, bandingkan dengan nomor 2 dan lakukan hal yang sama. Ulangi, periksa terhadap semua 5 nilai yang tersimpan. ini harus dilakukan di O (n)

Kevin
sumber
"Bump" itu adalah O (n) jika Anda menggunakan array, atau turun ke O (log n) (saya pikir) jika Anda menggunakan struktur yang lebih baik.
Kirk Strauser
Tidak perlu O (log k) - jika daftar adalah daftar tertaut maka tambahkan elemen baru ke atas dan menjatuhkan elemen terakhir lebih seperti O (2)
Alnitak
Benjolan tersebut adalah O (k) untuk daftar yang didukung array, O (1) untuk daftar yang terhubung secara tepat. Either way, pertanyaan semacam ini umumnya menganggap itu berdampak minimal dibandingkan dengan n dan itu tidak memperkenalkan lebih banyak faktor n.
bobince
itu juga akan menjadi O (1) jika benjolan itu menggunakan penyangga cincin
Alnitak
1
Bagaimanapun, algoritma komentar tidak lengkap, gagal untuk mempertimbangkan elemen dari n yang merupakan yang terbesar kedua (misalnya). Perilaku kasus terburuk, di mana setiap elemen dalam n harus dibandingkan dengan masing-masing dalam tabel skor tinggi, adalah O (kn) - tetapi itu masih mungkin berarti O (n) dalam hal pertanyaan.
bobince
1

saya ingin menyarankan satu jawaban

jika kita mengambil elemen k pertama dan mengurutkannya ke daftar nilai k yang tertaut

sekarang untuk setiap nilai lain bahkan untuk kasus terburuk jika kita melakukan penyisipan sort untuk nilai nk sisanya bahkan dalam jumlah kasus terburuk perbandingan akan menjadi k * (nk) dan untuk nilai k yang akan diurutkan biarlah k * (k- 1) jadi keluar menjadi (nk-k) yaitu o (n)

Bersulang


sumber
1
menyortir membutuhkan waktu nlogn ... algoritma harus berjalan dalam waktu linear
MrDatabase
1

Penjelasan dari algoritma median - of - median untuk menemukan bilangan bulat terbesar ke - k dapat ditemukan di sini: http://cs.indstate.edu/~spitla/presentation.pdf

Implementasi dalam c ++ di bawah ini:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int findMedian(vector<int> vec){
//    Find median of a vector
    int median;
    size_t size = vec.size();
    median = vec[(size/2)];
    return median;
}

int findMedianOfMedians(vector<vector<int> > values){
    vector<int> medians;

    for (int i = 0; i < values.size(); i++) {
        int m = findMedian(values[i]);
        medians.push_back(m);
    }

    return findMedian(medians);
}

void selectionByMedianOfMedians(const vector<int> values, int k){
//    Divide the list into n/5 lists of 5 elements each
    vector<vector<int> > vec2D;

    int count = 0;
    while (count != values.size()) {
        int countRow = 0;
        vector<int> row;

        while ((countRow < 5) && (count < values.size())) {
            row.push_back(values[count]);
            count++;
            countRow++;
        }
        vec2D.push_back(row);
    }

    cout<<endl<<endl<<"Printing 2D vector : "<<endl;
    for (int i = 0; i < vec2D.size(); i++) {
        for (int j = 0; j < vec2D[i].size(); j++) {
            cout<<vec2D[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<endl;

//    Calculating a new pivot for making splits
    int m = findMedianOfMedians(vec2D);
    cout<<"Median of medians is : "<<m<<endl;

//    Partition the list into unique elements larger than 'm' (call this sublist L1) and
//    those smaller them 'm' (call this sublist L2)
    vector<int> L1, L2;

    for (int i = 0; i < vec2D.size(); i++) {
        for (int j = 0; j < vec2D[i].size(); j++) {
            if (vec2D[i][j] > m) {
                L1.push_back(vec2D[i][j]);
            }else if (vec2D[i][j] < m){
                L2.push_back(vec2D[i][j]);
            }
        }
    }

//    Checking the splits as per the new pivot 'm'
    cout<<endl<<"Printing L1 : "<<endl;
    for (int i = 0; i < L1.size(); i++) {
        cout<<L1[i]<<" ";
    }

    cout<<endl<<endl<<"Printing L2 : "<<endl;
    for (int i = 0; i < L2.size(); i++) {
        cout<<L2[i]<<" ";
    }

//    Recursive calls
    if ((k - 1) == L1.size()) {
        cout<<endl<<endl<<"Answer :"<<m;
    }else if (k <= L1.size()) {
        return selectionByMedianOfMedians(L1, k);
    }else if (k > (L1.size() + 1)){
        return selectionByMedianOfMedians(L2, k-((int)L1.size())-1);
    }

}

int main()
{
    int values[] = {2, 3, 5, 4, 1, 12, 11, 13, 16, 7, 8, 6, 10, 9, 17, 15, 19, 20, 18, 23, 21, 22, 25, 24, 14};

    vector<int> vec(values, values + 25);

    cout<<"The given array is : "<<endl;
    for (int i = 0; i < vec.size(); i++) {
        cout<<vec[i]<<" ";
    }

    selectionByMedianOfMedians(vec, 8);

    return 0;
}
totjammykd
sumber
Solusi ini tidak berfungsi. Anda perlu mengurutkan array sebelum mengembalikan median untuk case elemen 5.
Agnishom Chattopadhyay
1

Ada juga algoritma pemilihan Wirth , yang memiliki implementasi yang lebih sederhana daripada QuickSelect. Algoritme pemilihan Wirth lebih lambat daripada QuickSelect, tetapi dengan beberapa perbaikan, ia menjadi lebih cepat.

Lebih detail. Dengan menggunakan optimasi MODIFIND dari Vladimir Zabrodsky dan pemilihan pivot median-of-3 dan memberikan perhatian pada langkah-langkah terakhir dari bagian partisi dari algoritma, saya telah membuat algoritma berikut (yang secara imajinatif dinamai "LefSelect"):

#define F_SWAP(a,b) { float temp=(a);(a)=(b);(b)=temp; }

# Note: The code needs more than 2 elements to work
float lefselect(float a[], const int n, const int k) {
    int l=0, m = n-1, i=l, j=m;
    float x;

    while (l<m) {
        if( a[k] < a[i] ) F_SWAP(a[i],a[k]);
        if( a[j] < a[i] ) F_SWAP(a[i],a[j]);
        if( a[j] < a[k] ) F_SWAP(a[k],a[j]);

        x=a[k];
        while (j>k & i<k) {
            do i++; while (a[i]<x);
            do j--; while (a[j]>x);

            F_SWAP(a[i],a[j]);
        }
        i++; j--;

        if (j<k) {
            while (a[i]<x) i++;
            l=i; j=m;
        }
        if (k<i) {
            while (x<a[j]) j--;
            m=j; i=l;
        }
    }
    return a[k];
}

Dalam tolok ukur yang saya lakukan di sini , LefSelect adalah 20-30% lebih cepat daripada QuickSelect.

estama
sumber
1

Solusi Haskell:

kthElem index list = sort list !! index

withShape ~[]     []     = []
withShape ~(x:xs) (y:ys) = x : withShape xs ys

sort []     = []
sort (x:xs) = (sort ls `withShape` ls) ++ [x] ++ (sort rs `withShape` rs)
  where
   ls = filter (<  x)
   rs = filter (>= x)

Ini mengimplementasikan median solusi median dengan menggunakan metode withShape untuk menemukan ukuran partisi tanpa benar-benar menghitungnya.

pengguna3585010
sumber
1

Berikut ini adalah implementasi C ++ dari QuickSelect Acak. Idenya adalah untuk secara acak memilih elemen pivot. Untuk mengimplementasikan partisi acak, kami menggunakan fungsi acak, rand () untuk menghasilkan indeks antara l dan r, menukar elemen pada indeks yang dihasilkan secara acak dengan elemen terakhir, dan akhirnya memanggil proses partisi standar yang menggunakan elemen terakhir sebagai pivot.

#include<iostream>
#include<climits>
#include<cstdlib>
using namespace std;

int randomPartition(int arr[], int l, int r);

// This function returns k'th smallest element in arr[l..r] using
// QuickSort based method.  ASSUMPTION: ALL ELEMENTS IN ARR[] ARE DISTINCT
int kthSmallest(int arr[], int l, int r, int k)
{
    // If k is smaller than number of elements in array
    if (k > 0 && k <= r - l + 1)
    {
        // Partition the array around a random element and
        // get position of pivot element in sorted array
        int pos = randomPartition(arr, l, r);

        // If position is same as k
        if (pos-l == k-1)
            return arr[pos];
        if (pos-l > k-1)  // If position is more, recur for left subarray
            return kthSmallest(arr, l, pos-1, k);

        // Else recur for right subarray
        return kthSmallest(arr, pos+1, r, k-pos+l-1);
    }

    // If k is more than number of elements in array
    return INT_MAX;
}

void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Standard partition process of QuickSort().  It considers the last
// element as pivot and moves all smaller element to left of it and
// greater elements to right. This function is used by randomPartition()
int partition(int arr[], int l, int r)
{
    int x = arr[r], i = l;
    for (int j = l; j <= r - 1; j++)
    {
        if (arr[j] <= x) //arr[i] is bigger than arr[j] so swap them
        {
            swap(&arr[i], &arr[j]);
            i++;
        }
    }
    swap(&arr[i], &arr[r]); // swap the pivot
    return i;
}

// Picks a random pivot element between l and r and partitions
// arr[l..r] around the randomly picked element using partition()
int randomPartition(int arr[], int l, int r)
{
    int n = r-l+1;
    int pivot = rand() % n;
    swap(&arr[l + pivot], &arr[r]);
    return partition(arr, l, r);
}

// Driver program to test above methods
int main()
{
    int arr[] = {12, 3, 5, 7, 4, 19, 26};
    int n = sizeof(arr)/sizeof(arr[0]), k = 3;
    cout << "K'th smallest element is " << kthSmallest(arr, 0, n-1, k);
    return 0;
}

Kompleksitas waktu kasus terburuk dari solusi di atas masih O (n2). Dalam kasus terburuk, fungsi acak selalu dapat memilih elemen sudut. Kompleksitas waktu yang diharapkan dari QuickSelect acak di atas adalah Θ (n)

pelajar
sumber
Pengodean yang bagus. Terima kasih telah berbagi, +1
Hengameh
1
  1. Buat antrian Prioritas dibuat.
  2. Masukkan semua elemen ke tumpukan.
  3. Panggil polling () k kali.

    public static int getKthLargestElements(int[] arr)
    {
        PriorityQueue<Integer> pq =  new PriorityQueue<>((x , y) -> (y-x));
        //insert all the elements into heap
        for(int ele : arr)
           pq.offer(ele);
        // call poll() k times
        int i=0;
        while(i&lt;k)
         {
           int result = pq.poll();
         } 
       return result;        
    }
    
Bhagwati Malav
sumber
0

Ini adalah implementasi dalam Javascript.

Jika Anda melepaskan batasan yang tidak dapat Anda ubah array, Anda dapat mencegah penggunaan memori tambahan menggunakan dua indeks untuk mengidentifikasi "partisi saat ini" (dalam gaya quicksort klasik - http://www.nczonline.net/blog/2012/ 11/27 / computer-science-in-javascript-quicksort / ).

function kthMax(a, k){
    var size = a.length;

    var pivot = a[ parseInt(Math.random()*size) ]; //Another choice could have been (size / 2) 

    //Create an array with all element lower than the pivot and an array with all element higher than the pivot
    var i, lowerArray = [], upperArray = [];
    for (i = 0; i  < size; i++){
        var current = a[i];

        if (current < pivot) {
            lowerArray.push(current);
        } else if (current > pivot) {
            upperArray.push(current);
        }
    }

    //Which one should I continue with?
    if(k <= upperArray.length) {
        //Upper
        return kthMax(upperArray, k);
    } else {
        var newK = k - (size - lowerArray.length);

        if (newK > 0) {
            ///Lower
            return kthMax(lowerArray, newK);
        } else {
            //None ... it's the current pivot!
            return pivot;
        }   
    }
}  

Jika Anda ingin menguji kinerjanya, Anda dapat menggunakan variasi ini:

    function kthMax (a, k, logging) {
         var comparisonCount = 0; //Number of comparison that the algorithm uses
         var memoryCount = 0;     //Number of integers in memory that the algorithm uses
         var _log = logging;

         if(k < 0 || k >= a.length) {
            if (_log) console.log ("k is out of range"); 
            return false;
         }      

         function _kthmax(a, k){
             var size = a.length;
             var pivot = a[parseInt(Math.random()*size)];
             if(_log) console.log("Inputs:", a,  "size="+size, "k="+k, "pivot="+pivot);

             // This should never happen. Just a nice check in this exercise
             // if you are playing with the code to avoid never ending recursion            
             if(typeof pivot === "undefined") {
                 if (_log) console.log ("Ops..."); 
                 return false;
             }

             var i, lowerArray = [], upperArray = [];
             for (i = 0; i  < size; i++){
                 var current = a[i];
                 if (current < pivot) {
                     comparisonCount += 1;
                     memoryCount++;
                     lowerArray.push(current);
                 } else if (current > pivot) {
                     comparisonCount += 2;
                     memoryCount++;
                     upperArray.push(current);
                 }
             }
             if(_log) console.log("Pivoting:",lowerArray, "*"+pivot+"*", upperArray);

             if(k <= upperArray.length) {
                 comparisonCount += 1;
                 return _kthmax(upperArray, k);
             } else if (k > size - lowerArray.length) {
                 comparisonCount += 2;
                 return _kthmax(lowerArray, k - (size - lowerArray.length));
             } else {
                 comparisonCount += 2;
                 return pivot;
             }
     /* 
      * BTW, this is the logic for kthMin if we want to implement that... ;-)
      * 

             if(k <= lowerArray.length) {
                 return kthMin(lowerArray, k);
             } else if (k > size - upperArray.length) {
                 return kthMin(upperArray, k - (size - upperArray.length));
             } else 
                 return pivot;
     */            
         }

         var result = _kthmax(a, k);
         return {result: result, iterations: comparisonCount, memory: memoryCount};
     }

Sisa kode ini hanya untuk membuat beberapa taman bermain:

    function getRandomArray (n){
        var ar = [];
        for (var i = 0, l = n; i < l; i++) {
            ar.push(Math.round(Math.random() * l))
        }

        return ar;
    }

    //Create a random array of 50 numbers
    var ar = getRandomArray (50);   

Sekarang, jalankan tes Anda beberapa kali. Karena Math.random () itu akan menghasilkan setiap kali hasil yang berbeda:

    kthMax(ar, 2, true);
    kthMax(ar, 2);
    kthMax(ar, 2);
    kthMax(ar, 2);
    kthMax(ar, 2);
    kthMax(ar, 2);
    kthMax(ar, 34, true);
    kthMax(ar, 34);
    kthMax(ar, 34);
    kthMax(ar, 34);
    kthMax(ar, 34);
    kthMax(ar, 34);

Jika Anda mengujinya beberapa kali, Anda bahkan dapat melihat secara empiris bahwa jumlah iterasi adalah, rata-rata, O (n) ~ = konstan * n dan nilai k tidak mempengaruhi algoritma.

Chris Cinelli
sumber
0

Saya datang dengan algoritma ini dan tampaknya O (n):

Katakanlah k = 3 dan kami ingin menemukan item terbesar ke-3 dalam array. Saya akan membuat tiga variabel dan membandingkan setiap item dari array dengan minimum dari ketiga variabel ini. Jika item array lebih besar dari minimum kami, kami akan mengganti variabel min dengan nilai item. Kami melanjutkan hal yang sama hingga akhir array. Minimum dari tiga variabel kami adalah item terbesar ke-3 dalam array.

define variables a=0, b=0, c=0
iterate through the array items
    find minimum a,b,c
    if item > min then replace the min variable with item value
    continue until end of array
the minimum of a,b,c is our answer

Dan, untuk menemukan item terbesar K kita membutuhkan variabel K.

Contoh: (k = 3)

[1,2,4,1,7,3,9,5,6,2,9,8]

Final variable values:

a=7 (answer)
b=8
c=9

Dapatkah seseorang tolong tinjau ini dan beri tahu saya apa yang saya lewatkan?

advncd
sumber
0

Berikut ini adalah implementasi dari algoritma eladv yang disarankan (Saya juga taruh implementasi ini dengan pivot acak):

public class Median {

    public static void main(String[] s) {

        int[] test = {4,18,20,3,7,13,5,8,2,1,15,17,25,30,16};
        System.out.println(selectK(test,8));

        /*
        int n = 100000000;
        int[] test = new int[n];
        for(int i=0; i<test.length; i++)
            test[i] = (int)(Math.random()*test.length);

        long start = System.currentTimeMillis();
        random_selectK(test, test.length/2);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        */
    }

    public static int random_selectK(int[] a, int k) {
        if(a.length <= 1)
            return a[0];

        int r = (int)(Math.random() * a.length);
        int p = a[r];

        int small = 0, equal = 0, big = 0;
        for(int i=0; i<a.length; i++) {
            if(a[i] < p) small++;
            else if(a[i] == p) equal++;
            else if(a[i] > p) big++;
        }

        if(k <= small) {
            int[] temp = new int[small];
            for(int i=0, j=0; i<a.length; i++)
                if(a[i] < p)
                    temp[j++] = a[i];
            return random_selectK(temp, k);
        }

        else if (k <= small+equal)
            return p;

        else {
            int[] temp = new int[big];
            for(int i=0, j=0; i<a.length; i++)
                if(a[i] > p)
                    temp[j++] = a[i];
            return random_selectK(temp,k-small-equal);
        }
    }

    public static int selectK(int[] a, int k) {
        if(a.length <= 5) {
            Arrays.sort(a);
            return a[k-1];
        }

        int p = median_of_medians(a);

        int small = 0, equal = 0, big = 0;
        for(int i=0; i<a.length; i++) {
            if(a[i] < p) small++;
            else if(a[i] == p) equal++;
            else if(a[i] > p) big++;
        }

        if(k <= small) {
            int[] temp = new int[small];
            for(int i=0, j=0; i<a.length; i++)
                if(a[i] < p)
                    temp[j++] = a[i];
            return selectK(temp, k);
        }

        else if (k <= small+equal)
            return p;

        else {
            int[] temp = new int[big];
            for(int i=0, j=0; i<a.length; i++)
                if(a[i] > p)
                    temp[j++] = a[i];
            return selectK(temp,k-small-equal);
        }
    }

    private static int median_of_medians(int[] a) {
        int[] b = new int[a.length/5];
        int[] temp = new int[5];
        for(int i=0; i<b.length; i++) {
            for(int j=0; j<5; j++)
                temp[j] = a[5*i + j];
            Arrays.sort(temp);
            b[i] = temp[2];
        }

        return selectK(b, b.length/2 + 1);
    }
}
TheLogicGuy
sumber
0

itu mirip dengan strategi quickSort, di mana kita memilih pivot yang sewenang-wenang, dan membawa elemen yang lebih kecil ke kiri, dan yang lebih besar ke kanan

    public static int kthElInUnsortedList(List<int> list, int k)
    {
        if (list.Count == 1)
            return list[0];

        List<int> left = new List<int>();
        List<int> right = new List<int>();

        int pivotIndex = list.Count / 2;
        int pivot = list[pivotIndex]; //arbitrary

        for (int i = 0; i < list.Count && i != pivotIndex; i++)
        {
            int currentEl = list[i];
            if (currentEl < pivot)
                left.Add(currentEl);
            else
                right.Add(currentEl);
        }

        if (k == left.Count + 1)
            return pivot;

        if (left.Count < k)
            return kthElInUnsortedList(right, k - left.Count - 1);
        else
            return kthElInUnsortedList(left, k);
    }
Lee.O.
sumber
0

Anda dapat menemukan elemen terkecil k dalam waktu O (n) dan ruang konstan. Jika kita menganggap array hanya untuk bilangan bulat.

Pendekatannya adalah melakukan pencarian biner pada rentang nilai Array. Jika kita memiliki nilai min_ dan nilai maks_ keduanya dalam rentang integer, kita dapat melakukan pencarian biner pada rentang itu. Kita dapat menulis fungsi komparator yang akan memberi tahu kita jika ada nilai kth-terkecil atau lebih kecil dari kth-terkecil atau lebih besar dari kth-terkecil. Lakukan pencarian biner hingga Anda mencapai angka terkecil ke-k

Ini kode untuk itu

Solusi kelas:

def _iskthsmallest(self, A, val, k):
    less_count, equal_count = 0, 0
    for i in range(len(A)):
        if A[i] == val: equal_count += 1
        if A[i] < val: less_count += 1

    if less_count >= k: return 1
    if less_count + equal_count < k: return -1
    return 0

def kthsmallest_binary(self, A, min_val, max_val, k):
    if min_val == max_val:
        return min_val
    mid = (min_val + max_val)/2
    iskthsmallest = self._iskthsmallest(A, mid, k)
    if iskthsmallest == 0: return mid
    if iskthsmallest > 0: return self.kthsmallest_binary(A, min_val, mid, k)
    return self.kthsmallest_binary(A, mid+1, max_val, k)

# @param A : tuple of integers
# @param B : integer
# @return an integer
def kthsmallest(self, A, k):
    if not A: return 0
    if k > len(A): return 0
    min_val, max_val = min(A), max(A)
    return self.kthsmallest_binary(A, min_val, max_val, k)
Anubhav Agarwal
sumber
0

Ada juga satu algoritma, yang mengungguli algoritma quickselect. Ini disebut algoritma Floyd-Rivets (FR) .

Artikel asli: https://doi.org/10.1145/360680.360694

Versi yang dapat diunduh: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf

Artikel Wikipedia https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm

Saya mencoba menerapkan quickselect dan algoritma FR di C ++. Saya juga membandingkannya dengan implementasi standar library C ++ std :: nth_element (yang pada dasarnya adalah introelect hybrid quickselect dan heapselect). Hasilnya adalah quickselect dan nth_element berjalan rata-rata, tetapi algoritma FR berlari kira-kira. dua kali lebih cepat dibandingkan dengan mereka.

Kode contoh yang saya gunakan untuk algoritma FR:

template <typename T>
T FRselect(std::vector<T>& data, const size_t& n)
{
    if (n == 0)
        return *(std::min_element(data.begin(), data.end()));
    else if (n == data.size() - 1)
        return *(std::max_element(data.begin(), data.end()));
    else
        return _FRselect(data, 0, data.size() - 1, n);
}

template <typename T>
T _FRselect(std::vector<T>& data, const size_t& left, const size_t& right, const size_t& n)
{
    size_t leftIdx = left;
    size_t rightIdx = right;

    while (rightIdx > leftIdx)
    {
        if (rightIdx - leftIdx > 600)
        {
            size_t range = rightIdx - leftIdx + 1;
            long long i = n - (long long)leftIdx + 1;
            long long z = log(range);
            long long s = 0.5 * exp(2 * z / 3);
            long long sd = 0.5 * sqrt(z * s * (range - s) / range) * sgn(i - (long long)range / 2);

            size_t newLeft = fmax(leftIdx, n - i * s / range + sd);
            size_t newRight = fmin(rightIdx, n + (range - i) * s / range + sd);

            _FRselect(data, newLeft, newRight, n);
        }
        T t = data[n];
        size_t i = leftIdx;
        size_t j = rightIdx;
        // arrange pivot and right index
        std::swap(data[leftIdx], data[n]);
        if (data[rightIdx] > t)
            std::swap(data[rightIdx], data[leftIdx]);

        while (i < j)
        {
            std::swap(data[i], data[j]);
            ++i; --j;
            while (data[i] < t) ++i;
            while (data[j] > t) --j;
        }

        if (data[leftIdx] == t)
            std::swap(data[leftIdx], data[j]);
        else
        {
            ++j;
            std::swap(data[j], data[rightIdx]);
        }
        // adjust left and right towards the boundaries of the subset
        // containing the (k - left + 1)th smallest element
        if (j <= n)
            leftIdx = j + 1;
        if (n <= j)
            rightIdx = j - 1;
    }

    return data[leftIdx];
}

template <typename T>
int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}
L'ahim
sumber
-1

Apa yang akan saya lakukan adalah ini:

initialize empty doubly linked list l
for each element e in array
    if e larger than head(l)
        make e the new head of l
        if size(l) > k
            remove last element from l

the last element of l should now be the kth largest element

Anda bisa menyimpan pointer ke elemen pertama dan terakhir dalam daftar yang ditautkan. Mereka hanya berubah ketika pembaruan daftar dibuat.

Memperbarui:

initialize empty sorted tree l
for each element e in array
    if e between head(l) and tail(l)
        insert e into l // O(log k)
        if size(l) > k
            remove last element from l

the last element of l should now be the kth largest element
Jasper Bekkers
sumber
Bagaimana jika e lebih kecil dari kepala (l)? Itu masih bisa lebih besar dari elemen terbesar ke-K, tetapi tidak akan pernah ditambahkan ke daftar itu. Anda perlu mengurutkan daftar item agar ini berfungsi, dalam urutan menaik.
Elie
Kau benar, kurasa aku harus memikirkan ini lagi. :-)
Jasper Bekkers
Solusinya adalah dengan memeriksa apakah e berada di antara head (l) dan tail (l) dan masukkan pada posisi yang benar jika itu. Membuat ini O (kn). Anda bisa membuatnya O (n log k) saat menggunakan pohon biner yang melacak elemen min dan max.
Jasper Bekkers
-1

Pertama kita dapat membangun BST dari array yang tidak disortir yang membutuhkan waktu O (n) dan dari BST kita dapat menemukan elemen terkecil k dalam O (log (n)) yang secara keseluruhan menghitung ke urutan O (n).

pengguna2601131
sumber