O (n) 'de n uzunluğunda sıralanmamış bir dizideki kth en büyük elemanı nasıl bulabilirim?


220

O (n) n uzunluğunda sıralanmamış bir dizi kth büyük elemanı bulmak için bir yol olduğuna inanıyorum. Ya da belki "beklenen" O (n) falan. Bunu nasıl yapabiliriz?


49
Bu arada, burada açıklanan hemen hemen her algoritma k == n olduğunda O (n ^ 2) veya O (n log n) haline dönüşür. Yani, k'nin tüm değerleri için tek bir tanesinin O (n) olduğunu düşünmüyorum. Bunu işaret ettiğim için modifiye oldum ama yine de bilmen gerektiğini düşündüm.
Kirk Strauser

19
Seçim algoritmaları sabit herhangi bir k değeri için O (n) olabilir. Yani, k = 25 için, n'nin herhangi bir değeri için O (n) olan bir seçim algoritmanız olabilir ve bunu, n ile ilgisi olmayan belirli bir k değeri için yapabilirsiniz. Algoritmanın artık O (n) olmadığı durum, k değerinin n = değerine k = n veya k = n / 2 gibi bir bağımlılığı olduğu zamandır. Ancak bu, k = 25 algoritmasını 25 öğeden oluşan bir listede çalıştırırsanız aniden artık O (n) olmadığı anlamına gelmez, çünkü O gösterimi algoritmanın belirli bir özelliğini değil, bir özelliğini açıklar koşmak.
Tyler McHenry

1
Bu soruyu bir Amazon röportajında ​​ikinci en büyük unsuru bulmanın genel bir örneği olarak sordum. Görüşmeci röportajı yönetirken, orijinal diziyi yok edip edemeyeceğimi sormadım (yani onu sıralayabilirim), bu yüzden karmaşık bir çözüm buldum.
Sambatyon

4
Bu, Jon Bentley'nin Programlama İncilerinin 11. Sütununda (Sıralama) Soru 9'dur.
Qiang Xu

3
@KirkStrauser: k == n veya k == n-1 ise önemsiz hale gelir. Tek geçişte maksimum veya 2. maksimum alabiliriz. Bu yüzden burada sağlanan algoritmalar pratik olarak {1,2, n-1, n} 'ye ait olmayan k değerleri için kullanılacaktır
Aditya Joshee

Yanıtlar:


173

Buna k . Dereceden istatistiği bulma denir . Ortalama zaman, en kötü durum süresi alan çok basit bir randomize algoritma ( quickselect olarak adlandırılır ) ve en kötü durum zamanını alan oldukça karmaşık rastgele olmayan bir algoritma ( introselect olarak adlandırılır ) vardır . Wikipedia hakkında bazı bilgiler var , ama çok iyi değil.O(n)O(n^2)O(n)

İhtiyacınız olan her şey bu powerpoint slaytlarında . Sadece en O(n)kötü durum algoritmasının (introselect) temel algoritmasını çıkarmak için :

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)

Cormen ve ark.'nın Algoritma Giriş kitabında da çok güzel detaylandırılmıştır.


6
Slaytlar için teşekkürler.
Kshitij Banerjee

5
Neden 5 bedende çalışması gerekiyor? Neden 3 bedenle çalışamıyor?
Joffrey Baratheon

11
@eladv Slayt bağlantısı kesildi :(
Misha Moroshko

7
@eladv Plese bozuk bağlantıyı düzeltir.
maxx777

1
@MishaMoroshko bağlantısı düzeltildi
alfasin

118

Eğer gerçek istiyorsanız O(n)aksine, algoritma O(kn)veya buna benzer bir şey, o zaman (sen ilgilenen olmadığınızı bölümü dışarı atmak nerede temelde quicksort var) QuickSelect kullanmalıdır. Prof profilimin çalışma zamanı analizi ile harika bir yazımı var: ( referans )

QuickSelect algoritması, ayrıştırılmamış bir öğe dizisinin k. En küçük öğesini hızlı bir şekilde bulur n. Bu bir olan RandomizedAlgorithm yüzden kötü durum hesaplamak, beklenen çalışma süresi.

İşte 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

Bu algoritmanın çalışma süresi nedir? Düşman bizim için paraları çevirir, biz Pivot her zaman büyük unsurdur ve fark edebilirsiniz kçalışan bir zaman vererek, daima 1'dir

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

Ancak seçenekler gerçekten rastgele ise, beklenen çalışma süresi

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

Biz tekrarlama hep daha büyük topraklarda değil tamamen makul varsayım yapıyoruz nerede A1ya A2.

T(n) <= anBazıları için tahmin edelim a. Sonra anlıyoruz

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

ve şimdi bir şekilde artı işaretinin sağındaki korkunç toplamı cnsoldaki emmeyi almak zorundayız . Eğer sadece onu bağlarsak , kabaca alırız . Ama bu çok büyük - fazladan sıkmak için yer yok . Şimdi aritmetik seri formülünü kullanarak toplamı genişletelim: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

burada n'nin çirkin floor(n/2)faktörleri daha temiz (ve daha küçük) ile değiştirmek için "yeterince büyük" olmasından yararlanıyoruz n/4. Şimdi devam edebiliriz

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

sağladı a > 16c.

Bu verir T(n) = O(n). Açıkça Omega(n), yani anlıyoruz T(n) = Theta(n).


12
Hızlı seçim ortalama durumda yalnızca O (n) 'dir. Medyan-medyan algoritması, en kötü durumda O (n) zamanında problemi çözmek için kullanılabilir.
John Kurlak

Ne anlama geliyor k > length(A) - length(A2)?
WoooHaaaa

bu O (n) değil, işlevi tekrar tekrarlayan (T (n)) olarak adlandırıyorsunuz. Yinelenen işlev T (n) içinde zaten bir O (n) var, bu yüzden açıkça düşünmeden, genel karmaşıklık O (n) 'den daha büyük olurdu.
user1735921

3
@MRROY Pivotun Aiçine A1ve A2etrafına bölündüğümüz göz önüne alındığında , bunu biliyoruz length(A) == length(A1)+length(A2)+1. Yani, k > length(A)-length(A2)eşdeğerdir k > length(A1)+1, ki kbu bir yerde olduğunda doğrudur A2.
Filipe Gonçalves

@ FilipeGonçalves, evet pivotta yinelenen eleman yoksa. len (A1) + len (A2) + K-kopyası = len (A)
d1val

16

Bu konuda hızlı bir Google ('k. En büyük öğe dizisi') şunu döndürdü: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17

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

(özellikle en büyük 3d içindi)

ve bu cevap:

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)

15
Peki, aslında O (n) + O (k log n), K'nin önemli değerleri için azalmaz
Jimmy

2
Ancak, bu iki bağlantılı listeye ekleme noktasını bulmak O (k) 'dir.
Kirk Strauser

1
Ve k sabitse, O (k) = O (1)
Tyler McHenry

1
@warren: Big-O yaklaşıyor, ama her zaman aşırı yaklaşıyorsunuz. Quicksort aslında O (n ^ 2) 'dir, çünkü en kötü durum budur. bu O (n + k log n) 'dir.
Claudiu

1
k'yı sabit olarak göremezsiniz. K = n'nin bu durumda zaman karmaşıklığının O (nlogn) olması mümkündür
sabbir

11

Quicksort'u seviyorsun. Bir öğeyi rastgele seçin ve her şeyi daha yüksek veya daha düşük şekilde itin. Bu noktada gerçekte hangi elemanı seçtiğinizi bileceksiniz ve eğer yaptığınız kth elemanı ise, aksi takdirde kth elemanının düşeceği çöp kutusu (daha yüksek veya daha düşük) ile tekrar edersiniz. kth elementinin n, O (n) ile büyüdüğünü bulmak gerekir.


2
Hızlı seçim budur, FWIW.
rogerdpack

6

Algoritma Analizi A Programcı Companion bir versiyonunu verir ise sabit faktör kadar yüksek olduğunu yazar devletleri rağmen, muhtemelen naif sort--listesi-o-select yöntemi tercih ediyorum, O (n).

Sorunuzun mektubunu cevapladım :)


2
Her durumda gerçekten doğru değil. Medyan-medyan uyguladım ve .NET'te yerleşik Sort yöntemiyle karşılaştırdım ve özel çözüm gerçekten büyüklük sırasına göre daha hızlı koştu. Şimdi asıl soru şu: verilen koşullarda bu sizin için önemli mi? Bir astarla karşılaştırıldığında 100 satırlık kod yazma ve hata ayıklama, yalnızca bu kod çalıştırılacaksa, kullanıcının çalışma süresindeki farkı fark etmeye başlaması ve işlemin tamamlanmasını bekleyen rahatsızlık hissetmesi durumunda işe yarar.
Zoran Horvat

5

Verilerinizi değiştirmesine rağmen, C ++ standart kitaplığı neredeyse tam olarak bu işlev çağrısına sahiptir nth_element. Doğrusal çalışma zamanı, O (N) bekleniyor ve kısmi bir sıralama yapıyor.

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

1
Hayır, beklenen ortalama O (n) çalışma süresine sahiptir. Örneğin, çabuk sıralama ortalama O (nlogn) olur ve en kötü O (n ^ 2) olur. Vay be, düz bir şey aslında yanlış!
Kirk Strauser

5
Hayır, bu cevapta hiçbir yanlışlık yok. Çalışır ve C ++ standardı beklenen bir doğrusal çalışma süresi gerektirir.
David Nehme

Röportajda benden O (k) 'nin yer uygunluğunu varsaymam istendi ve' n 'çok büyük. Nth_element'in o (n) boşluğuna ihtiyacı olacağını düşündüğüm için ona O (n) çözümünü söyleyemedim. Yanlış mı? Altta yatan algoritma nth_element için quicksort dayanmıyor?
Manish Baphna

4

O (n) karmaşıklığından emin olmasa da, O (n) ve nLog (n) arasında olacağından emin olacaktır. Ayrıca O (n) 'ye nLog (n)' den daha yakın olduğunuzdan emin olun. İşlev Java ile yazılmıştır

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;
    }
}

Güzel kodlama, +1. Ancak fazladan alan kullanmaya gerek yoktur.
Hengameh

4

Dinamik programlamayı, özellikle de turnuva yöntemini kullanarak ayrıştırılmamış öğelerde kth minimimum bulmayı uyguladım. Yürütme süresi O (n + klog (n)) şeklindedir. Kullanılan mekanizma Wikipedia sayfasında Seçim Algoritması ile ilgili yöntemlerden biri olarak listelenmiştir (yukarıdaki gönderilerden birinde belirtildiği gibi). Sen algoritması hakkında okumak ve aynı zamanda benim blog sayfasında kod (java) bulabilirsiniz Bulma Kth Asgari . Buna ek olarak, mantık listenin kısmi sıralamasını yapabilir - O (klog (n)) zamanında ilk K min (veya maks.) Değerini döndürün.

Verilen kod minimum kth sonucuna rağmen, turnuva ağacı oluşturmak için yapılan ön çalışmaları göz ardı ederek O (klog (n)) 'de kth maksimumu bulmak için benzer mantık kullanılabilir.


3

Gördüğünüz en büyük k öğelerini takip ederek zaman için O (n + kn) = O (n) (sabit k için) ve boşluk için O (k) olarak yapabilirsiniz.

Dizideki her öğe için en büyük k listesini tarayabilir ve daha büyükse en küçük öğeyi yenisiyle değiştirebilirsiniz.

Warren'ın öncelikli yığın çözümü daha temiz.


3
Bu, en küçük öğe için sorulduğunda en kötü O (n ^ 2) durumuna sahip olacaktır.
Elie

2
"En küçük öğe" k = n olduğu için k artık sabit değildir.
Tyler McHenry

Ya da belki de şimdiye kadar gördüğünüz en büyük k'nin bir yığınını (veya ters yığınını veya dengeli bir ağacı) tutun O(n log k)... büyük k durumunda hala O'ya (nlogn) dejenere olur. Ancak k küçük değerleri için iyi çalışacağını düşünürdüm ... ... muhtemelen burada belirtilen diğer algoritmalardan daha hızlı [???]
rogerdpack

3

Seksi hızlı seçim içinde piton

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]

Güzel bir çözüm, ancak bunun sıralanmamış bir listedeki kth en küçük elemanı döndürmesi . Liste tanımlama karşılaştırma operatörlerini Geri vites, a1 = [i for i in arr if i > arr[r]]ve a2 = [i for i in arr if i < arr[r]], kth dönecektir büyük elemanı.
gumption

Küçük bir ölçütten, hatta büyük dizilerde bile, bu manuel uygulamayı kullanmaktan (listeler numpy.sortiçin numpy arrayveya sortedlisteler için) sıralama daha hızlıdır .
Næreen

2

Dizinin medyanını lineer zamanda bulun, sonra diziyi iki parçaya bölmek için tam olarak hızlı sıralamada olduğu gibi bölüm prosedürünü kullanın, değerleri medyandan daha küçük (<) soldan ve (>) medyandan daha büyük sağdan , bu da dizilim zamanında yapılabilir, şimdi, dizinin kth öğesinin bulunduğu kısmına gidin, Şimdi nüks olur: T (n) = T (n / 2) + cn, bana O (n) fazlalık verir.


Ortanca bulmaya gerek yok. medyan olmadan yaklaşımınız hala iyi.
Hengameh

2
Ve medyanı lineer zamanda nasıl buluyorsun, sormaya cesaret edebilirim? ... :)
rogerdpack

2

Aşağıda, sıralanmamış bir algoritmada Kth öğesini bulma algoritmasının nasıl çalıştığına dair kapsamlı bir açıklama ile tam uygulamaya bağlantı verilmiştir. Temel fikir diziyi QuickSort'ta olduğu gibi bölümlendirmektir. Ancak aşırı durumlardan kaçınmak için (örneğin her adımda pivot olarak en küçük eleman seçildiğinde, algoritma O (n ^ 2) çalışma süresinde dejenere olur), medyan-of-median algoritması adı verilen özel pivot seçimi uygulanır. Bütün çözelti en kötü ve ortalama durumda O (n) zamanında çalışır.

İşte tam makaleye bağlantı (Kth'ın en küçük öğesini bulmakla ilgilidir , ancak prensip Kth'ı en büyük bulmak için aynıdır ):

Sıralanmamış Bir Dizide Kth En Küçük Öğeyi Bulma


2

Bu makaleye göre n öğe listesindeki en büyük Kth öğesini bulmak, aşağıdaki algoritmanın O(n)en kötü durumda zaman alacaktır .

  1. Diziyi, her biri 5 öğeden oluşan n / 5 listesine bölün.
  2. 5 öğeden oluşan her alt dizideki medyanı bulun.
  3. Özyinelemeli olarak tüm medyanların medyanını bulun, buna M diyelim
  4. Diziyi iki alt dizi 1. alt diziye ayırın, M'den daha büyük öğeleri içerir, diyelim ki bu alt dizinin a1 olduğunu, diğer alt dizinin de M'den küçük öğeleri içerdiğini, bu alt diziye a2 diyelim.
  5. K <= | a1 | ise, seçimi döndürün (a1, k).
  6. K− 1 = | a1 | ise, M'ye geri dönün.
  7. K> | a1 | + 1, dönüş seçimi (a2, k −a1 - 1).

Analiz: Orijinal makalede önerildiği gibi:

Listeyi iki yarıya bölmek için medyanı kullanıyoruz (ilk yarı, eğer k <= n/2ve ikinci yarı aksi takdirde). Bu algoritma, zaman alır cnbazı sabiti yineleme ilk seviyede c, cn/2(biz boyutu, n / 2 arasında bir listede Recurse beri), bir sonraki düzeyde cn/4üçüncü seviyede, vb. Toplam süre cn + cn/2 + cn/4 + .... = 2cn = o(n).

Bölüm boyutu neden 3 değil 5 alınır?

Orijinal makalede belirtildiği gibi :

Listeyi 5'e bölmek, en kötü durumun 70 - 30 arasında bölünmesini sağlar. Medyanların medyandan daha büyük olan yarısının en az yarısı, dolayısıyla n / 5 bloklarının en az yarısının en az 3 elementi vardır ve bu 3n/10, diğer bölümün en kötü durumda 7n / 10 olduğu anlamına gelir. Bu T(n) = T(n/5)+T(7n/10)+O(n). Since n/5+7n/10 < 1, en kötü çalışma süresini verir O(n).

Şimdi yukarıdaki algoritmayı şu şekilde uygulamaya çalıştım:

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];
    }

Sadece tamamlama uğruna, başka bir algoritma Öncelik Kuyruğunu kullanır ve zaman alır 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;
    }

Bu algoritmaların her ikisi de şu şekilde test edilebilir:

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));
    }

Beklendiği gibi çıktı: 18 18


@rogerdpack Takip ettiğim bağlantıyı sağladım.
akhil_mittal

2

Bu tür yaklaşıma ne dersiniz?

A buffer of length kve tmp_maxa'yı koruyun , tmp_max elde etmek O (k) 'dir ve n kez yapılır, böyleceO(kn)

resim açıklamasını buraya girin

Doğru mu yoksa bir şey mi kaçırıyorum?

Her ne kadar ortalama hızlı seçim durumu ve en kötü medyan istatistik yöntemini yenmezse de, anlaşılması ve uygulanması oldukça kolaydır.


1
Sevdim, anlaşılması daha kolay. Belirttiğiniz gibi karmaşıklık O (nk) olmasına rağmen.
Hacat

1

listeyi tekrarlayın. geçerli değer saklanan en büyük değerden büyükse, değeri en büyük değer olarak saklayın ve 1-4 aşağı ve 5 damla listeden çarpın. Değilse, 2 numarayla karşılaştırın ve aynı şeyi yapın. Kaydedilen 5 değerin tümünü kontrol ederek tekrarlayın. bunu O (n) de yapmalı


Bir dizi kullanıyorsanız bu "yumru" O (n) veya daha iyi bir yapı kullanıyorsanız O (log n) (sanırım) aşağı.
Kirk Strauser

O (günlük k) olması gerekmez - liste bağlantılı bir listeyse, yeni öğeyi en üste eklemek ve son öğeyi bırakmak daha çok O (2)
Alnitak

Tümsek, dizi destekli bir liste için O (k), uygun şekilde bağlanmış bir liste için O (1) olacaktır. Her iki durumda da, bu tür bir soru genellikle n'ye nazaran daha az etkili olduğunu varsayar ve n'ye daha fazla faktör getirmez.
bobince

yumru bir halka tampon kullanırsa O (1) olurdu
Alnitak

1
Her neyse, yorumun algoritması eksik, n'nin yeni (örneğin) ikinci en büyük olan bir unsuru olarak düşünülemiyor. N'deki her öğenin yüksek puan tablosundaki her öğeyle karşılaştırılması gereken en kötü durum davranışı O (kn) 'dur - ancak yine de soru açısından muhtemelen O (n) anlamına gelir.
bobince

1

bir cevap önermek istiyorum

ilk k öğelerini alıp bunları bağlantılı k değerleri listesine ayırırsak

şimdi en kötü durumda bile diğer tüm değerler için en kötü durumda bile dinlenme nk değerleri için ekleme sıralaması yaparsak karşılaştırma sayısı k * (nk) olur ve sıralanacak önceki k değerleri için k * (k- 1) yani o (n) olan (nk-k)

şerefe


1
sıralama nlogn zaman alır ... algoritma doğrusal zamanda çalışmalıdır
MrDatabase

1

N'den k'ıncı en büyük tamsayıyı bulmak için medyan - medyan algoritmasının açıklaması burada bulunabilir: http://cs.indstate.edu/~spitla/presentation.pdf

C ++ ile uygulama aşağıdadır:

#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;
}

Bu çözüm işe yaramıyor. 5 element durumu için medyanı döndürmeden önce diziyi sıralamanız gerekir.
Agnishom Chattopadhyay

1

Orada da Wirth'in seçim algoritması QuickSelect göre daha kolaydır uygulama vardır. Wirth'in seçim algoritması QuickSelect'ten daha yavaştır, ancak bazı iyileştirmelerle daha hızlı hale gelir.

Daha ayrıntılı olarak. Vladimir Zabrodsky'nin MODIFIND optimizasyonunu ve medyan-3 pivot seçimini kullanarak ve algoritmanın bölümleme bölümünün son adımlarına biraz dikkat ederek, aşağıdaki algoritmayı (hayal edilebilir şekilde "LefSelect" olarak adlandırdım) buldum:

#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];
}

Burada yaptığım kriterlerde LefSelect, QuickSelect'ten% 20-30 daha hızlı.


1

Haskell Çözümü:

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)

Bu, bir bölümün boyutunu gerçekten hesaplamadan keşfetmek için withShape yöntemini kullanarak medyan çözümlerin medyanını uygular.


1

İşte Randomized QuickSelect'in bir C ++ uygulaması. Fikir rastgele bir pivot elemanı seçmek. Rastgele bölüm uygulamak için, l ve r arasında dizin oluşturmak için rastgele bir işlev, rand () kullanırız, rastgele oluşturulan dizindeki öğeyi son öğeyle değiştiririz ve son olarak pivot olarak son öğeyi kullanan standart bölüm işlemini çağırırız.

#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;
}

Yukarıdaki çözeltinin en kötü zaman karmaşıklığı hala O (n2) 'dir. En kötü durumda, randomize fonksiyon her zaman bir köşe elemanı seçebilir. Yukarıdaki randomize QuickSelect'in beklenen zaman karmaşıklığı Θ (n)


Güzel kodlama.
Paylaştığınız

1
  1. Öncelik kuyruğunu oluşturun.
  2. Tüm öğeleri yığın içine yerleştirin.
  3. Anket () k kez çağırın.

    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;        
    }
    

0

Bu Javascript bir uygulamadır.

Diziyi değiştiremediğiniz kısıtlamayı kaldırırsanız, "geçerli bölümü" (klasik hızlı sıralama tarzında - http://www.nczonline.net/blog/2012/) tanımlamak için iki dizin kullanarak fazladan bellek kullanımını engelleyebilirsiniz. 11/27 / javascript-quicksort bilgisayar bilimi / ).

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;
        }   
    }
}  

Nasıl çalıştığını test etmek istiyorsanız, bu varyasyonu kullanabilirsiniz:

    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};
     }

Kodun geri kalanı sadece oyun alanı oluşturmak için:

    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);   

Şimdi, birkaç kez test yapın. Math.random () nedeniyle her seferinde farklı sonuçlar üretecektir:

    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);

Birkaç kez test ederseniz, yinelemenin sayısının ortalama olarak O (n) ~ = sabit * n olduğunu ve k değerinin algoritmayı etkilemediğini ampirik olarak görebilirsiniz.


0

Ben bu algoritma ile geldi ve O (n) gibi görünüyor:

Diyelim ki k = 3 ve dizideki en büyük 3. öğeyi bulmak istiyoruz. Üç değişken oluşturur ve dizinin her bir öğesini bu üç değişkenin minimum değeriyle karşılaştırırdım. Dizi öğesi minimumumuzdan büyükse, min değişkenini öğe değeriyle değiştiririz. Dizinin sonuna kadar aynı şeyi sürdürüyoruz. Üç değişkenimizin minimum değeri dizideki 3. en büyük öğedir.

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

Kth'ın en büyük maddesini bulmak için K değişkenlerine ihtiyacımız var.

Örnek: (k = 3)

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

Final variable values:

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

Birisi lütfen bunu gözden geçirip eksik olduğumu bana bildirebilir mi?


0

İşte eladv önerilen algoritmanın uygulanması (Ben de burada rastgele pivot ile uygulama koymak):

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);
    }
}

0

keyfi bir pivot seçtiğimiz ve daha küçük elemanları sola, daha büyük olanı sağa getirdiğimiz quickSort stratejisine benzer.

    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);
    }


0

En küçük elementi O (n) zaman ve sabit boşlukta bulabilirsiniz. Dizinin yalnızca tamsayılar için olduğunu düşünürsek.

Yaklaşım, Array değerleri aralığında ikili bir arama yapmaktır. Tamsayı aralığında hem min_value hem de max_value varsa, bu aralıkta ikili arama yapabiliriz. Herhangi bir değerin kth-small veya kth-smallest'den daha küçük veya kth-smallest'den daha büyük olup olmadığını bize söyleyen bir karşılaştırıcı işlevi yazabiliriz. En küçük sayıya ulaşana kadar ikili aramayı yapın

İşte bunun kodu

sınıf Çözümü:

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)

0

Hızlı seçim algoritmasından daha iyi performans gösteren bir algoritma da vardır. Adı Floyd-Rivets (FR) algoritması .

Orijinal makale: https://doi.org/10.1145/360680.360694

İndirilebilir sürüm: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf

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

C ++ 'da quickselect ve FR algoritması uygulamaya çalıştım. Ayrıca onları standart C ++ kütüphane uygulamaları ile karşılaştırdım std :: nth_element (temel olarak hızlı seçim ve heapselect'in introselect hibriti). Sonuç hızlı seçildi ve nth_element ortalama olarak karşılaştırılabilir, ancak FR algoritması yakl. iki kat daha hızlı.

FR algoritması için kullandığım örnek kod:

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));
}

-1

Ne yapacağım şudur:

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

İşaretçileri bağlı listedeki ilk ve son öğeye depolayabilirsiniz. Yalnızca listede güncellemeler yapıldığında değişir.

Güncelleme:

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

E baş (l) 'den küçükse ne olur? Hala en büyük k'inci elementten daha büyük olabilir, ancak asla bu listeye eklenmez. Bunun çalışabilmesi için öğe listesini artan sırada sıralamanız gerekir.
Elie

Haklısın, sanırım bunu biraz daha düşünmem gerekecek. :-)
Jasper Bekkers

Çözüm, e'nin kafa (l) ile kuyruk (l) arasında olup olmadığını kontrol etmek ve varsa doğru pozisyonda yerleştirmek olacaktır. Bunu O (kn) yapmak. Min ve maks öğelerini izleyen bir ikili ağaç kullanırken O (n log k) yapabilirsiniz.
Jasper Bekkers

-1

İlk önce O (n) zaman alan sıralanmamış diziden bir BST oluşturabiliriz ve BST'den O (log (n)) 'deki kth en küçük elementi bulabiliriz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.