Пошук у відсортованому та поверненому масиві


79

Готуючись до співбесіди, я наткнувся на це цікаве питання:

Вам був наданий масив, який сортується, а потім обертається.

Наприклад:

  • Нехай arr = [1,2,3,4,5], що сортується
  • Поверніть його двічі вправо, щоб дати [4,5,1,2,3].

Тепер, як найкраще шукати в цьому відсортованому + поверненому масиві?

Можна повернути масив, а потім виконати двійковий пошук. Але це не краще, ніж виконувати лінійний пошук у вхідному масиві, оскільки обидва варіанти є найгіршим варіантом O (N).

Будь ласка, надайте кілька вказівок. Я багато гуглив на спеціальних алгоритмах для цього, але не міг знайти жодного.

Я розумію C та C ++.


9
Якщо це домашнє завдання, додайте homeworkтег. Це заохочувало б людей лагідно підштовхувати вас у правильному напрямку, замість того, щоб розміщувати відповіді, що відповідають умовам.
sbi

3
Чи знаєте ви, скільки разів обертали масив?
Yochai Timmer

2
Для масиву такого розміру вам зовсім не потрібно турбуватися. У чому ваша справжня проблема?
sbi

3
Ні, це не домашнє завдання. Я не знаю кількість обертань. І приклад був простим. Масив може містити мільйони елементів.
Джонс,

1
Чи завжди масив має послідовні значення, починаючи з 1? Або він може мати що-небудь (включаючи дублікати)?
The Archetypal Paul

Відповіді:


210

Це можна зробити за O(logN)допомогою дещо зміненого двійкового пошуку.

Цікавою властивістю відсортованого + поверненого масиву є те, що коли ви ділите його на дві половини, принаймні одна з двох половин завжди буде відсортована.

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

як здається, правий підмасив не сортується, тоді як лівий підмасив сортується.

Якщо середина є точкою обертання, їх буде відсортовано як лівий, так і правий підмасив.

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

Але в будь-якому випадку потрібно відсортувати одну половину (підмасив) .

Ми можемо легко дізнатись, яка половина сортується, порівнюючи початковий і кінцевий елементи кожної половини.

Як тільки ми знайдемо, яка половина сортується, ми можемо побачити, чи присутній ключ у цій половині - просте порівняння з крайніми.

Якщо ключ присутній у тій половині, ми рекурсивно викликаємо функцію на тій половині,
інакше ми рекурсивно викликаємо наш пошук на другій половині.

Ми відкидаємо половину масиву в кожному виклику, що робить цей алгоритм O(logN).

Псевдокод:

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

Ключ тут полягає в тому, що один підмасив завжди буде відсортований, за допомогою якого ми можемо відкинути половину масиву.


3
Простий. Короткий. Приклади. FTW !!
Ашвін

і саме тому мені подобається Stackoverflow, ви завжди отримуєте новий і найкращий підхід до вирішення проблеми. Спасибі @codeaddict
Бхарат Соні

3
Якою має бути зміна рішення для розміщення дублікатів, оскільки це не впливає на масив із дублікатами, як пошук у "15" {10, 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}?
Шадаб Ансарі,

@ShadabAnsari Я думаю, що це була б зовсім інша відповідь. Цього легше дотримуватись із вкладеними умовами, оскільки це дозволяє зосередитись лише на відсортованому підмасиві. У мене були проблеми, коли я думав про весь масив, а не лише про його частину.
Ехтеш Чоудхурі

4
"Цікавою властивістю відсортованого + поверненого масиву є те, що коли ви ділите його на дві половини, принаймні одна з двох половин завжди буде відсортовано." Це відокремлює генія або досвідченого від початківця. Дякую, цей один рядок допоміг мені наочно уявити ціле рішення.
wild_nothing

21

Прийнята відповідь має помилку, коли в масиві є повторювані елементи. Наприклад, arr = {2,3,2,2,2}а 3 - це те, що ми шукаємо. Тоді програма у прийнятій відповіді поверне -1 замість 1.

Це запитання про інтерв’ю детально обговорюється в книзі „Зламування інтерв’ю з кодуванням”. Стан повторюваних елементів спеціально обговорюється в цій книзі. Оскільки op сказав у коментарі, що елементи масиву можуть бути будь-якими, я даю своє рішення як псевдокод нижче:

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
Я думаю, ви єдиний, хто правильно розглянув повторювані елементи. Але ваш підхід не гарантує логарифмічної складності. Особливо на вхідних даних, таких як 5,5,5,5,5,5, ... (багато виправлень), 5,1, 5
Помідор

Яка складність часу, якщо ми маємо повторювані елементи?
Прашант Деббадвар,

Я думаю, що воно має бути лінійним, якщо допускаються дублікати. Розглянемо список N 1з 2десь одним . Це може бути де завгодно, і це буде дійсним введенням. Ми це шукаємо 2. Незалежно від того, який діапазон M> 2 чисел ми розглядаємо, якщо його 2немає на жодній зі сторін, ми не можемо сказати, чи міститься він у цих M числах чи ні. Тож ви не можете звузити пошук будь-яким способом, який допомагає.
Сопель

18

Ви можете зробити 2 двійкові пошуки: спочатку знайти такий індекс i, що arr[i] > arr[i+1].

Мабуть, (arr\[1], arr[2], ..., arr[i])і (arr[i+1], arr[i+2], ..., arr[n]) є обидва відсортовані масиви.

Тоді якщо arr[1] <= x <= arr[i], ви виконуєте двійковий пошук у першому масиві, інакше у другому.

Складність O(logN)

EDIT: код .


Спасибі макс, але я не розумію, як ти застосуєш свою першу BS?
Джонс,

Я маю на увазі, яким буде ваш ключ пошуку?
Джонс,

@Jones, простіше написати код, ніж пояснити. Я відредагував відповідь, шукайте посилання.
Макс

4
Чому вам доводиться явно шукати "точку зламу"? Чому б не використовувати модифікований двійковий пошук безпосередньо для пошуку елемента та одночасно для перевірки на наявність «аномалій»?
ruslik

Я запізнююся тут, мені задали те саме питання, я не вирішив його в O (logn), моє рішення було трохи вдосконалено щодо O (n). Моїм підходом був два покажчики i = 0 та j = size () - 1, у циклі перевірити ключ arr [i] == або arr [j] == якщо знайдено повернути i або j і розбити, інакше icrement i та декремент j, умова розриву була i <j, у цьому випадку цикл буде працювати в гіршому випадку n / 2 номери часу, якщо ключ присутній посередині
Jaydeep Shil

8

Моєю першою спробою було б знайти за допомогою двійкового пошуку кількість застосованих обертань - це можна зробити, знайшовши індекс n де a [n]> a [n + 1] за допомогою звичайного двійкового механізму пошуку. Потім виконайте звичайний двійковий пошук, обертаючи всі індекси за знайдену зміну.


Яким буде ваш ключ пошуку при виконанні BS, щоб знайти кількість гнилі?
Джонс,

2
@Jones: це був би модифікований двійковий пошук. Ви шукаєте точку, в якій зменшуються два сусідні значення. Вгадайте індекс. Якщо значення цього індексу більше першого значення в масиві, продовжуйте шукати праворуч від вашого здогаду. Якщо менше, дивіться ліворуч. Але відповідь кодексу краще, якщо вам насправді все одно, де перерив , ви просто хочете виконати пошук.
Steve Jessop

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

Як зазначалося вище, це не розгляне випадки дублікатів записів. Однак, якщо елементи унікальні, це дуже простий код, оскільки він не робить жодної рекурсії.
kaushal

3

Якщо ви знаєте, що масив повернуто s вправо, ви можете просто виконати двійковий пошук, зміщений s вправо. Це O (lg N)

Під цим, я маю на увазі, ініціалізуйте лівий ліміт до s і правий до (s-1) mod N, і виконайте двійковий пошук між ними, доклавши трохи уваги, щоб працювати у правильній області.

Якщо ви не знаєте, на скільки обернено масив, ви можете визначити, наскільки велике обертання, використовуючи двійковий пошук, який дорівнює O (lg N), а потім виконайте зміщений двійковий пошук, O (lg N), a загальна сума O (lg N) досі.


2

Якщо ви знаєте, наскільки (далеко) його обертали, ви все одно можете виконати двійковий пошук.

Фокус у тому, що ви отримуєте два рівні індексів: ви робите bs у віртуальному діапазоні 0..n-1, а потім розкручуєте їх, коли фактично шукаєте значення.


2

Відповідь на вищезгадану публікацію "Це питання інтерв'ю детально обговорюється в книзі" Зламування інтерв'ю кодування ". У цій книзі спеціально обговорюється стан дублікатів елементів. Оскільки в коментарі ОП зазначено, що елементи масиву можуть бути будь-якими, я я даю своє рішення як псевдокод нижче: "

Ваше рішення O (n) !! (Остання умова if, коли ви перевіряєте обидві половини масиву на наявність однієї умови, робить його золем лінійної часової складності)

Мені краще зробити лінійний пошук, ніж застрявати в лабіринті помилок та помилок сегментації під час кодування.

Я не думаю, що є краще рішення, ніж O (n), для пошуку в обертаному відсортованому масиві (з дублікатами)


2

Спочатку не потрібно обертати масив. Ви можете використовувати двійковий пошук на поверненому масиві (з деякими змінами).

Нехай N - це число, яке ви шукаєте:

Прочитайте перше число (arr [початок]) та число в середині масиву (arr [кінець]):

  • якщо arr [початок]> arr [кінець] -> перша половина не сортується, але друга половина сортується:

    • якщо arr [кінець]> N -> число в індексі: (середнє + N - арр [кінець])

    • якщо N повторити пошук у першій частині масиву (див. кінець - середину першої половини масиву тощо)

(те саме, якщо перша частина відсортована, а друга - ні)


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

}

1
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

Спочатку потрібно знайти константу зсуву, k. Це можна зробити за час O (lgN). З постійного зсуву k ви можете легко знайти елемент, який шукаєте, за допомогою двійкового пошуку з константою k. Розширений двійковий пошук також займає час O (lgN). Загальний час роботи становить O (lgN + lgN) = O (lgN)

Щоб знайти постійний зсув, k. Вам просто потрібно шукати мінімальне значення в масиві. Індекс мінімального значення масиву повідомляє вам про постійний зсув. Розглянемо відсортований масив [1,2,3,4,5].

Можливі зрушення:
    [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 

Щоб зробити будь-який алгоритм за час O (lgN), головне - завжди знаходити способи розділити проблему навпіл. Після цього решта деталей реалізації стає легкою

Нижче наведено код на алгоритмі на C ++

// 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; 
}
Сподіваюся, це допомагає! =)
Незабаром Чі Лун, 
Університет Торонто 

1

Для поверненого масиву з дублікатами, якщо потрібно знайти перше входження елемента, можна скористатися наведеною нижче процедурою (код 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);
    }       

}

Це вдосконалення вищезазначеної процедури кодексів. Зверніть увагу на додаткову умову if, як показано нижче:

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

0

Ось просте (у часі, просторі) ефективне нерекурсивне рішення O (log n) python, яке не змінює вихідний масив. Обрізає повернутий масив навпіл, поки у мене не буде лише двох індексів для перевірки та поверне правильну відповідь, якщо один індекс відповідає.

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

Спробуйте це рішення

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

Цей код у C ++ повинен працювати у всіх випадках, хоча він працює з дублікатами, будь ласка, повідомте мене, якщо в цьому коді є помилка.

#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

У Javascript

var search = function(nums, target,low,high) {
    low= (low || low === 0) ? low : 0;

    high= (high || high == 0) ? high : nums.length -1;

    if(low > high)
        return -1;

    let mid = Math.ceil((low + high) / 2);


    if(nums[mid] == target)
        return mid;

    if(nums[low] < nums[mid]) {
        // if key is in the left half
        if (nums[low] <= target && target <= nums[mid]) 
            // search the left half
            return search(nums,target,low,mid-1);
        else
            // search the right half                 
            return search(nums,target,mid+1,high);
    } else {
        // if the key is in the right half.
        if(nums[mid] <= target && nums[high] >= target) 
            return search(nums,target,mid+1,high)
        else
            return search(nums,target,low,mid-1)
    }
};

Вхід: nums = [4,5,6,7,0,1,2], ціль = 0 Вихід: 4


0
import java.util.*;

class Main{
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int arr[]=new int[n];
        int max=Integer.MIN_VALUE;
        int min=Integer.MAX_VALUE;
        int min_index=0,max_index=n;

        for(int i=0;i<n;i++){
            arr[i]=sc.nextInt();
            if(arr[i]>max){
                max=arr[i];
            max_index=i;
            }
            if(arr[i]<min){
                min=arr[i];
                min_index=i;
            }

        }

        int element=sc.nextInt();
        int index;
        if(element>arr[n-1]){
            index=Arrays.binarySearch(arr,0,max_index+1,element);
        }
        else {
             index=Arrays.binarySearch(arr,min_index,n,element);
        }
        if(index>=0){
            System.out.println(index);
        }
        else{
            System.out.println(-1);
        }
    }

}

0

Ось мої два центи:

  • Якщо масив не містить дублікатів, рішення можна знайти в O (log (n)). Як багато людей доводили це, допрацьована версія двійкового пошуку може бути використана для пошуку цільового елемента.

  • Однак, якщо масив містить дублікати, я думаю, що немає можливості знайти цільовий елемент в O (log (n)). Ось приклад показує, чому, на мою думку, O (log (n)) неможливий. Розглянемо два масиви нижче:

a = [2,.....................2...........3,6,2......2]
b = [2.........3,6,2........2......................2]

Усі крапки заповнені цифрою 2. Ви бачите, що обидва масиви сортуються та обертаються. Якщо хтось хоче розглянути бінарний пошук, то їм доведеться скоротити домен пошуку наполовину на кожній ітерації - ось як ми отримуємо O (log (n)). Припустимо, що ми шукаємо число 3. У першому випадку ми можемо побачити, як воно ховається в правій частині масиву, а у другому випадку воно ховається в другій стороні масиву. Ось що ми знаємо про масив на цьому етапі:

  • ліворуч = 0
  • справа = довжина - 1;
  • середина = ліворуч + (праворуч - ліворуч) / 2;
  • arr [середина] = 2;
  • arr [зліва] = 2;
  • arr [праворуч] = 2;
  • ціль = 3;

Це вся інформація, якою ми маємо. Ми чітко бачимо, що для прийняття рішення про виключення половини масиву недостатньо. В результаті цього єдиним способом є лінійний пошук. Я не кажу, що ми не можемо оптимізувати цей O (n) час, все, що я кажу, це те, що ми не можемо робити O (log (n)).


0

Щось мені не подобається у двійковому пошуку через середину, середину 1 тощо, тому я завжди використовую двійковий пошук кроком / стрибком

Як використовувати його на оберненому масиві? використовувати двічі (один раз знайти зсув, а потім використовувати .at (), щоб знайти зсунутий індекс -> вихідний індекс)

Або порівняйте перший елемент, якщо він менше першого елемента, він повинен знаходитися біля кінця

виконайте пошук стрибків назад із кінця, зупиніться, якщо знайдено будь-який шарнірний лейо

якщо це> елемент start, просто виконайте звичайний пошук стрибків :)


0

Реалізовано за допомогою C #

public class Solution {
        public int Search(int[] nums, int target) {
             if (nums.Length == 0) return -1;
                int low = 0;
                int high = nums.Length - 1;
                while (low <= high)
                {
                    int mid = (low + high) / 2;
                    if (nums[mid] == target) return mid;
                    if (nums[low] <= nums[mid]) // 3 4 5 6 0 1 2
                    {
                        if (target >= nums[low] && target <= nums[mid])
                            high = mid;
                        else
                            low = mid + 1;
                    }
                    else // 5 6 0 1 2 3 4
                    {
                        if (target >= nums[mid] && target <= nums[high])
                            low= mid;
                        else
                            high = mid - 1;
                    }
                }
                return -1;
        }
    }

-1

Іншим підходом, який би працював з повторюваними значеннями, є пошук обертання, а потім регулярний двійковий пошук із застосуванням обертання кожного разу, коли ми отримуємо доступ до масиву.

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))

-1

Мій простий код: -

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

Складність часу O (log (N)).


-1

Запитання: Пошук у поворотному відсортованому масиві

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.