有效计算已排序数组中键的出现次数的方法

时间:2010-12-01 08:08:51

标签: arrays algorithm

在微软的现场采访中提到了这一点。

计算数组中给定键的出现次数。

我回答了线性搜索,因为元素可能会分散在 阵列。说密钥在开头和结尾都有。所以我们 需要扫描整个阵列。

接下来他询问阵列是否已排序?

想了一会儿,说我会再次使用线性搜索。因为 密钥的重复(如果存在)可以在阵列中的任何位置。作为一个 优化我还说如果第一个和最后一个数组元素是相同的 可以将数组长度作为答案。

两种情况下我的分析是否正确?

示例:

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 个答案:

答案 0 :(得分:28)

对于未排序的数组,我们除了线性搜索之外没有什么可做的。

对于排序数组,您可以使用略微修改的二进制搜索在O(logN)中执行此操作:

  • 找到第一次出现的索引 key,将其称为f
  • 查找上次出现的索引 key,将其称为l
  • 如果数组key中存在l-f+1 是答案。

查找第一次出现:

arr[i]是第一次出现key iff

  • 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

同样,您可以找到最后一次出现。

答案 1 :(得分:8)

有一次,我将提出一个用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)中(就比较数而言)。

答案 2 :(得分:3)

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

}

答案 3 :(得分:1)

您可以使用二进制搜索的递归版本

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)给出了键数

答案 4 :(得分:1)

**  时间复杂度 = O(lg N)其中N是数组的大小

** binarySearchXXXXX的参数:**

  1. int [] array是一个长度为&gt; = 1
  2. 的排序数组
  3. int k:搜索的关键
  4. **

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

答案 5 :(得分:1)

对于排序部分,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;
  }
}

答案 6 :(得分:0)

包数组;

/ *  *给定排序数组,查找元素发生的次数。  *二进制搜索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));
}

}

答案 7 :(得分:0)

我们可以使用Linear和Binary Search来解决这个问题。 但线性搜索将是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;
}
}

还有另一个跟进问题是:排序数组中给定数字的第一次和最后一次出现。

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)

答案 8 :(得分:-1)

如果数组未排序,是的,从一端到另一端的线性搜索就像它一样好。

但是,如果对数组进行排序,则可以通过应用二进制或插值搜索技术来比线性时间更好。

将问题视为与“在排序列表中查找数字X”相同,并添加“然后向左和向右扫描以确定X出现的次数”的详细信息。第一部分,搜索,最好在大多数情况下使用二进制或插值搜索。

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

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

答案 9 :(得分:-2)

是的,你对于未排序的数组是正确的,但是对于排序的数组,你可以使用二进制搜索找到元素的一个出现,一旦发现一个出现,只扫描相邻的元素,直到找到不匹配然后停止。