web-dev-qa-db-fra.com

Recherche dans un tableau trié et pivoté

Pendant que je me préparais pour un entretien technique, je suis tombé sur cette question intéressante:

Vous avez reçu un tableau qui est trié puis pivoté.

exemple

Laissez arr = [1,2,3,4,5] qui est trié puis pivoté, dites deux fois à droite pour donner

[4,5,1,2,3]

Maintenant, quelle est la meilleure méthode de recherche dans ce tableau trié + pivoté?

On peut décompresser le tableau puis faire une recherche binaire. Mais ce n’est pas mieux que de faire une recherche linéaire dans le tableau d’entrée car les deux sont les pires des cas O (N).

S'il vous plaît fournir des indications. J'ai beaucoup cherché sur des algorithmes spéciaux pour cela, mais je n'en ai trouvé aucun.

Je comprends c et c ++

59
Jones

Cela peut être fait dans O(logN) en utilisant une recherche binaire légèrement modifiée.

La propriété intéressante d'un tableau trié + pivoté est que, lorsque vous le divisez en deux moitiés, au moins une des deux moitiés sera toujours triée.

Let input array arr = [4,5,6,7,8,9,1,2,3]
number of elements  = 9
mid index = (0+8)/2 = 4

[4,5,6,7,8,9,1,2,3]
         ^
 left   mid  right

comme sembler juste, le sous-tableau n'est pas trié tandis que le sous-tableau de gauche est trié.

Si mi est le point de rotation, les sous-tableaux gauche et droit seront triés. 

[6,7,8,9,1,2,3,4,5]
         ^

Mais dansdans tous les cas, une moitié (sous-tableau) doit être triée.

Nous pouvons facilement savoir quelle moitié est triée en comparant les éléments de début et de fin de chaque moitié.

Une fois que nous avons trouvé quelle moitié est triée, nous pouvons voir si la clé est présente dans cette demi-comparaison simple avec les extrêmes.

Si la clé est présente dans cette moitié, nous appelons récursivement la fonction sur cette moitié. 
sinon nous appelons récursivement notre recherche sur l'autre moitié.

Nous rejetons la moitié de la matrice dans chaque appel, ce qui rend cet algorithme O(logN).

Pseudo code:

function search( arr[], key, low, high)

        mid = (low + high) / 2

        // key not present
        if(low > high)
                return -1

        // key found
        if(arr[mid] == key)
                return mid

        // if left half is sorted.
        if(arr[low] <= arr[mid])

                // if key is present in left half.
                if (arr[low] <= key && arr[mid] >= key) 
                        return search(arr,key,low,mid-1)

                // if key is not present in left half..search right half.
                else                 
                        return search(arr,key,mid+1,high)
                end-if

        // if right half is sorted. 
        else    
                // if key is present in right half.
                if(arr[mid] <= key && arr[high] >= key) 
                        return search(arr,key,mid+1,high)

                // if key is not present in right half..search in left half.
                else
                        return search(arr,key,low,mid-1)
                end-if
        end-if  

end-function

La clé ici est qu'un sous-tableau sera toujours trié, en utilisant lequel nous pouvons rejeter la moitié du tableau.

136
codaddict

Vous pouvez faire 2 recherches binaires: d'abord pour trouver l'index i tel que arr[i] > arr[i+1]

Apparemment, (arr\[1], arr[2], ..., arr[i]) et (arr[i+1], arr[i+2], ..., arr[n]) sont tous deux des tableaux triés.

Ensuite, si arr[1] <= x <= arr[i], vous effectuez une recherche binaire dans le premier tableau, sinon dans le second.

La complexité O(logN)

EDIT: le code .

14
Max

La réponse sélectionnée a un bogue lorsqu'il y a des éléments en double dans le tableau. Par exemple, arr = {2,3,2,2,2} et 3 sont ce que nous recherchons. Ensuite, le programme dans la réponse sélectionnée retournera -1 au lieu de 1. 

Cette question d’entrevue est discutée en détail dans le livre «Casser l’interview de codage». La condition des éléments en double est spécialement traitée dans ce livre. Puisque l'opération a indiqué dans le commentaire que les éléments de tableau peuvent être n'importe quoi, je donne ma solution sous forme de pseudo-code ci-dessous:

function search( arr[], key, low, high)

    if(low > high)
        return -1

    mid = (low + high) / 2

    if(arr[mid] == key)
        return mid

    // if the left half is sorted.
    if(arr[low] < arr[mid]) {

        // if key is in the left half
        if (arr[low] <= key && key <= arr[mid]) 
            // search the left half
            return search(arr,key,low,mid-1)
        else
            // search the right half                 
            return search(arr,key,mid+1,high)
        end-if

    // if the right half is sorted. 
    else if(arr[mid] < arr[low])    
        // if the key is in the right half.
        if(arr[mid] <= key && arr[high] >= key) 
            return search(arr,key,mid+1,high)
        else
            return search(arr,key,low,mid-1)
        end-if

    else if(arr[mid] == arr[low])

        if(arr[mid] != arr[high])
            // Then elements in left half must be identical. 
            // Because if not, then it's impossible to have either arr[mid] < arr[high] or arr[mid] > arr[high]
            // Then we only need to search the right half.
            return search(arr, mid+1, high, key)
        else 
            // arr[low] = arr[mid] = arr[high], we have to search both halves.
            result = search(arr, low, mid-1, key)
            if(result == -1)
                return search(arr, mid+1, high, key)
            else
                return result
   end-if
end-function
11
ChuanRocks

Ma première tentative serait de trouver en utilisant la recherche binaire le nombre de rotations appliquées - ceci peut être fait en recherchant l'indice n où a [n]> a [n + 1] en utilisant le mécanisme de recherche binaire habituel .. recherche binaire régulière en faisant pivoter tous les index par équipe trouvée.

8
RomanK
int rotated_binary_search(int A[], int N, int key) {
  int L = 0;
  int R = N - 1;

  while (L <= R) {
    // Avoid overflow, same as M=(L+R)/2
    int M = L + ((R - L) / 2);
    if (A[M] == key) return M;

    // the bottom half is sorted
    if (A[L] <= A[M]) {
      if (A[L] <= key && key < A[M])
        R = M - 1;
      else
        L = M + 1;
    }
    // the upper half is sorted
    else {
      if (A[M] < key && key <= A[R])
        L = M + 1;
      else
        R = M - 1;
    }
  }
  return -1;
}
4
Akki Javed

Si vous savez que le tableau a été pivoté à droite, vous pouvez simplement effectuer une recherche binaire décalée à droite. C'est O (lg N)

Par cela, je veux dire, initialiser la limite gauche à s et le droit à (s-1) mod N, et faire une recherche binaire entre ceux-ci, en prenant un peu de soin de travailler dans la zone correcte.

Si vous ne savez pas combien le tableau a été pivoté, vous pouvez déterminer la taille de la rotation en utilisant une recherche binaire, qui est O (lg N), puis effectuez une recherche binaire décalée, O (lg N), a total général de O (lg N) encore.

Réponse pour le message mentionné ci-dessus "Cette question est discutée en détail dans le livre" Cracking the Coding Interview "(Entretien de codage). La condition des éléments en double est spécifiquement traitée dans ce livre. je donne ma solution comme pseudo-code ci-dessous: "

Votre solution est O(n) !! (La dernière condition if où vous vérifiez les deux moitiés du tableau pour une seule condition en fait un problème de complexité temporelle linéaire)

Je ferais mieux de faire une recherche linéaire que de rester coincé dans un labyrinthe de bugs et de fautes de segmentation pendant une ronde de codage.

Je ne pense pas qu'il existe une meilleure solution que O(n) pour une recherche dans un tableau trié pivoté (avec des doublons)

2
NIKUNJ BHARTIA

Si vous savez à quel point la rotation a eu lieu, vous pouvez toujours effectuer une recherche binaire. 

Le truc, c'est que vous obtenez deux niveaux d'index: vous faites le b.s. dans une plage virtuelle 0..n-1, puis annulez-les pour rechercher une valeur. 

2
Henk Holterman

vous n'avez pas besoin de faire pivoter le tableau d'abord, vous pouvez utiliser la recherche binaire sur le tableau pivoté (avec quelques modifications)

supposez que N est le numéro que vous recherchez:

lit le premier nombre (arr [début]) et le nombre au milieu du tableau (arr [fin]):

  • si arr [début]> arr [fin] -> la première moitié n'est pas triée mais la seconde moitié est triée:

    • si arr [fin]> N -> le numéro est dans l'index: (milieu + N - arr [fin])

    • si N répète la recherche sur la première partie du tableau (voir la fin comme étant le milieu de la première moitié du tableau, etc.)

(idem si la première partie est triée mais pas la seconde)

2
SivGo
short mod_binary_search( int m, int *arr, short start, short end)
{

 if(start <= end)
 {
    short mid = (start+end)/2;

    if( m == arr[mid])
        return mid;
    else
    {
        //First half is sorted
        if(arr[start] <= arr[mid])
        {
            if(m < arr[mid] && m >= arr[start])
                return mod_binary_search( m, arr, start, mid-1);
            return mod_binary_search( m, arr, mid+1, end);
        }

        //Second half is sorted
        else
        {
            if(m > arr[mid] && m < arr[start])
                return mod_binary_search( m, arr, mid+1, end);
            return mod_binary_search( m, arr, start, mid-1);
        }
    }
 }
 return -1;
}
1
Srikant Aggarwal
public class PivotedArray {

//56784321 first increasing than decreasing
public static void main(String[] args) {
    // TODO Auto-generated method stub
    int [] data ={5,6,7,8,4,3,2,1,0,-1,-2};

    System.out.println(findNumber(data, 0, data.length-1,-2));

}

static int findNumber(int data[], int start, int end,int numberToFind){

    if(data[start] == numberToFind){
        return start;
    }

    if(data[end] == numberToFind){
        return end;
    }
    int mid = (start+end)/2;
    if(data[mid] == numberToFind){
        return mid;
    }
    int idx = -1;
    int midData = data[mid];
    if(numberToFind < midData){
        if(midData > data[mid+1]){
            idx=findNumber(data, mid+1, end, numberToFind);
        }else{
            idx =  findNumber(data, start, mid-1, numberToFind);
        }
    }

    if(numberToFind > midData){
        if(midData > data[mid+1]){
            idx =  findNumber(data, start, mid-1, numberToFind);

        }else{
            idx=findNumber(data, mid+1, end, numberToFind);
        }
    }
    return idx;
}

}
1
RockSolid

Tout d’abord, vous devez trouver la constante de décalage, k. Cela peut être fait dans le temps O(lgN). À partir du décalage constant k, vous pouvez facilement trouver l’élément recherché en utilisant Une recherche binaire avec la constante k. La recherche binaire augmentée prend également O(lgN) temps Le temps total d’exécution est O (lgN + lgN) = O(lgN) 

Pour trouver le changement constant, k. Il vous suffit de rechercher la valeur minimale dans le tableau. L'index de la valeur minimale du tableau vous indique le décalage constant. Considérons le tableau trié [1,2,3,4,5]. 

 Les changements possibles sont: 
 [1,2,3,4,5] // k = 0 
 [5,1,2,3,4] // k = 1 
 [4,5,1,2,3] // k = 2 
 [3,4,5,1,2] // k = 3 
 [2,3,4,5,1] // k = 4 
 [1,2,3,4,5] // k = 5% 5 = 0 

Pour faire un algorithme dans O(lgN), la clé est de toujours trouver des moyens de diviser le problème par deux. Une fois cela fait, le reste des détails de la mise en œuvre est facile

Ci-dessous le code en C++ pour l'algorithme

// This implementation takes O(logN) time
// This function returns the amount of shift of the sorted array, which is
// equivalent to the index of the minimum element of the shifted sorted array. 
#include <vector> 
#include <iostream> 
using namespace std; 

int binarySearchFindK(vector<int>& nums, int begin, int end)
{
    int mid = ((end + begin)/2); 
    // Base cases
    if((mid > begin && nums[mid] < nums[mid-1]) || (mid == begin && nums[mid] <= nums[end]))     
        return mid; 
    // General case 
    if (nums[mid] > nums[end]) 
    {
        begin = mid+1; 
        return binarySearchFindK(nums, begin, end); 
    }
    else
    {
        end = mid -1; 
        return binarySearchFindK(nums, begin, end); 
    }   
}  
int getPivot(vector<int>& nums)
{
    if( nums.size() == 0) return -1; 
    int result = binarySearchFindK(nums, 0, nums.size()-1); 
    return result; 
}

// Once you execute the above, you will know the shift k, 
// you can easily search for the element you need implementing the bottom 

int binarySearchSearch(vector<int>& nums, int begin, int end, int target, int pivot)
{
    if (begin > end) return -1; 
    int mid = (begin+end)/2;
    int n = nums.size();  
    if (n <= 0) return -1; 

    while(begin <= end)
    {
        mid = (begin+end)/2; 
        int midFix = (mid+pivot) % n; 
        if(nums[midFix] == target) 
        {
            return midFix; 
        }
        else if (nums[midFix] < target)
        {
            begin = mid+1; 
        }
        else
        {
            end = mid - 1; 
        }
    }
    return -1; 
}
int search(vector<int>& nums, int target) {
    int pivot = getPivot(nums); 
    int begin = 0; 
    int end = nums.size() - 1; 
    int result = binarySearchSearch(nums, begin, end, target, pivot); 
    return result; 
}
 Espérons que cela aide! =) 
 Bientôt Chee Loong, 
 Université de Toronto 
1
Chee Loong Soon

Ce code en C++ devrait fonctionner dans tous les cas. Bien que cela fonctionne avec les doublons, merci de me faire savoir s'il y a un bogue dans ce code.

#include "bits/stdc++.h"
using namespace std;
int searchOnRotated(vector<int> &arr, int low, int high, int k) {

    if(low > high)
        return -1;

    if(arr[low] <= arr[high]) {

        int p = lower_bound(arr.begin()+low, arr.begin()+high, k) - arr.begin();
        if(p == (low-high)+1)
            return -1;
        else
            return p; 
    }

    int mid = (low+high)/2;

    if(arr[low] <= arr[mid]) {

        if(k <= arr[mid] && k >= arr[low])
            return searchOnRotated(arr, low, mid, k);
        else
            return searchOnRotated(arr, mid+1, high, k);
    }
    else {

        if(k <= arr[high] && k >= arr[mid+1])
            return searchOnRotated(arr, mid+1, high, k);
        else
            return searchOnRotated(arr, low, mid, k);
    }
}
int main() {

    int n, k; cin >> n >> k;
    vector<int> arr(n);
    for(int i=0; i<n; i++) cin >> arr[i];
    int p = searchOnRotated(arr, 0, n-1, k);
    cout<<p<<"\n";
    return 0;
}
0
Siddhant

Mon code simple: -

public int search(int[] nums, int target) {
    int l = 0;
    int r = nums.length-1;
    while(l<=r){
        int mid = (l+r)>>1;
        if(nums[mid]==target){
            return mid;
        }
        if(nums[mid]> nums[r]){
            if(target > nums[mid] || nums[r]>= target)l = mid+1;
            else r = mid-1;
        }
        else{
            if(target <= nums[r] && target > nums[mid]) l = mid+1;
            else r = mid -1;
        }
    }
    return -1;
}

Complexité temporelle O (log (N)).

0
HeadAndTail

Pour un tableau pivoté avec des doublons, si vous devez trouver la première occurrence d'un élément, vous pouvez utiliser la procédure ci-dessous (code Java):

public int mBinarySearch(int[] array, int low, int high, int key)
{
    if (low > high)
        return -1; //key not present

    int mid = (low + high)/2;

    if (array[mid] == key)
        if (mid > 0 && array[mid-1] != key)
            return mid;

    if (array[low] <= array[mid]) //left half is sorted
    {
        if (array[low] <= key && array[mid] >= key)
            return mBinarySearch(array, low, mid-1, key);
        else //search right half
            return mBinarySearch(array, mid+1, high, key);
    }
    else //right half is sorted
    {
        if (array[mid] <= key && array[high] >= key)
            return mBinarySearch(array, mid+1, high, key);
        else
            return mBinarySearch(array, low, mid-1, key);
    }       

}

Ceci est une amélioration de la procédure de codaddict ci-dessus. Notez la condition supplémentaire si comme ci-dessous:

if (mid > 0 && array[mid-1] != key)
0
ranjeeth1978

Question: Recherche dans un tableau trié par rotation

public class SearchingInARotatedSortedARRAY {
    public static void main(String[] args) {
        int[] a = { 4, 5, 6, 0, 1, 2, 3 };

        System.out.println(search1(a, 6));

    }

    private static int search1(int[] a, int target) {
        int start = 0;
        int last = a.length - 1;
        while (start + 1 < last) {
            int mid = start + (last - start) / 2;

            if (a[mid] == target)
                return mid;
            // if(a[start] < a[mid]) => Then this part of the array is not rotated
            if (a[start] < a[mid]) {
                if (a[start] <= target && target <= a[mid]) {
                    last = mid;
                } else {
                    start = mid;
                }
            }
            // this part of the array is rotated
            else {
                if (a[mid] <= target && target <= a[last]) {
                    start = mid;
                } else {
                    last = mid;
                }
            }
        } // while
        if (a[start] == target) {
            return start;
        }
        if (a[last] == target) {
            return last;
        }
        return -1;
    }
}
0
Soudipta Dutta

Voici une solution python O non-récursive simple (temps, espace) efficace (log n) qui ne modifie pas le tableau d'origine. Découpe le tableau pivoté en deux jusqu'à ce que je n'ai plus que deux index à vérifier et renvoie la réponse correcte si un index correspond.

def findInRotatedArray(array, num):

lo,hi = 0, len(array)-1
ix = None


while True:


    if hi - lo <= 1:#Im down to two indices to check by now
        if (array[hi] == num):  ix = hi
        Elif (array[lo] == num): ix = lo
        else: ix = None
        break

    mid = lo + (hi - lo)/2
    print lo, mid, hi

    #If top half is sorted and number is in between
    if array[hi] >= array[mid] and num >= array[mid] and num <= array[hi]:
        lo = mid

    #If bottom half is sorted and number is in between
    Elif array[mid] >= array[lo] and num >= array[lo] and num <= array[mid]:
        hi = mid


    #If top half is rotated I know I need to keep cutting the array down
    Elif array[hi] <= array[mid]:
        lo = mid

    #If bottom half is rotated I know I need to keep cutting down
    Elif array[mid] <= array[lo]:
        hi = mid

print "Index", ix
0
Leon

Une autre approche qui fonctionnerait avec des valeurs répétées consiste à trouver la rotation puis à effectuer une recherche binaire régulière en appliquant la rotation à chaque fois que nous accédons au tableau. 

test = [3, 4, 5, 1, 2]
test1 = [2, 3, 2, 2, 2]

def find_rotated(col, num):
    pivot = find_pivot(col)
    return bin_search(col, 0, len(col), pivot, num)

def find_pivot(col):
    prev = col[-1]
    for n, curr in enumerate(col):
        if prev > curr:
            return n
        prev = curr
    raise Exception("Col does not seem like rotated array")

def rotate_index(col, pivot, position):
    return (pivot + position) % len(col)

def bin_search(col, low, high, pivot, num):
    if low > high:
        return None
    mid = (low + high) / 2
    rotated_mid = rotate_index(col, pivot, mid)
    val = col[rotated_mid]
    if (val == num):
        return rotated_mid
    Elif (num > val):
        return bin_search(col, mid + 1, high, pivot, num)
    else:
        return bin_search(col, low, mid - 1,  pivot, num)

print(find_rotated(test, 2))
print(find_rotated(test, 4))
print(find_rotated(test1, 3))
0
barracel

Essayez cette solution 

bool search(int *a, int length, int key)
{
int pivot( length / 2 ), lewy(0), prawy(length);
if (key > a[length - 1] || key < a[0]) return false;
while (lewy <= prawy){
    if (key == a[pivot]) return true;
    if (key > a[pivot]){
        lewy = pivot;
        pivot += (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}
    else{
        prawy = pivot;
        pivot -= (prawy - lewy) / 2 ? (prawy - lewy) / 2:1;}}
return false;
}
0
Bart