Algorithm/Rotate Array

[Practice Algorithm, GeeksForGeeks] Search pivot in an sorted and rotated array

Tree_Park 2020. 11. 11. 23:16
728x90

음.. 정렬되고, 순회되어 변경된 array에서 해당 key를 찾고 index를 반환하는 알고리즘이다. 당장 생각나는 바로는 무식하게 for문으로 돌려서 O(n)으로 찾을 수 있겠지만, 문제에서 찾고자 하는 바는 O(logn)의 시간복잡도를 원한다.

음.. 이진트리를 쓰면 O(logn)으로 줄여볼 수 있을거 같은데, 이진트리를 쓰지않고서 할 수 있는 방법이 있을까?

 

practice.geeksforgeeks.org/problems/search-in-a-rotated-array/0

 

Search in a Rotated Array | Practice | GeeksforGeeks

 

practice.geeksforgeeks.org


문제

Given a sorted and rotated array A of N distinct elements which is rotated at some point, and given an element K. The task is to find the index of the given element K in the array A.

Input:
The first line of the input contains an integer T, denoting the total number of test cases. Then T test cases follow. Each test case consists of three lines. First line of each test case contains an integer N denoting the size of the given array. Second line of each test case contains N space separated integers denoting the elements of the array A. Third line of each test case contains an integer K denoting the element to be searched in the array.

Output:
Corresponding to each test case, output the index of the element found in the array.  If element is not present, then output -1.

Constraints:
1 ≤ T ≤ 100
1 ≤ N ≤ 107
0 ≤ Ai ≤ 108
1 ≤ K ≤ 108

Example:
Input:
3
9
5 6 7 8 9 10 1 2 3
10
3
3 1 2
1
4
3 5 1 2
6

Output:
5
1
-1

Explanation:
Testcase 1: 10 is found at index 5.


우선 무식하게 for문을 돌려보았다.

코드

package rotate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class SearchInRotatedArray {

	public static int searchPivot(Integer[] arr, int n, int k) {		
		for(int i=0; i<n; i++) {
			if(arr[i] == k) return i; 
		}
		return -1;
	}
	
	
	public static void main(String[] args) {
		try(BufferedReader bufferedReader = new BufferedReader(
			new InputStreamReader(System.in))
		)
		{
			int testCase = Integer.parseInt(bufferedReader.readLine());
			StringBuilder stringBuilder = new StringBuilder();
			for(int i=0; i<testCase; i++) {
				int n = Integer.parseInt(bufferedReader.readLine());
				Integer[] arr = new Integer[n];
				Arrays.stream(bufferedReader.readLine().split(" "))
					.map(s->Integer.parseInt(s))
					.collect(Collectors.toList())
					.toArray(arr);
				
				int k = Integer.parseInt(bufferedReader.readLine());
				stringBuilder.append(
						searchPivot(arr, n, k) + "\n");				
			}
			System.out.println(stringBuilder.toString());			
		}catch(IOException ex) {
			ex.printStackTrace();
		} catch(NumberFormatException ex) {
			ex.printStackTrace();
		}				
	}

}

최대한 I/O에 쓰이는 입출력 시간을 줄이는 꼼수를 써서 드는 전체 time을 줄여보았다.

당연히 통과는 했지만..

내 적당적당함에 절망해버렸다. 이 챕터에서는 아직 다른 자료구조를 배우지 않은 상태이고, 방법이 있을것이리라 생각하고 좀 더 고집을 부려보기로 했다.

public static int searchPivot(Integer[] arr, int n, int k) {		
		for(int i=0; i<n; i++) {			
			if(arr[0]>k && n>1 && arr[1]>arr[0]) {
				for(int j=n-1; j>0; j--) {
					if(arr[j]==k) return j;
				}
				return -1;
			}
			if(arr[i] == k) return i; 
		}
		return -1;
	}

조건을 주었다.

    - 배열의 첫 번째 요소가 찾으려는 피벗보다 크고

    - 배열의 사이즈가 1보다 크고, 배열의 두 번째 요소가 첫 번째 요소보다 클 때(즉, 정렬되어있을 때)

배열을 뒤에서부터 순회하면 되겠네!

음.. 시간이 줄긴 줄었는데.. logn인가?

 


문제에서는 어떻게 풀었는지 확인해 보았다. binary search를 활용했잖어..?

 예제로 나온 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.stream.Collectors;

public class SearchInRotatedArray {

	    public static int pivotedBinarySearch(Integer arr[], int n, int key) 
	    { 
	        int pivot = findPivot(arr, 0, n - 1); 
	  
	        // If we didn't find a pivot, then 
	        // array is not rotated at all 
	        if (pivot == -1) 
	            return binarySearch(arr, 0, n - 1, key); 
	  
	        // If we found a pivot, then first 
	        // compare with pivot and then 
	        // search in two subarrays around pivot 
	        if (arr[pivot] == key) 
	            return pivot; 
	        if (arr[0] <= key) 
	            return binarySearch(arr, 0, pivot - 1, key); 
	        return binarySearch(arr, pivot + 1, n - 1, key); 
	    } 
	  
	    /* Function to get pivot. For array  
	       3, 4, 5, 6, 1, 2 it returns 
	       3 (index of 6) */
	    public static int findPivot(Integer arr[], int low, int high) 
	    { 
	        // base cases 
	        if (high < low) 
	            return -1; 
	        if (high == low) 
	            return low; 
	  
	        /* low + (high - low)/2; */
	        int mid = (low + high) / 2; 
	        if (mid < high && arr[mid] > arr[mid + 1]) 
	            return mid; 
	        if (mid > low && arr[mid] < arr[mid - 1]) 
	            return (mid - 1); 
	        if (arr[low] >= arr[mid]) 
	            return findPivot(arr, low, mid - 1); 
	        return findPivot(arr, mid + 1, high); 
	    } 
	  
	    /* Standard Binary Search function */
	    public static int binarySearch(Integer arr[], int low, int high, int key) 
	    { 
	        if (high < low) 
	            return -1; 
	  
	        /* low + (high - low)/2; */
	        int mid = (low + high) / 2; 
	        if (key == arr[mid]) 
	            return mid; 
	        if (key > arr[mid]) 
	            return binarySearch(arr, (mid + 1), high, key); 
	        return binarySearch(arr, low, (mid - 1), key); 
	    }
	
	public static void main(String[] args) {
		try(BufferedReader bufferedReader = new BufferedReader(
			new InputStreamReader(System.in))
		)
		{
			int testCase = Integer.parseInt(bufferedReader.readLine());
			StringBuilder stringBuilder = new StringBuilder();
			for(int i=0; i<testCase; i++) {
				int n = Integer.parseInt(bufferedReader.readLine());
				Integer[] arr = new Integer[n];
				Arrays.stream(bufferedReader.readLine().split(" "))
					.map(s->Integer.parseInt(s))
					.collect(Collectors.toList())
					.toArray(arr);
				
				int k = Integer.parseInt(bufferedReader.readLine());
				stringBuilder.append(
						pivotedBinarySearch(arr, n, k) + "\n");				
			}
			System.out.println(stringBuilder.toString());			
		}catch(IOException ex) {
			ex.printStackTrace();
		} catch(NumberFormatException ex) {
			ex.printStackTrace();
		}				
	}

}

.

으음.. index를 divided 한 것까지는 생각했는데, 머리가 안돌아가는지 디버깅만 계속하다가 실패했다. 하지만, 예제로 나온 코드를 보니 내가 정답에 거의 근접했다는 것을 볼 수 있었다 ㅠ.. 좀만 더 시간 투자해볼껄..

내 코드보다 0.1초나 줄었다.. 좀만 더 생각하자 이 바보야..

결과

😒 input data 적용은 저번 코드를 그대로 참고했다.

🤔 sorted, rotated를 활용한 것까지는 좋은데..

🤢 sorted면, 바로 이진트리 생각난 것도 좋은데, 왜 그 내부 로직은 적용할 생각을 못한걸까

😜 생각도 좀 하자 바보야..

 

* 위에서 제시한 코드 이외에도 여러 방법이 나와있고, 각각 친절하게 원리와 시간복잡도 및 공간복잡도가 명시되어있으니 필요하면 직접 사이트에서 봐보는것을 추천한다.

www.geeksforgeeks.org/array-rotation/

 

Program for array rotation - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org