web-dev-qa-db-ja.com

重複したソート済み配列でのバイナリ検索の使用

私は、値xがソートされた配列で見つかるすべてのインデックスを出力するメソッドの作成を任されています。

配列を0からN(配列の長さ)までスキャンした場合、実行時間はO(n)最悪の場合)になると理解しています。メソッドはソートされます。これはO(log n)になるため、バイナリ検索を利用できると想定しています。ただし、これは、配列に一意の値がある場合にのみ機能します。バイナリ検索は、特定の値の最初の「検索」。ソートされた配列でxを見つけるためにバイナリ検索を実行し、このインデックスの前後のすべての値をチェックすることを考えていましたが、配列にすべてのx値が含まれている場合、それははるかに良いだろうように見えます。

私が求めているのは、O(n)よりも優れた、ソートされた配列内の特定の値のすべてのインデックスを見つけるより良い方法があるのでしょうか?

public void PrintIndicesForValue42(int[] sortedArrayOfInts)
{
    // search through the sortedArrayOfInts

    // print all indices where we find the number 42. 
}

例:sortedArray = {1、13、42、42、42、77、78}は次のように出力します: "42がインデックスで見つかりました:2、3、4"

18
5StringRyan

まあ、実際に並べ替えられた配列がある場合は、探しているインデックスの1つが見つかるまでバイナリ検索を実行できます。残りのインデックスはすべて隣にあるため、簡単に見つけることができます。その他。

最初のものを見つけたら、それより前のすべてのインスタンスを見つけてから、その後のすべてのインスタンスを見つけます。

その方法を使用すると、おおまかにO(lg(n)+ k)を取得する必要があります。ここでkは、検索する値の出現回数です。

編集:

そして、いいえ、O(k)時間よりも短い値ですべてのk値にアクセスすることはできません。


2番目の編集:実際に役立つものを提供しているように感じられるように:

Xの最初と最後の出現を単に検索する代わりに、最初の出現のバイナリ検索と最後の出現のバイナリ検索を行うことができます。 O(lg(n))合計になります。これを実行すると、すべてのインデックス間のXも含まれていることがわかります(ソートされていると仮定)

これは、値がx[〜#〜] and [〜#〜]に等しいかどうかを検索して検索することで実行できます。最初の出現を探すか最後の出現を探すかに応じて)はxと等しくなります。

16
Sam I am

O(lg n)で結果を取得します

public static void PrintIndicesForValue(int[] numbers, int target) {
    if (numbers == null)
        return;

    int low = 0, high = numbers.length - 1;
    // get the start index of target number
    int startIndex = -1;
    while (low <= high) {
        int mid = (high - low) / 2 + low;
        if (numbers[mid] > target) {
            high = mid - 1;
        } else if (numbers[mid] == target) {
            startIndex = mid;
            high = mid - 1;
        } else
            low = mid + 1;
    }

    // get the end index of target number
    int endIndex = -1;
    low = 0;
    high = numbers.length - 1;
    while (low <= high) {
        int mid = (high - low) / 2 + low;
        if (numbers[mid] > target) {
            high = mid - 1;
        } else if (numbers[mid] == target) {
            endIndex = mid;
            low = mid + 1;
        } else
            low = mid + 1;
    }

    if (startIndex != -1 && endIndex != -1){
        for(int i=0; i+startIndex<=endIndex;i++){
            if(i>0)
                System.out.print(',');
            System.out.print(i+startIndex);
        }
    }
}
28
Xiangyu Xu
public void PrintIndicesForValue42(int[] sortedArrayOfInts) {
    int index_occurrence_of_42 = left = right = binarySearch(sortedArrayOfInts, 42);
    while (left - 1 >= 0) {
        if (sortedArrayOfInts[left-1] == 42)
            left--;
    }
    while (right + 1 < sortedArrayOfInts.length) {
        if (sortedArrayOfInts[right+1] == 42)
            right++;
    }
    System.out.println("Indices are from: " + left + " to " + right);
}

これは、O(log(n) + #occurrences)で実行されます。コードを読んで理解してください。非常に簡単です。

3
user789327

Modified Binary Searchを使用しています。 O(LogN)になります。スペースの複雑さはO(1)になります。 BinarySearchModifiedを2回呼び出しています。 1つは要素の開始インデックスを見つけるため、もう1つは要素の終了インデックスを見つけるためです。

private static int BinarySearchModified(int[] input, double toSearch)
    {
        int start = 0;
        int end = input.Length - 1;

        while (start <= end)
        {
            int mid = start + (end - start)/2;
            if (toSearch < input[mid]) end = mid - 1;
            else start = mid + 1;
        }

        return start;
    }


    public static Result GetRange(int[] input, int toSearch)
    {
        if (input == null) return new Result(-1, -1);

        int low = BinarySearchModified(input, toSearch - 0.5);

        if ((low >= input.Length) || (input[low] != toSearch)) return new Result(-1, -1);

        int high = BinarySearchModified(input, toSearch + 0.5);

        return new Result(low, high - 1);
    } 

 public struct Result
    {
        public int LowIndex;
        public int HighIndex;

        public Result(int low, int high)
        {
            LowIndex = low;
            HighIndex = high;
        }
    }
1
Vikrant

バイナリ検索を使用する必要がない場合は、ハッシュマップが機能する可能性があります。

Keyが値自体であるHashMapを作成し、次にvalueはその値が配列内にあるインデックスの配列です。配列をループして、各値のHashMapの各配列を更新します。

各値のインデックスのルックアップ時間は〜O(1)になり、マップ自体の作成は〜O(n)になります。

1
Michael

私はバイナリ検索を使用して解決策を思い付きました、一致が見つかった場合は、両側でバイナリ検索を実行するだけです。

public static void main(String[] args) {
    int a[] ={1,2,2,5,5,6,8,9,10};
    System.out.println(2+" IS AVAILABLE  AT = "+findDuplicateOfN(a, 0, a.length-1, 2));
    System.out.println(5+" IS AVAILABLE  AT = "+findDuplicateOfN(a, 0, a.length-1, 5));
    int a1[] ={2,2,2,2,2,2,2,2,2};
    System.out.println(2+" IS AVAILABLE  AT = "+findDuplicateOfN(a1, 0, a1.length-1, 2));

    int a2[] ={1,2,3,4,5,6,7,8,9};
    System.out.println(10+" IS AVAILABLE  AT = "+findDuplicateOfN(a2, 0, a2.length-1, 10));
}

public static String findDuplicateOfN(int[] a, int l, int h, int x){
    if(l>h){
        return "";
    }
    int m = (h-l)/2+l;
    if(a[m] == x){
        String matchedIndexs = ""+m;
        matchedIndexs = matchedIndexs+findDuplicateOfN(a, l, m-1, x);
        matchedIndexs = matchedIndexs+findDuplicateOfN(a, m+1, h, x);
        return matchedIndexs;
    }else if(a[m]>x){
        return findDuplicateOfN(a, l, m-1, x);
    }else{
        return findDuplicateOfN(a, m+1, h, x);
    }
}


2 IS AVAILABLE  AT = 12 
5 IS AVAILABLE  AT = 43 
2 IS AVAILABLE  AT = 410236578 
10 IS AVAILABLE  AT =

これはまだO(logn)複雑さの結果を提供していると思います。

1
Mohan Kamaraj

以下は、Javaコードであり、指定されたソートされた配列で検索キーが分散される範囲を返します。

public static int doBinarySearchRec(int[] array, int start, int end, int n) {
    if (start > end) {
        return -1;
    }
    int mid = start + (end - start) / 2;

    if (n == array[mid]) {
        return mid;
    } else if (n < array[mid]) {
        return doBinarySearchRec(array, start, mid - 1, n);
    } else {
        return doBinarySearchRec(array, mid + 1, end, n);
    }
}

/**
 * Given a sorted array with duplicates and a number, find the range in the
 * form of (startIndex, endIndex) of that number. For example,
 * 
 * find_range({0 2 3 3 3 10 10}, 3) should return (2,4). find_range({0 2 3 3
 * 3 10 10}, 6) should return (-1,-1). The array and the number of
 * duplicates can be large.
 * 
 */
public static int[] binarySearchArrayWithDup(int[] array, int n) {

    if (null == array) {
        return null;
    }
    int firstMatch = doBinarySearchRec(array, 0, array.length - 1, n);
    int[] resultArray = { -1, -1 };
    if (firstMatch == -1) {
        return resultArray;
    }
    int leftMost = firstMatch;
    int rightMost = firstMatch;

    for (int result = doBinarySearchRec(array, 0, leftMost - 1, n); result != -1;) {
        leftMost = result;
        result = doBinarySearchRec(array, 0, leftMost - 1, n);
    }

    for (int result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n); result != -1;) {
        rightMost = result;
        result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n);
    }

    resultArray[0] = leftMost;
    resultArray[1] = rightMost;

    return resultArray;
}
1
Bhumik Thakkar
Find_Key(int arr[], int size, int key){
int begin = 0;
int end = size - 1;
int mid = end / 2;
int res = INT_MIN;

while (begin != mid)
{
    if (arr[mid] < key)
        begin = mid;
    else
    {
        end = mid;
        if(arr[mid] == key)
            res = mid;
    }
    mid = (end + begin )/2;
}
return res;
}

Intの配列が昇順で並べ替えられていると仮定します。キーオカレンスの最初のインデックスのインデックスまたはINT_MINを返します。 O(lg n)で実行されます。

1
Bhuvan
public void printCopies(int[] array)
{
    HashMap<Integer, Integer> memberMap = new HashMap<Integer, Integer>();
    for(int i = 0; i < array.size; i++)
       if(!memberMap.contains(array[i]))
           memberMap.put(array[i], 1);
       else
       {
           int temp = memberMap.get(array[i]); //get the number of occurances
           memberMap.put(array[i], ++temp); //increment his occurance
       }

    //check keys which occured more than once
    //dump them in a ArrayList
    //return this ArrayList
 }

あるいは、発生数を数える代わりに、それらのインデックスをarraylistに入れ、それをカウントの代わりにマップに入れることができます。

   HashMap<Integer, ArrayList<Integer>> 
   //the integer is the value, the arraylist a list of their indices

public void printCopies(int[] array)
{
    HashMap<Integer, ArrayList<Integer>> memberMap = new HashMap<Integer, ArrayList<Integer>>();
    for(int i = 0; i < array.size; i++)
       if(!memberMap.contains(array[i]))
       {
           ArrayList temp = new ArrayList();
           temp.add(i);
           memberMap.put(array[i], temp);
       }
       else
       {
           ArrayList temp = memberMap.get(array[i]); //get the lsit of indices
           temp.add(i);
           memberMap.put(array[i], temp); //update the index list
       }

    //check keys which return lists with length > 1
    //handle the result any way you want
 }

ねえ、私はこれが投稿される必要があると思います。

 int predefinedDuplicate = //value here;
 int index = Arrays.binarySearch(array, predefinedDuplicate);
 int leftIndex, rightIndex;
 //search left
 for(leftIndex = index; array[leftIndex] == array[index]; leftIndex--); //let it run thru it
 //leftIndex is now the first different element to the left of this duplicate number string
 for(rightIndex = index; array[rightIndex] == array[index]; rightIndex++); //let it run thru it

 //right index contains the first different element to the right of the string
 //you can arraycopy this [leftIndex+1, rightIndex-1] string or just print it
 for(int i = leftIndex+1; i<rightIndex; i++)
 System.out.println(array[i] + "\t");
0
Shark

左端のターゲットと右端のターゲットのlog(n)バイナリ検索の別の結果。これはC++ですが、かなり読みやすいと思います。

アイデアは、常にleft = right + 1。したがって、左端のターゲットを見つけるために、rightをターゲットより小さい右端の数値に移動できる場合、左は左端のターゲットになります。

左端のターゲットの場合:

int binary_search(vector<int>& nums, int target){
    int n = nums.size();
    int left = 0, right = n - 1;

    // carry right to the greatest number which is less than target.
    while(left <= right){
        int mid = (left + right) / 2;
        if(nums[mid] < target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    // when we are here, right is at the index of greatest number
    // which is less than target and since left is at the next, 
    // it is at the first target's index
    return left;
}

右端のターゲットの場合、考え方は非常に似ています。

int binary_search(vector<int>& nums, int target){
    while(left <= right){
        int mid = (left + right) / 2;
        // carry left to the smallest number which is greater than target.
        if(nums[mid] <= target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    // when we are here, left is at the index of smallest number
    // which is greater than target and since right is at the next, 
    // it is at the first target's index
    return right;
}
0
ozdemir08