web-dev-qa-db-fra.com

Utilisation de la recherche binaire avec un tableau trié avec des doublons

J'ai été chargé de créer une méthode qui imprimera tous les index où la valeur x est trouvée dans un tableau trié. 

Je comprends que si nous parcourions simplement le tableau de 0 à N (longueur du tableau), la durée d'exécution serait de O(n) pire des cas. Étant donné que le tableau qui sera passé dans la méthode sera trié, je suppose que je peux tirer parti de l'utilisation d'une recherche binaire, car ce sera O (log n). Cependant, cela ne fonctionne que si le tableau a des valeurs uniques. Étant donné que la recherche binaire se terminera après la première "recherche" d'une valeur particulière. Je pensais faire une recherche binaire pour trouver x dans le tableau trié, puis vérifier toutes les valeurs avant et après cet index, mais si le tableau contenait toutes les valeurs x, il ne semblait pas que ce serait beaucoup mieux.

Je suppose que ce que je demande est la suivante: existe-t-il un meilleur moyen de trouver tous les indices d’une valeur donnée dans un tableau trié supérieur à O (n)?

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

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

Par exemple, triéArray = {1, 13, 42, 42, 42, 77, 78} indiquerait: "42 ont été trouvés aux indices: 2, 3, 4"

13
5StringRyan

Eh bien, si vous avez réellement un tableau trié, vous pouvez faire une recherche binaire jusqu'à ce que vous trouviez l'un des index que vous cherchez, et à partir de là, le reste devrait être facile à trouver car ils sont tous à côté de chaque- autre.

une fois que vous avez trouvé votre premier, vous allez rechercher toutes les instances précédentes, puis toutes les instances qui suivent. 

En utilisant cette méthode, vous devriez obtenir à peu près O (lg (n) + k) k est le nombre d’occurrences de la valeur que vous recherchez. 

MODIFIER:

Et, non, vous ne pourrez jamais accéder à toutes les valeurs k dans des valeurs inférieures à O(k) time. 


Deuxième édition: pour que je puisse réellement sentir quelque chose d’utile:

Au lieu de simplement rechercher les première et dernière occurrences de X, vous pouvez effectuer une recherche binaire pour la première occurrence et une recherche binaire pour la dernière occurrence. ce qui donnera O(lg(n)) total. une fois que vous avez fait cela, vous saurez que tous les index entre les deux contiennent également X (en supposant qu'il soit trié)

Vous pouvez le faire en cherchant si la valeur est égale à x ,ETen vérifiant si la valeur à gauche (ou à droite, selon que vous cherchiez la première occurrence ou la dernière occurrence) est égal à x .

14
Sam I am

Vous obtiendrez le résultat en 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);
        }
    }
}
24
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);
}

Cela fonctionnerait dans O(log(n) + # occurrences) Lire et comprendre le code. C'est assez simple.

4
user789327

Il utilise la recherche binaire modifiée. Ce sera O (LogN). La complexité de l'espace sera O (1). Nous appelons BinarySearchModified à deux reprises. Un pour trouver l’index de début d’un élément et un autre pour trouver l’indice de fin d’un élément. 

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

Un Hashmap peut fonctionner si vous n'êtes pas obligé d'utiliser une recherche binaire.

Créez une table de hachage où Key est la valeur elle-même, puis value est un tableau d'indices où cette valeur se trouve dans le tableau. Parcourez votre tableau en mettant à jour chaque tableau dans HashMap pour chaque valeur. 

Le temps de recherche pour les indices de chaque valeur sera ~ O (1) et la carte elle-même sera ~ O (n).

1
Michael

J'ai trouvé la solution en utilisant la recherche binaire, la seule chose à faire est de faire la recherche binaire des deux côtés si la correspondance est trouvée. 

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 =

Je pense que cela fournit toujours les résultats dans la complexité O(logn).

1
Mohan Kamaraj

Vous trouverez ci-dessous le code Java qui renvoie la plage pour laquelle la clé de recherche est étendue dans le tableau trié donné:

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

En supposant que le tableau d'ints soit dans un ordre de tri croissant; Retourne l'index du premier index de l'occurrence de clé ou INT_MIN. Fonctionne en 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
 }

Alternativement, au lieu de compter le nombre d'occurrences, vous pouvez mettre leurs index dans un répertoire et les mettre sur la carte au lieu du nombre.

   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
 }

hé, je suppose que cela devra être posté.

 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

Un autre résultat pour la recherche binaire log (n) des cibles les plus à gauche et les plus à droite. Ceci est en C++, mais je pense que c'est assez lisible. 

L'idée est que nous finissons toujours par left = right + 1. Donc, pour trouver la cible la plus à gauche, si nous pouvons déplacer right au nombre le plus à droite qui est inférieur à la cible, left sera à la cible la plus à gauche.

Pour la cible la plus à gauche:

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

Pour la cible la plus à droite, l’idée est très similaire:

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