Эффективный способ подсчета вхождений ключа в отсортированный массив - PullRequest
18 голосов
/ 01 декабря 2010

Это было задано в интервью Microsoft на месте.

Подсчитать количество вхождений данного ключа в массиве.

Я ответил на линейный поиск, поскольку элементы могут быть разбросаны вмассив.Скажем, ключ найден в начале и в конце.Поэтому нам нужно просканировать весь массив.

Затем он спросил, что если массив отсортирован?

Подумал некоторое время и сказал, что снова воспользуюсь линейным поиском.Потому что повторы ключа, если они есть, могут быть где угодно в массиве.В качестве оптимизации я также сказал, что если первый и последний элементы массива совпадают, вы можете взять длину массива в качестве ответа.

Правильно ли выполнен мой анализ в обоих случаях?

Пример:

Input = [0 0 1 1 1 2 2 3 3], key = 1, Answer = 3
Input = [0 0 2 2 3 3],       key = 1, Answer = 0

Ответы [ 10 ]

28 голосов
/ 01 декабря 2010

Для несортированного массива мы мало что можем сделать, кроме линейного поиска.

Для отсортированного массива вы можете сделать это в O(logN), используя слегка модифицированный двоичный поиск:

  • Найти индекс первого появления key, назовите это f.
  • Найти индекс последнего появления key, назовите это l.
  • Если key существует в массиве l-f+1 это ответ.

Нахождение первого вхождения:

arr[i] - это первое вхождение key тогда

  • arr[i] == key и либо
    • i == 0 (это первый элемент массив) или
    • arr[i-1] != key (это не первый элемент массива и элемент для осталось другое)

Вы можете слегка изменить бинарный поиск, чтобы найти первое вхождение.
В бинарном поиске вы прекращаете поиск, когда находите arr[mid] == key.
Измените условие так, чтобы вы прекратили поиск, когда найдете первое вхождение вместо любое вхождение.

Алгоритм:

low = 0
high = arrSize - 1 

while low <=  high

  mid = (low + high) / 2

  //if arr[mid] == key         // CHANGE
  if arr[mid] == key AND ( mid == 0 OR arr[mid-1] != key )
    return mid
  //else if ( key < arr[mid] ) // CHANGE
  else if ( key <= arr[mid] ) 
    high = mid - 1
  else
    low = mid + 1        
  end-if

end-while

return -1

Аналогично вы можете найти последнее вхождение.

8 голосов
/ 01 декабря 2010

На этот раз я предложу реализацию на C ++.

size_t count(std::vector<int> const& vec, int key)
{
  auto p = std::equal_range(vec.begin(), vec.end(), key);
  return std::distance(p.first, p.second);
}

equal_range использует бинарный поиск, результат эквивалентен:

std::make_pair(std::lower_bound(vec.begin(), vec.end(), key),
               std::upper_bound(vec.begin(), vec.end(), key);

но реализация должна сделать это немного быстрее, хотя все они в O (log N) (с точки зрения количества сравнений).

3 голосов
/ 06 августа 2014
#include<stdio.h>
int binarysearch(int a[],int n,int k,bool searchfirst){
    int result=-1;
    int low=0,high=n-1;
    while(low<=high){
        int mid=(low+high)/2;
        if(a[mid]==k)  {
              result=mid; 
           if(searchfirst)
              high=mid-1; 
            else
              low=mid+1;
    }
    else if(k<a[mid])  high=mid-1;
    else low=mid+1;
    }
    return result;
}

int main(){
    int a[]={1,1,1,2,2,3,3,3,6,6,6,6,6,7,7};
    int n=sizeof(a)/sizeof(a[0]);
    int x=6;
    int firstindex=binarysearch(a,n,x,true);
    printf("%d\n",firstindex);
    if(firstindex==-1){
        printf("elment not found in the array:\n ");
    }
    else {
        int lastindex=binarysearch(a,n,x,false);
        printf("%d\n",lastindex);
        printf("count is = %d", lastindex-firstindex+1);
    }

}
1 голос
/ 15 сентября 2013

Как насчет этого для отсортированной части, со сложностью времени O (logN)?

int count(int a[], int k, int l, int h) {
  if (l>h) {
    return 0;
  }
  int mid = (l+h)/2;
  if (k > a[mid]) {
     return count(a, k, mid+1, h);
  }
  else if (k < a[mid]) {
     return count(a, k, l, mid-1);
  }
  else {
     return count(a, k, mid+1, h) + count(a, k, l, mid-1) + 1;
  }
}
1 голос
/ 16 сентября 2012

** Сложность времени = O (lg N), где N - размер массива

** Аргументы для binarySearchXXXXX: **

  1. int [] массив - это отсортированный массив длины> = 1
  2. int k: ключ для поиска

**

package array;

 public class BinarySearchQuestion {

public static int binarySearchFirst(int[] array, int k) {
    int begin = 0;
    int end = array.length-1;
    int mid = -1;
    while (begin <= end) {
        mid = begin + (end - begin) / 2;
        if (array[mid] < k) {
            begin = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    //System.out.println("Begin index :: " + begin + " ,  array[begin] " + array[begin]);
    return (begin <= array.length - 1  && begin >= 0 && array[begin] != k) ? -1 : begin;
    //      return begin;
}

public static int binarySearchLast(int[] array, int k) {
    int begin = 0;
    int end = array.length - 1;
    int mid = -1;
    while (begin <= end) {
        mid = begin + (end - begin) / 2;
        if (array[mid] > k) {
            end = mid - 1;
        } else {
            begin = mid + 1;
        }
    }
    //System.out.println("Last index end :: " + end + " ,  array[mid] " + array[end]);
    return (end <= array.length - 1  && end >= 0 &&  array[end] != k) ? -1 : end;
    //return end;
}

/**
 * @param args
 */
public static void main(String[] args) {
             //     int[] array = { 0, 1,1,1, 2, 3, 4,4,4,5, 5, 5, 5, 5, 5, 5, 5, 5, 5,5,6,6,6,6, 6, 7, 7, 7,
             //             7, 8, 9 };
            //      int[] array = {-1, 0,1, 1,1,2,3};
    int[] array = {1,1,1};

    int low = binarySearchFirst(array, 1);
    int high = binarySearchLast(array, 1);
    int total = (high >= low && low != -1 && high != -1) ? ( high - low + 1 ): 0;
    System.out.println("Total Frequency " + total);
}

   }
1 голос
/ 04 декабря 2010

Вы можете использовать рекурсивную версию бинарного поиска

int modifiedbinsearch_low(int* arr, int low, int high , int key)
{   
    if(low==high) return high ; 

    int mid = low + (high-low) /2;

    if(key >  arr[mid] ) { modifiedbinsearch_low(arr,mid + 1 , high,key);  } 
    else  { modifiedbinsearch_low(arr,low,mid,key);  }  
}
int modifiedbinsearch_high(int* arr, int low, int high , int key)
{   
    if(low==high) return high ; 

    int mid = low + (high-low) /2;

    if(key <  arr[mid] ) { modifiedbinsearch_high(arr,low,mid,key);  } 
    else  { modifiedbinsearch_high(arr,mid+1,high,key);  } 

} 

.

int low = modifiedbinsearch_low( ...)

int high = modifiedbinsearch_high( ...)

(high - low) дает количество ключей

0 голосов
/ 21 апреля 2018

Мы можем решить эту проблему, используя как линейный, так и бинарный поиск. Но линейный поиск будет O (n). Двоичный поиск даст O (Logn). Следовательно, лучше использовать бинарный поиск. Полная программа:

public class Test4 {
public static void main(String[] args) {
     int a[] = {1, 2, 2, 3, 3, 3, 6,6,6,6,6,66,7}; 
     int x =  6; 

         System.out.println(fix(a,x));
}

private static int fix(int[] a, int x) {
    int res = 0 ;

    for (int i = 0; i < a.length; i++) {
        int ch = a[i];
        if(x == ch) {res++ ;}
    }
    return res;
}
}

Есть еще один вопрос, который задают: 1-й и последний вхождения заданного числа в отсортированный массив.

class Occurence1 {

    public static void findFirstAndLast(int a[], int x) {

        int first = -1, last = -1;
        for (int i = 0; i < a.length; i++) {
            if (x == a[i]) {
                if (first == -1) {
                    first = i;
                }
                // update last
                last = i;
            } // if

        } // for                                                                           
        if (first != -1) {
            System.out.println("First Occurrence = " + first);
            System.out.println("Last Occurrence = " + last);
        } 
    }// end1

    public static void main(String[] args) {
        int arr[] = { 1, 2, 2, 2, 2, 3, 4, 7, 8, 8 };
        int x = 8;
        findFirstAndLast(arr, x);
    }
}

В Python:

def findFirstAndLast(a, x):
    first = -1 ; last = -1
    for i in range(len(a)) :
        if(x == a[i]): 
            if(first == -1):first = i 

         # update last if the first contains oter value than -1    
        last = i

    if(first != -1):
        print("first => ",first)
        print("last =>", last)       


a = [1, 2, 3,4, 5, 6, 7, 8, 1, 10, 10]
x = 10
findFirstAndLast(a, x)
0 голосов
/ 06 января 2017

массив пакетов;

/ * * Учитывая отсортированный массив, найдите количество раз, когда элемент произошел. * Бинарный поиск O (lgn) * * /

открытый класс NumberOfN {

static int bSearchLeft(int[] arr, int start, int end, int n){

    while(start < end){

        int mid = (start + end)>>1;
        if(arr[mid] < n){
            start = mid + 1;
        }else{
            end = mid;
        }

    }

    return end;
}

static int bSearchRight(int[] arr, int start, int end, int n){

    while(start < end){

        int mid = (start + end)>>1;
        if(arr[mid] <= n){
            start = mid + 1;
        }else{
            end = mid;
        }

    }

    return end;
}

/**
 * @param args
 */
public static void main(String[] args) {

    int[] arr = new int[]{3,3,3,3};
    int n = 3;
    int indexLeft = bSearchLeft(arr, 0, arr.length, n);
    int indexRight = bSearchRight(arr, 0, arr.length, n);
    System.out.println(indexLeft + " " +indexRight);
    System.out.println("Number of occurences: " + (indexRight - indexLeft));
}

}

0 голосов
/ 01 декабря 2010

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

0 голосов
/ 01 декабря 2010

Если массив не отсортирован, то да, линейный поиск с одного конца до другого так же хорош, как он получает.

Однако, если массив отсортирован, вы можете добиться большего успеха, чем линейное время, применяя методы двоичного или интерполяционного поиска.

Относитесь к проблеме так же, как к «Найти число X в отсортированном списке» с добавленной деталью «затем отсканируйте влево и вправо, чтобы определить, сколько раз появится X». Первая часть, поиск, в большинстве случаев лучше всего выполнять с двоичным или интерполяционным поиском.

http://en.wikipedia.org/wiki/Interpolation_search

http://en.wikipedia.org/wiki/Binary_search

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