정렬 및 회전 된 배열에서 검색


79

인터뷰를 준비하는 동안 다음과 같은 흥미로운 질문을 발견했습니다.

정렬 된 다음 회전 된 배열이 주어졌습니다.

예를 들면 :

  • arr = [1,2,3,4,5]정렬 된 Let ,
  • 제공하려면 오른쪽으로 두 번 회전하십시오 [4,5,1,2,3].

이제이 정렬 된 + 회전 된 배열에서 하나의 검색이 얼마나 최선일까요?

배열을 회전 해제 한 다음 이진 검색을 수행 할 수 있습니다. 그러나 둘 다 최악의 경우 O (N)이기 때문에 입력 배열에서 선형 검색을 수행하는 것보다 낫지 않습니다.

몇 가지 지침을 제공하십시오. 나는 이것에 대한 특별한 알고리즘을 많이 봤지만 아무것도 찾을 수 없었다.

C와 C ++를 이해합니다.


9
숙제라면 homework태그 를 추가 해주세요 . 그러면 사람들이 붙여 넣을 수있는 답변을 게시하는 대신 올바른 방향으로 부드럽게 넛지하도록 유도 할 수 있습니다.
sbi 2011 년

3
어레이가 회전 된 횟수를 알고 있습니까?
Yochai Timmer 2011 년

2
그 크기의 배열에 대해서는 전혀 걱정할 필요가 없습니다. 당신의 진짜 문제는 무엇입니까?
sbi 2011 년

3
아니, 숙제가 아닙니다. 나는 회전 수를 모른다. 그리고 그 예는 단순하게 유지되었습니다. 배열에는 수백만 개의 요소가있을 수 있습니다.
Jones

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 !!
Ashwin

이것이 제가 Stackoverflow를 좋아하는 이유입니다. 항상 문제를 해결하기위한 새롭고 최상의 접근 방식을 얻습니다. 감사 @codeaddict
바라트 SONI

3
"15"를 검색하는 것과 같이 중복 된 어레이를 찾지 못하므로 중복을 수용하기위한 솔루션의 변경 사항은 무엇입니까 {10, 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}?
Shadab Ansari

@ShadabAnsari 나는 그것이 완전히 다른 대답이 될 것이라고 생각합니다. 정렬 된 하위 배열에만 집중할 수 있으므로 중첩 된 조건을 따르는 것이 더 쉽습니다. 어레이의 일부가 아닌 전체 어레이에 대해 생각하는 데 문제가있었습니다.
Ehtesh Choudhury

4
"정렬 + 회전 배열의 흥미로운 특성은 두 개의 반으로 나눌 때 두 반쪽 중 적어도 하나가 항상 정렬된다는 것입니다." 이것은 천재 또는 경험이 초보자와 분리됩니다. 감사합니다.이 한 줄은 전체 솔루션을 시각화하는 데 도움이되었습니다.
wild_nothing jul.

21

허용되는 답변에는 배열에 중복 요소가있을 때 버그가 있습니다. 예를 들어, arr = {2,3,2,2,2}3은 우리가 찾고있는 것입니다. 그런 다음 수락 된 답변의 프로그램은 1 대신 -1을 반환합니다.

이 인터뷰 질문은 'Cracking the Coding Interview'책에 자세히 설명되어 있습니다. 중복 요소의 조건은 그 책에서 특별히 논의됩니다. 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와 같은 입력에서
Tomato

반복되는 요소가있는 경우 시간 복잡도는 얼마입니까?
프라 샨스 Debbadwar

중복이 허용되면 선형이어야한다고 생각합니다. 어딘가에 1하나가 2있는 N 의 목록을 고려하십시오 . 어디에나있을 수 있으며 유효한 입력이됩니다. 우리는 그것을 찾고 2있습니다. 우리가 고려하는 M> 2 숫자의 범위에 상관없이, 그것이 어느 쪽에도 없다면 2우리는 그것이 M 숫자에 포함되어 있는지 여부를 알 수 없습니다. 따라서 도움이되는 방식으로 검색 범위를 좁힐 수 없습니다.
Sopel

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)

편집 : 코드 .


감사합니다.하지만 첫 BS를 어떻게 적용 할 것인지 이해가 안 되나요?
Jones

검색 키는 무엇입니까?
Jones

@Jones, 코드를 작성하고 설명하는 것이 더 쉽습니다. 나는 대답을 편집하고 링크를 찾으십시오.
Max

4
"중단 점"을 명시 적으로 검색해야하는 이유는 무엇입니까? 수정 된 이진 검색을 직접 사용하여 요소를 검색하는 동시에 "이상"을 확인하지 않는 이유는 무엇입니까?
ruslik 2011 년

나는 여기에 늦었고 동일한 질문을 받았으며 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

내 첫 번째 시도는 이진 검색을 사용하여 적용된 회전 수를 찾는 것입니다. 이것은 일반적인 이진 검색 메커니즘을 사용하여 a [n]> a [n + 1] 인 인덱스 n을 찾아서 수행 할 수 있습니다. 그런 다음 찾은 시프트 당 모든 인덱스를 회전하면서 일반 이진 검색을 수행합니다.


부패 수를 찾기 위해 BS를 수행 할 때 검색 키는 무엇입니까?
존스

2
@Jones : 수정 된 이진 검색입니다. 두 개의 인접한 값이 감소하는 지점을 찾고 있습니다. 색인을 맞춰보세요. 해당 인덱스의 값이 배열의 첫 번째 값보다 크면 추측의 오른쪽을 계속 찾으십시오. 더 적 으면 계속 왼쪽을 봅니다. 그러나 대구 중독자의 대답은 실제로 불연속성이 어디에 있는지 신경 쓰지 않고 검색을 수행하고 싶을 때 더 좋습니다 .
Steve Jessop 2011 년

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

얼마나 (멀리) 회전했는지 안다면 이진 검색을 할 수 있습니다.

비결은 두 가지 수준의 인덱스를 얻는 것입니다. 가상 0..n-1 범위에서 bs를 수행 한 다음 실제로 값을 찾을 때 회전을 해제합니다.


2

위에서 언급 한 게시물에 대한 답글 "이 인터뷰 질문은 'Cracking the Coding Interview'책에서 자세히 논의됩니다. 중복 요소의 조건은 해당 책에서 특별히 논의됩니다. op가 코멘트에서 배열 요소는 무엇이든 될 수 있다고 말했기 때문에 저는 내 솔루션을 아래의 의사 코드로 제공하고 있습니다. "

귀하의 솔루션은 O (n) !! (단일 조건에 대해 배열의 절반을 모두 확인하는 마지막 if 조건은 선형 시간 복잡도의 솔이됩니다)

코딩 라운드 중에 버그와 세분화 오류의 미로에 갇히는 것보다 선형 검색을 수행하는 것이 좋습니다.

회전 정렬 된 배열 (중복 포함)에서 검색하는 경우 O (n)보다 더 나은 솔루션이 있다고 생각하지 않습니다.


2

먼저 배열을 회전 할 필요가 없습니다. 회전 된 배열에서 이진 검색을 사용할 수 있습니다 (일부 수정 포함).

N을 검색중인 번호로 지정하십시오.

첫 번째 숫자 (arr [start])와 배열 중간의 숫자 (arr [end])를 읽습니다.

  • arr [start]> arr [end]-> 첫 번째 절반은 정렬되지 않지만 두 번째 절반은 정렬됩니다.

    • arr [end]> N-> 숫자가 인덱스에있는 경우 : (middle + N-arr [end])

    • 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; 
}
이것이 도움이되기를 바랍니다! =)
Soon Chee Loong, 
토론토 대학교 

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) 파이썬 솔루션입니다. 두 개의 인덱스 만 확인할 때까지 회전 된 배열을 반으로 자르고 하나의 인덱스가 일치하면 정답을 반환합니다.

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

자바 스크립트에서

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], target = 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

mid, mid-1 등으로 인해 바이너리 검색에 대해 마음에 들지 않는 것이 있기 때문에 항상 바이너리 스트라이드 / 점프 검색을 사용합니다.

회전 된 배열에서 사용하는 방법? 두 번 사용 (한 번 시프트를 찾은 다음 .at ()를 사용하여 시프트 된 인덱스-> 원래 인덱스를 찾습니다)

또는 첫 번째 요소를 비교합니다. 첫 번째 요소보다 작 으면 끝 근처에 있어야합니다.

끝에서 뒤로 점프 검색을 수행하고, 피벗 타이어 리 멘트가 발견되면 중지

> 시작 요소이면 일반 점프 검색을 수행하십시오. :)


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;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.