정렬 및 회전 된 배열에서 검색
인터뷰를 준비하는 동안 다음과 같은 흥미로운 질문을 발견했습니다.
정렬 된 다음 회전 된 배열이 주어졌습니다.
예를 들면 :
arr = [1,2,3,4,5]정렬 된 Let ,- 제공하려면 오른쪽으로 두 번 회전하십시오
[4,5,1,2,3].이제이 정렬 된 + 회전 된 배열에서 하나의 검색이 얼마나 최선일까요?
배열을 회전 해제 한 다음 이진 검색을 수행 할 수 있습니다. 그러나 둘 다 최악의 경우 O (N)이기 때문에 입력 배열에서 선형 검색을 수행하는 것보다 낫지 않습니다.
몇 가지 지침을 제공하십시오. 나는 이것에 대한 특별한 알고리즘을 많이 봤지만 아무것도 찾을 수 없었다.
C와 C ++을 이해합니다.
이것은 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
여기서 핵심은 하나의 하위 배열이 항상 정렬되어 배열의 절반을 버릴 수 있다는 것입니다.
인덱스를 찾을 수 첫째 : 당신은 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)
편집 : 코드 .
허용되는 답변에는 배열에 중복 요소가있을 때 버그가 있습니다. 예를 들어, 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
내 첫 번째 시도는 이진 검색을 사용하여 적용된 회전 수를 찾는 것입니다. 이것은 일반적인 이진 검색 메커니즘을 사용하여 a [n]> a [n + 1] 인 인덱스 n을 찾아서 수행 할 수 있습니다. 그런 다음 찾은 시프트 당 모든 인덱스를 회전하면서 일반 이진 검색을 수행합니다.
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;
}
배열이 s 오른쪽으로 회전 된 것을 알고 있다면 s를 오른쪽으로 이동 한 이진 검색을 수행 할 수 있습니다. 이것은 O (lg N)
즉, 왼쪽 제한을 s로, 오른쪽을 (s-1) mod N으로 초기화하고 이진 검색을 수행하여 올바른 영역에서 작업하기 위해 약간의주의를 기울입니다.
배열이 얼마나 회전했는지 모르는 경우 이진 검색 (O (lg N))을 사용하여 회전의 크기를 확인한 다음 이동 이진 검색, O (lg N), a 여전히 O (lg N)의 총합.
얼마나 (멀리) 회전했는지 안다면 이진 검색을 할 수 있습니다.
비결은 두 가지 수준의 인덱스를 얻는 것입니다. 가상 0..n-1 범위에서 bs를 수행 한 다음 실제로 값을 찾을 때 회전을 해제합니다.
먼저 배열을 회전 할 필요가 없습니다. 회전 된 배열에서 이진 검색을 사용할 수 있습니다 (일부 수정 포함).
N이 검색하는 번호라고 가정합니다.
첫 번째 숫자 (arr [start])와 배열 중간의 숫자 (arr [end])를 읽습니다.
arr [start]> arr [end]-> 첫 번째 절반은 정렬되지 않지만 두 번째 절반은 정렬됩니다.
arr [end]> N-> 숫자가 인덱스에있는 경우 : (middle + N-arr [end])
N이면 배열의 첫 번째 부분에서 검색을 반복합니다 (배열의 전반부 중간이되는 끝 참조).
(첫 번째 부분이 정렬되었지만 두 번째 부분이 정렬되지 않은 경우 동일)
위에서 언급 한 게시물에 대한 답글 "이 인터뷰 질문은 'Cracking the Coding Interview'책에서 자세히 논의됩니다. 중복 요소의 조건은 해당 책에서 특별히 논의됩니다. op가 코멘트에서 배열 요소는 무엇이든 될 수 있다고 말했기 때문에 저는 내 솔루션을 아래의 의사 코드로 제공하고 있습니다. "
귀하의 솔루션은 O (n) !! (단일 조건에 대해 배열의 절반을 모두 확인하는 마지막 if 조건은 선형 시간 복잡도의 솔이됩니다.)
코딩 라운드 중에 버그와 세분화 오류의 미로에 갇히는 것보다 선형 검색을 수행하는 것이 좋습니다.
회전 정렬 된 배열 (중복 포함)에서 검색하는 경우 O (n)보다 더 나은 솔루션이 있다고 생각하지 않습니다.
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;
}
}
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;
}
먼저 시프트 상수 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, 토론토 대학교
다음은 원래 배열을 수정하지 않는 간단한 (시간, 공간) 효율적인 비 재귀 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
반복되는 값으로 작동하는 또 다른 접근 방식은 회전을 찾은 다음 배열에 액세스 할 때마다 회전을 적용하는 정규 이진 검색을 수행하는 것입니다.
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))
이 솔루션 시도
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;
}
중복 된 회전 배열의 경우 요소의 첫 번째 발생을 찾아야하는 경우 아래 절차 (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)
내 간단한 코드 :-
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)).
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;
}
질문 : 회전 정렬 된 배열에서 검색
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;
}
}
참고 URL : https://stackoverflow.com/questions/4773807/searching-in-a-sorted-and-rotated-array
'Program Club' 카테고리의 다른 글
| iOS Swift에서 원을 어떻게 그리나요? (0) | 2020.11.12 |
|---|---|
| C #에서 html을 구문 분석하는 가장 좋은 방법은 무엇입니까? (0) | 2020.11.12 |
| WPF에서 점선 또는 점선 테두리를 어떻게 얻을 수 있습니까? (0) | 2020.11.12 |
| 스토리 보드로 가로로 디자인하기 (0) | 2020.11.12 |
| Android Studio 3.1에서 기호 (테마, 위젯, 속성 등)를 확인할 수 없습니다. (0) | 2020.11.12 |