Алгоритм нахождения k наименьших чисел в массиве из n элементов - PullRequest
20 голосов
/ 21 марта 2011

Я пытаюсь написать алгоритм, который может печатать k наименьших чисел в массиве размера n за O (n) времени, но я не могу уменьшить сложность времени до n.Как я могу это сделать?

Ответы [ 12 ]

40 голосов
/ 06 октября 2011

Я уже делал это в интервью раньше, и один из самых элегантных / эффективных способов сделать это -

O(n log k). 
with space: O(k) (thanks, @Nzbuu)

В основном вы будете использовать максимальную кучу размером, ограниченным k. Для каждого элемента в массиве проверьте, не меньше ли он максимального значения (только O (1)). Если это так, поместите это в кучу (O (log k)) и уберите макс. Если оно больше, переходите к следующему пункту.

Конечно, куча не дает отсортированный список из k элементов, но это можно сделать в O (k log k), что легко.

Точно так же вы можете сделать то же самое для поиска самых больших k предметов, в этом случае вы бы использовали минимальную кучу.

25 голосов
/ 21 марта 2011

вам нужно будет найти k 'наименьший элемент, используя' алгоритм выбора ', который равен O (n), а затем снова выполнить итерацию массива и вернуть каждый элемент, который меньше / равен ему.
алгоритм выбора: http://en.wikipedia.org/wiki/Selection_algorithm
вам придется обратить внимание, если у вас есть повторы: вам нужно убедиться, что вы не возвращаете больше, чем k элементов (это возможно, если, например, у вас есть 1,2, ..., k, k, k , ...)

РЕДАКТИРОВАТЬ:
полный алгоритм и возвращение списка в соответствии с запросом: пусть массив будет A

 1. find the k'th element in A using 'selection algorithm', let it be 'z'
 2. initialize an empty list 'L'
 3. initialize counter<-0
 4. for each element in A: 
 4.1. if element < z: 
   4.1.1. counter<-counter + 1 ; L.add(element)
 5. for each element in A:
 5.1. if element == z AND count < k:
   5.1.1. counter<-counter + 1 ; L.add(element)
 6. return L

обратите внимание, что третья итерация необходима, если в вашем списке могут быть дубликаты. если это невозможно - это не нужно, просто измените условие в 4.1 на
также обратите внимание: L.add вставляет элемент в связанный список и, таким образом, имеет значение O (1).

5 голосов
/ 21 марта 2011

Предполагая, что вы пытаетесь показать K наименьших чисел, вы можете использовать алгоритм выбора Хоара, чтобы найти k th наименьшее число.Это делит массив на меньшие числа, число k th и большие числа.

1 голос
/ 12 апреля 2019

Это можно сделать за O (n) время, используя пространство O (n), как я полагаю.Как уже упоминалось, вы можете использовать алгоритм Hoares или вариант быстрого выбора.

По сути, вы запускаете Quicksort для массива, но запускаете только на той стороне раздела, которая необходима для того, чтобы K или K-1 были больше.элементы, отличные от pivot (вы можете включить lr, исключить pivot).Если список не нужно сортировать, то вы можете просто вывести остаток массива из сводной таблицы.Поскольку быстрая сортировка может быть выполнена на месте, для этого требуется пространство O (n), а поскольку вы делите пополам часть массива (в среднем), которую вы проверяете каждый раз, это занимает O (2n) == O (n) времени

1 голос
/ 21 апреля 2017

Другой метод - используйте алгоритм быстрого выбора, и результатом будут все элементы слева от возвращаемого результата. Средняя сложность по времени будет O (n), а в худшем случае будет O (n ^ 2). Пространственная сложность будет O (1).

1 голос
/ 27 января 2017

Я знаю не совсем то, что вы ищете, но довольно простое O (n * k) время и O (k) пространство. Это самая большая буква К, так что нужно будет ее провалить.

Для грубой мин за k (результат) можно заменить кучей

private int[] FindKBiggestNumbersM(int[] testArray, int k)
{
    int[] result = new int[k];
    int indexMin = 0;
    result[indexMin] = testArray[0];
    int min = result[indexMin];

    for (int i = 1; i < testArray.Length; i++)
    {
        if(i < k)
        {
            result[i] = testArray[i];
            if (result[i] < min)
            {
                min = result[i];
                indexMin = i;
            }
        }
        else if (testArray[i] > min)
        {
            result[indexMin] = testArray[i];
            min = result[indexMin];
            for (int r = 0; r < k; r++)
            {
                if (result[r] < min)
                {
                    min = result[r];
                    indexMin = r;
                }
            }
        }
    }
    return result;
}
1 голос
/ 18 октября 2015

Лучшее возможное решение проблемы заключается в следующем. Используйте быструю сортировку, чтобы найти стержни и отбросить часть, в которой этот k-й элемент не лежит, и рекурсивно найти следующий стержень. (Это kth Max finder, вам нужно изменить условие if else, чтобы сделать его kth Min finder). Вот код JavaScript code-

  // Complexity is O(n log(n))
  var source = [9, 2, 7, 11, 1, 3, 14, 22];

  var kthMax = function(minInd, MaxInd, kth) {
      // pivotInd stores the pivot position 
      // for current iteration
      var temp, pivotInd = minInd;
      if (minInd >= MaxInd) {
        return source[pivotInd];
      }

      for (var i = minInd; i < MaxInd; i++) {
        //If an element is greater than chosen pivot (i.e. last element)
        //Swap it with pivotPointer element. then increase ponter
        if (source[i] > source[MaxInd]) {
          temp = source[i];
          source[i] = source[pivotInd];
          source[pivotInd] = temp;
          pivotInd++;
        }
      }
      // we have found position for pivot elem. 
      // swap it to that position place .
      temp = source[pivotInd];
      source[pivotInd] = source[MaxInd];
      source[MaxInd] = temp;

      // Only try to sort the part in which kth index lies.
      if (kth > pivotInd) {
        return kthMax(pivotInd + 1, MaxInd, kth);
      } else if (kth < pivotInd) {
        return kthMax(minInd, pivotInd - 1, kth);
      } else {
        return source[pivotInd];
      }

    }
    // last argument is kth-1 , so if give 2 it will give you,
    // 3rd max which is 11

  console.log(kthMax(0, source.length - 1, 2));
1 голос
/ 16 мая 2014

Можно найти k наименьших из n элементов за O(n) время (под этим я подразумеваю истинное O(n) время, а не O(n + some function of k)). См. статью в Википедии «Алгоритм выбора» , особенно подразделы «Неупорядоченная частичная сортировка» и «Медианный выбор как основную стратегию», а также статью «Медиана медиан» для основной части, которая делает это O(n).

1 голос
/ 21 августа 2013

Это можно сделать за ожидаемое линейное время (O (n)).Сначала найдите kth наименьший элемент массива (используя метод разбиения сводной таблицы для нахождения kth статистики порядка), а затем просто выполните итерацию по циклу, чтобы проверить, какие элементы меньше, чем kth наименьший элемент.Обратите внимание, что это работает правильно только для отдельного элемента.

Вот код в c:

    /*find the k smallest elements of an array in O(n) time. Using the Kth order 
statistic-random pivoting algorithm to find the kth smallest element and then looping 
through the array to find the elements smaller than kth smallest element.Assuming 
distinct elements*/


    #include <stdio.h>
    #include <math.h>
    #include <time.h>
    #define SIZE 10
    #define swap(X,Y) {int temp=X; X=Y; Y=temp;}


    int partition(int array[], int start, int end)
    {
        if(start==end)
            return start;
        if(start>end)
            return -1;
        int pos=end+1,j;
        for(j=start+1;j<=end;j++)
        {       
            if(array[j]<=array[start] && pos!=end+1)
            {
                swap(array[j],array[pos]);
                pos++;
            }
            else if(pos==end+1 && array[j]>array[start])
                pos=j;
        }
        pos--;
        swap(array[start], array[pos]);
        return pos;
    }

    int order_statistic(int array[], int start, int end, int k)
    {
        if(start>end || (end-start+1)<k)
            return -1;                   //return -1 
        int pivot=rand()%(end-start+1)+start, position, p;
        swap(array[pivot], array[start]);
        position=partition(array, start, end);
        p=position;
        position=position-start+1;                  //size of left partition
        if(k==position)
            return array[p];
        else if(k<position)
            return order_statistic(array, start,p-1,k);
        else
            return order_statistic(array,p+1,end,k-position);
    }


    void main()
    {
        srand((unsigned int)time(NULL));
        int i, array[SIZE],k;
        printf("Printing the array...\n");
        for(i=0;i<SIZE;i++)
            array[i]=abs(rand()%100), printf("%d ",array[i]);
        printf("\n\nk=");
        scanf("%d",&k);
        int k_small=order_statistic(array,0,SIZE-1,k);
        printf("\n\n");
        if(k_small==-1)
        {
            printf("Not possible\n");
            return ;
        }
        printf("\nk smallest elements...\n");
        for(i=0;i<SIZE;i++)
        {
            if(array[i]<=k_small)
                printf("%d ",array[i]);
        }
    }
0 голосов
/ 20 февраля 2015

Как уже упоминалось, есть два способа выполнить такую ​​задачу:

1) Вы можете отсортировать весь массив n элементов с помощью быстрой сортировки , heapsort или любого O (n log n) алгоритма сортировки, который вы хотите, а затем выберите m наименьший значения в вашем массиве. Этот метод будет работать в O(n log n).

2) Вы можете использовать алгоритм выбора , чтобы найти m наименьших элементов в вашем массиве. Потребуется O(n) время, чтобы найти наименьшее значение kth , так как вы будете повторять этот алгоритм m раз, общее время будет m x O(n) = O(n).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...