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