web-dev-qa-db-fra.com

Recherche d'un numéro dans un tableau trié pivoté

Étant donné un tableau trié qui peut être pivoté, trouvez-y un élément dont la complexité temporelle est minimale.

exemple: le contenu d'un tableau peut être [8, 1, 2, 3, 4, 5]. Supposons que vous cherchiez 8 en elle.

30
Geek

La solution reste une recherche binaire dans la mesure où vous aurez besoin de partitionner le tableau en deux parties à examiner. 

Dans un tableau trié, il suffit d'examiner chaque partie et de déterminer si l'élément réside dans la première partie (appelons cela A) ou dans la deuxième partie (B). Étant donné que, par la définition d'un tableau trié, les partitions A et B seront triées, cela ne nécessite pas plus que quelques comparaisons simples des limites de la partition et de votre clé de recherche.

Dans un tableau trié ayant subi une rotation, seul l'un des A et B peut être garanti pour être trié. Si l'élément se trouve dans une pièce triée, la solution est simple: effectuez simplement la recherche comme si vous effectuiez une recherche binaire normale. Si, toutefois, vous devez rechercher une pièce non triée, appelez simplement votre fonction de recherche de manière récursive sur la pièce non triée.

Cela finit par donner une complexité temporelle de O(lg n).

(En réalité, je penserais qu'une telle structure de données aurait un index l'accompagnant pour indiquer combien de positions le tableau a été pivoté.)

Edit: une recherche sur Google m'amène à ce sujet assez daté (mais correct) de CodeGuru traitant du même problème. Pour ajouter à ma réponse, je vais copier un pseudo-code qui a été donné ici pour qu'il apparaisse ici avec ma solution (la pensée suit les mêmes lignes):

Search(set):
    if size of set is 1 and set[0] == item 
        return info on set[0]
    divide the set into parts A and B
    if A is sorted and the item is in the A's range
        return Search(A)
    if B is sorted and the item is in the B's range
        return Search(B)
    if A is not sorted
        return Search(A)
    if B is not sorted
        return Search(B)
    return "not found"
39
Andrew Song

O (log (N)) 

Réduit au problème de la position du plus grand nombre, ce qui peut être fait en vérifiant les premier, dernier et deuxième nombres de la zone, réduire récursivement la zone, diviser et conquérir, Ceci est O(log(N)) pas plus grand que la recherche binaire O (log (N)).

EDIT: Par exemple, vous avez 

6 7 8 1 2 3 4 5  
^       ^     ^ 

En regardant les 3 chiffres, vous savez que l'emplacement du plus petit/du plus grand nombre (on appellera plus tard mark) est dans la zone de 6 7 8 1 2, de sorte que 3 4 5 est hors de considération (généralement effectué en déplaçant votre région index début/fin (int) pointant vers les nombres 6 et 2).

L'étape suivante, 

6 7 8 1 2  
^   ^   ^  

Une fois encore, vous obtiendrez suffisamment d'informations pour indiquer de quel côté (gauche ou droite) se trouve la marque, puis la zone est réduite à la moitié (à 6 7 8). 

C’est l’idée: je pense que vous pouvez affiner une meilleure version de ceci, en fait, pour un entretien, un algorithme OK avec un code propre vaut mieux que le meilleur algorithme avec des codes OK: vous feriez mieux de vous en remettre chauffer. 

Bonne chance! 

18
Dr. Xray

On m'a posé cette question récemment dans une interview. La question était de décrire un algorithme permettant de rechercher une "clé" dans un tableau trié circulaire. Il m'a également été demandé d'écrire le code correspondant. :

Utilisez divide and conquer binary search . Pour chaque sous-tableau, vérifiez si le tableau est trié. Si trié, utilisez la recherche binaire classique

data [début] <data [fin] implique que les données sont triées. user binary else diviser le tableau plus loin jusqu'à obtenir un tableau trié.

    public boolean search(int start,int end){
    int mid =(start+end)/2;
    if(start>end)
    {
        return  false;
    }
    if(data[start]<data[end]){
        return this.normalBinarySearch(start, end);
    }
    else{
        //the other part is unsorted.
        return (this.search(start,mid) ||
        this.search(mid+1,end));
    }
}

où normalBinarySearch est une recherche binaire simple.

8
frictionlesspulley

C'est une simple modification de la recherche binaire normale. En fait, cela fonctionnera pour les tableaux pivotés et triés. Dans le cas de tableaux triés, il faudra cependant plus de travail que nécessaire.

Pour un tableau pivoté, lorsque vous divisez le tableau en deux sous-tableaux, il est possible que l'un de ces sous-tableaux ne soit pas en ordre. Vous pouvez facilement vérifier si chaque moitié est triée en comparant la première et la dernière valeur du sous-tableau. 

En sachant si chaque sous-matrice est triée ou non, nous pouvons choisir ce que vous ferez ensuite.

1) le sous-tableau de gauche est trié et la valeur est dans la plage du sous-tableau de gauche (vérifiez les deux extrémités!)

Ensuite, effectuez une recherche récursive dans le sous-tableau de gauche.

2) le sous-tableau de droite est trié et la valeur est dans les limites du sous-tableau de droite (vérifiez les deux extrémités!)

Ensuite, recherchez récursivement le bon sous-tableau.

3) left is Non trié

Recherche récursive dans le sous-tableau de gauche

4) Le droit n'est pas trié

Ensuite, recherchez récursivement le bon sous-tableau.

Remarque: la différence entre cette recherche et une recherche binaire normale réside dans le fait que nous ne pouvons pas simplement choisir le sous-tableau sur lequel effectuer une nouvelle analyse en comparant simplement la dernière valeur du sous-tableau à gauche (première valeur du sous-tableau de droite) pour prendre notre décision. La valeur doit être strictement dans le sous-tableau gauche ou droit et ce sous-tableau doit être trié, sinon nous devons recurse sur le sous-tableau non trié.

Voici quelques Objective-C qui implémente ceci:

@implementation BinarySearcher

- (BOOL)isValue:(int)value inArray:(int[])array withArraySize:(int)size {

    return [self subSearchArray:array forValue:value fromIndex:0 toIndex:size -1];
}

- (BOOL)subSearchArray:(int[])array forValue:(int)value fromIndex:(int)left toIndex:(int)right {

    if (left <= right) {

        int middle = (left + right) / 2;

        BOOL leftArraySorted = array[left] <= array[middle];
        BOOL rightArraySorted = array[middle + 1] <= array[right];

        if (array[middle] == value) {
            return YES;
        } else if (leftArraySorted && value >= array[left] && value < array[middle]) {
            return [self subSearchArray:array forValue:value fromIndex:left toIndex:middle];
        } else if (rightArraySorted && value >= array[middle + 1] && value <= array[right]) {
            return [self subSearchArray:array forValue:value fromIndex:middle + 1 toIndex:right];
        } else if (!leftArraySorted) {
            return [self subSearchArray:array forValue:value fromIndex:left toIndex:middle];
        } else if (!rightArraySorted) {
            return [self subSearchArray:array forValue:value fromIndex:middle + 1 toIndex:right];
        }
    }

    return NO;
}

@end
5
Michael Peterson

À n'importe quel index, une partition sera triée et d'autres non triées. Si la clé se trouve dans une partition triée, recherchez dans un tableau trié, sinon dans une partition non triée.

BS(lo, hi)
m = (lo + hi)/2
if(k = a[m])
    return m
b = 0
if(a[hi] > a[m])
    b = 1
if(b)
    if(k > a[m] && k<a[hi])
        BS(m+1, hi)
else
    BS(lo, m-1)
4
Prateek Caire

Vous pouvez envelopper un tableau avec une classe qui expose uniquement

E get (int index);

et se comporterait comme un tableau trié régulier . Par exemple, si vous avez 4 5 1 2 3 4, wrapper.get (0) retournera 1.

Maintenant, vous pouvez simplement réutiliser la solution de recherche binaire.

Wrapper peut ressembler à ça:

class RotatedArrayWrapper<T> {
  int startIndex;
  private final List<T> rotatedArray;

  public RotatedArrayWrapper(List<T> rotatedArray) {
    this.rotatedArray = rotatedArray;
    //find index of the smalest element in array
    //keep in mind that there might be duplicates
    startIndex = ... 
  } 

  public T get(int index) {
    int size = rotatedArray.size();
    if (index > size) throw Exception...

    int actualIndex = (startIndex + index) % size;
    return rotatedArray.get(actualIndex);
  }
}
2
Sergey

Voici mon approche à ce sujet:

public static int findMin(int[] a, int start, int end){
  int mid = (start + end)/2;
  if(start == mid){
    return a[mid+1];
  }else if(a[start] > a[mid]){
    return findMin(a, start, mid);
  }else if(a[mid+1] > a[start]){
    return findMin(a, mid+1, end);
  }else{
    return a[mid+1];
  }
}

Complexité temporelle: O (log n)

2
Kiran

Implémentation en Python. Pourrait être plus Pythonique:

from bisect import bisect_left


def index(a, x):
    """Binary search to locate the leftmost value exactly equal to x.
    see http://docs.python.org/2/library/bisect.html#searching-sorted-lists

    >>> index([5, 14, 27, 40, 51, 70], 27)
    2
    >>> index([1, 2, 3, 4], 10)
    Traceback (most recent call last):
        ...
    ValueError
    """
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError


def _index_shifted(value, sequence, start, stop):
    """Recursive reset location and binary search"""
    # if at reset (min) and it's not the value, it's not there
    if start == stop and sequence[start] != value:
        return -1
    mid = (stop + start) // 2
    # check mid, since we are already here
    if sequence[mid] == value:
        return mid
    # right side is sorted
    Elif sequence[mid] < sequence[stop]:
        # if value falls in range, search righ
        if sequence[stop] >= value > sequence[mid]:
            return index(sequence[mid:stop + 1], value) + mid
        # partition left side
        return _index_shifted(value, sequence, start, mid)
    # left side is sorted
    else:
        # if value falls in range, search left
        if sequence[mid] > value >= sequence[start]:
            return index(sequence[start:mid], value) + start
        # partition right side
        return _index_shifted(value, sequence, mid + 1, stop)


def index_shifted(sequence, value):
    """Returns index of value in a shifted sorted sequence; -1 if not present.

    >>> index_shifted([10, 13, 16, 19, 22, 25, 28, 31, 34, 37], 10)
    0
    >>> index_shifted([10, 13, 16, 19, 22, 25, 28, 31, 34, 37], 37)
    9
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 10)
    2
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 37)
    1
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 13)
    3
    >>> index_shifted([34, 37, 10, 13, 16, 19, 22, 25, 28, 31], 25)
    7
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], 10)
    5
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], -10)
    -1
    >>> index_shifted([25, 28, 31, 34, 37, 10, 13, 16, 19, 22], 100)
    -1
    """
    return _index_shifted(value, sequence, 0, len(sequence) - 1)
1
p7k

Dans le pire des cas, vous devez utiliser la recherche linéaire et examiner tout un tableau pour trouver une cible car, contrairement à un ensemble, un tableau peut contenir des doublons. Et si un tableau contient des doublons, vous ne pouvez pas utiliser la recherche binaire dans le problème donné.

Si les requêtes sur un tableau particulier sont peu fréquentes, vous pouvez utiliser la recherche binaire standard si tout le tableau est trié (le premier élément est strictement plus petit que le dernier élément) et utiliser la recherche linéaire sinon. Pour plus d'informations, voir À quelle vitesse pouvez-vous effectuer une recherche linéaire? discussion sur StackOverflow et 10 optimisations pour la recherche linéaire article de Thomas A. Limoncelli.

Cependant, si les requêtes sont fréquentes, vous pouvez trier le tableau d’entrée en temps linéaire (voir Algorithme le plus rapide pour le changement de cercle tableau de taille N pour la position M discussion sur StackOverflow) lors de l’étape de prétraitement et utiliser la recherche binaire standard comme d’habitude.

0
Leonid Vasilev

Voici une implémentation C++ de la réponse de @Andrew Song

int search(int A[], int s, int e, int k) {
    if (s <= e) {
        int m = s + (e - s)/2;
        if (A[m] == k)
            return m;
        if (A[m] < A[e] && k > A[m] && k <= A[e]) 
            return search(A, m+1, e, k);
        if (A[m] > A[s] && k < A[m] && k >= A[s]) 
            return search(A, s, m-1, k);
        if (A[m] < A[s]) 
            return search(A, s, m-1, k);
        if (A[m] > A[e])
            return search(A, m+1, e, k);
    }
    return -1;
}
0
AbKDs

Ma solution: Efficacité: O (logn)

public static int SearchRotatedSortedArray(int l, int d, int[] array, int toFind) {
    if (l >= d) {
        return -1;
    }
    if (array[l] == toFind) {
        return l;
    }
    if (array[d] == toFind) {
        return d;
    }

    int mid = (l + d) / 2;
    if (array[mid] == toFind) {
        return mid;
    }

    if ((array[mid] > toFind && toFind > array[l]) || (array[mid] < toFind && array[d] < toFind)) {
        return SearchRotatedSortedArray(l, mid - 1, array, toFind);
    } else {
        return SearchRotatedSortedArray(mid + 1, d, array, toFind);
    }

}
0
Igor Skiljevic

// cette solution divise le tableau en deux sous-réseaux égaux en utilisant la récursivité et applique une recherche binaire sur un tableau trié individuel. 

public class SearchRotatedSortedArray
{
    public static void main(String... args)
    {
        int[] array={5,6,1,2,3,4};

        System.out.println(search(array,Integer.parseInt(args[0]),0,5));
    }

    public static boolean search(int[] array,int element,int start,int end)
    {   
    if(start>=end)
    {
        if (array[end]==element) return true;else return false;
    }

    int mid=(start+end)/2;
    if(array[start]<array[end])
    {
        return binarySearch(array,element,start,end);
    }

    return search(array,element,start,mid)||search(array,element,mid+1,end);    
}

    public static boolean binarySearch(int[] array,int element,int start,int end)
    {
        int mid;

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

            if(array[mid]==element)
            return true;

            if(array[mid]<element)
            {
                start=mid+1;

            }
            else
            {
                end=mid-1;

            }
        }

        return false;
    }
}
0
amit veerani
public class RoatatedSorted {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub

    int[] rotArray = {5,6,7,8,9,10,15,16,17,1,2,3,4,};

    search(rotArray,0,rotArray.length-1,10);

}

private static void search(int[] a, int low, int high,int key) {

    //int mid =  low + (high-low)/2;
    //if(a[mid]==a[key]){System.out.println("element found at" + key+1 +" position"); return;}

    // find pos to split array
    int pos = findSplitIndex(a,low,high);
    System.out.println("split pos found at" + pos +" position");


    if(key>=a[low]&& key <=a[pos])
        bsearch(a,low,pos,key);
    if(key>=a[pos+1]&& key <=a[high])
        bsearch(a,pos+1,high,key);  
}


private static void bsearch(int[] a, int low, int high,int key) {
    // TODO Auto-generated method stub
    if(low>high) return;
    int mid =  low + (high-low)/2;
    if(a[mid]==key)
    {System.out.println("element found at" + ++mid +" position"); return;}
    if(a[mid] > key)
        bsearch(a,low,mid-1,key);
    if(a[mid]<key)
        bsearch(a,mid+1,high,key);
}

private static int findSplitIndex(int[] a, int low, int high) {
    // TODO Auto-generated method stub
    int mid;
    if(low>high)return -1;
    while(true) {
        mid =  low + (high-low)/2;
    if( a[mid]>a[mid+1])
    break;


    if(a[mid]>a[low])
        low=mid;
    if(a[high]>a[mid])
        high=mid;
}
    return mid;


}

}
0
Sunil T Nataraj

c'est une solution O(logn): testée. cela fonctionne à la fois sur les tableaux triés et tournés:

public static int rbs(int[] a, int l, int r, int t) {

    if (a[l] <= a[r]) {
        return bs(a, l, r, t);
    }

    if (r < l) {
        return -1;
    } else {
        int m = (l+r) / 2;
        if (a[m] == t) {
            return m;
        } else if (a[m] > t) { // check left half
            if (a[l] > a[m]) { // left is unsorted
                return rbs(a, l, m-1, t);
            } else { // left is sorted
                if (a[l] < t) { // t is in range
                    return bs(a, l, m-1, t);
                } else if (a[l] > t) { // t is out of range on left
                    if (a[r] >= t) {
                        return rbs (a, m+1, r, t);
                    } else
                        return -1;
                } else
                    return l;
            }
        } else { // other side
            if (a[r] < a[m]) { // right is unsorted
                return rbs(a, m+1, r, t);
            } else { // right is sorted
                if (a[r] > t) { // t is in range
                    return bs(a, m+1, r, t);
                } else if (a[r] < t) { // t is out of range on right side
                    if (a[l] <= t) {
                        return rbs (a, l, m-1, t);
                    } else
                        return -1;
                } else
                    return r;
            }
        }
    }
}


public static int bs(int[] a, int l, int r, int t) {

    int m = (l+r) / 2;

    if (r < l) {
        return -1;
    } else {
        if (a[m] == t)
            return m;
        else if (a[m] < t)
            return bs(a, m+1, r, t);
        else
            return bs (a, l, m-1, t);
    }
}
0
user1995294

Cette solution à PYTHON est expérimentée à 100%

Programme pour trouver un nombre dans un tableau trié mais pivoté  

def findNumber(a, x, start, end):
  if start > end:
    return  -1
  mid = (start + end) / 2
  if a[mid] == x:
    return mid
  ## Case : 1  if a[start] to a[mid] is sorted , then search first half of array
  if a[start] < a[mid]:
    if (x >= a[start] and x <= a[mid]):
      return findNumber(a, x, start, mid - 1)
    else:
      return findNumber(a,x, mid + 1, end)
  ## Case: 2 if a[start] to a[mid] is not sorted , then a[mid] to a[end] mist be sorted
  else:
    if (x >= a[mid] and x <= a[end]):
      return findNumber(a, x, mid + 1, end)
    else:
      return findNumber(a,x, start, mid -1)


a = [4,5,6,7,0,1,2]
print "Your array is  : " , a
x = input("Enter any number to find in array : ")
result = findNumber(a, x, 0, len(a) - 1)
print "The element is present at %d position: ", result
0
Karan Singh

Essayez cette solution,

     public static int findElement(int[] a, int key, int left, int right) {

           if (left > right) {
              return -1;
           }

           int mid = (left + right) / 2;

           if (key == a[mid]) {
              return mid;
           } else if (key < a[mid]) {
              return (a[left] <= a[mid] && a[left] < key ? findElement(
                a, key, left, mid - 1) : findElement(a, key,
                mid + 1, right));
           } else {
              return (a[mid] <= a[right] && a[right] < key ? findElement(
                a, key, left, mid - 1) : findElement(a, key,
                mid + 1, right));
    }

}
0
Sarath

D'abord pour trouver l'élément Minimum dans le tableau, puis divisez le tableau en deux parties. Après cette recherche la valeur donnée en utilisant l'arbre de recherche binaire. Complexité: O (logn) Si vous devez trouver l'élément Minimum dans le tableau pivoté, utilisez la même approche, ignorez simplement la recherche binaire. Complexité: O (logn)

//Search an element in a sorted and pivoted array
    class SearchInPivotedSortedArray
    {
        //searchInOrtedPivotedArray : Return index of matched element with given value.
        public static int searchInSortedPivotedArray(int[] A, int value)
        {
            int min = findMinElement(A,0,A.Length-1);
            if (min == A[0])
               return binarySearchTree(A, 0, A.Length-1, value);


            if (value <= A[A.Length-1])
               return binarySearchTree(A, min, A.Length-1, value);
            else
               return binarySearchTree(A, 0, min-1, value);


        }
        //return index of Pivot element
        public static int findMinElement(int[] Array, int low, int high)
        {

            if (low >= high)
                return low;

            int mid = (low + high) / 2;
            if (mid > low && Array[mid] < Array[mid - 1])
                return mid;
            if (mid < high && Array[mid] > Array[mid + 1])
                return mid + 1;

            if (Array[mid] < Array[high])
                return findMinElement(Array, low, mid - 1);

            return findMinElement(Array, mid + 1, high);

        }
        //Return match element index, if not found return -1
        public static int binarySearchTree(int[] array, int low, int high,int value)
        {
            if (low > high)
                return -1;
            int mid = (low + high)/2;

            if (array[mid] == value)
                return mid;
            if (array[mid] > value)
                return binarySearchTree(array, low, mid - 1, value);
            else
                return binarySearchTree(array, mid + 1, high, value);

        }
    }
0
rana_stack

def search (tableau, gauche, droite, cible): # Conditions d'arrêt pour la récursivité . sinon tableau: return -1 si laissé == à droite: retour gauche si array [left] == ​​cible else -1

    # Check if middle element is same as the target.
    mid = (left + right) / 2
    if array[mid] == target:
        return mid

    # Pivot point is at the right of the middle element.
    if array[left] <= array[mid]:
        if target >= array[left] and target <= array[mid]:
            return search(array, left, mid - 1, target)
        return search(array, mid + 1, right, target)

    # Pivot point is at the left of the middle element.
    if target >= array[mid] and target <= array[right]:
        return search(array, mid + 1, right, target)
    return search(array, left, mid - 1, target)

J'ai trouvé la solution de ce post

0
Mark
int findIndexInRotatedSort( vector<int> input, int s, int e, int toFind )
    {
        if (s > e || s >= input.size() || e < 0)
        {
            return -1;
        }

        int m = (s + e)/2;

        int sVal = input.at(s);
        int eVal = input.at(e);
        int mVal = input.at(m);

        if (sVal == toFind)
            return s;
        if (eVal == toFind)
            return e;
        if (mVal == toFind)
            return m;

        bool isFirstOrdered = (sVal < mVal);
        bool isSecondOrdered = (mVal < eVal);
        if (toFind > mVal)
        {
            if (!isSecondOrdered || toFind < eVal)
            {
                return findIndexInRotatedSort( input, m+1, e, toFind );
            }
            else
            {
                return findIndexInRotatedSort( input, s, m-1, toFind );
            }
        }
        else
        {
            if (!isFirstOrdered || toFind > sVal)
            {
                return findIndexInRotatedSort( input, s, m-1, toFind );
            }
            else
            {
                return findIndexInRotatedSort(  input, m+1, e, toFind );
            }
        }
    }
0
anon