코딩 테스트 및 알고리즘/leetcode for google

leetcode hard : Median Of Two Sorted Arrays

띠리링구 2022. 12. 30. 18:56

Median of Two Sorted Arrays

https://leetcode.com/problems/median-of-two-sorted-arrays/

풀기 전

예전에 이미 풀어봤고 감명깊게 봤던 문제라서 잘 기억하고 있다.

이 문제는 log time에 풀어야 한다.

두 array를 merge하는 순간 O(n + m)의 시간복잡도가 발생해 로그타임에 풀 수가 없다.

sorted, log time → binary search 연상.

두 array의 길이를 각가 m,n이라고 하자.

두 array를 merge하면 길이가 (m+n)이 될것이다.

median은 (m+n)이 홀수인경우 (m+n)//2 번째 요소고

짝수인경우 (m+n)//2번째 요소와 (m+n)//2-1번째 요소의 평균값이다.

HALF_LENGTH = (m+n)//2 라고 하자.

두 array를 임의의 지점에서 잘랐을 때 각 쪼개진 array들의 길이가 n1, n2, m1, m2라고 하자.

이때 (n1+m1)가 (n2+m2)랑 같거나 1차이 난다면 n1,m1의 오른쪽 끝 값과 n2,m2의 왼쪽 끝 값들을 조사해서 두 array를 합쳤을 경우의 (m+n)//2번째 요소와 (m+n)//2-1번째 요소를 찾을 수 있다.

솔루션 학습

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        if len(nums1) > len(nums2):
            return self.findMedianSortedArrays(nums2, nums1)
        
        total_len = len(nums1) + len(nums2)
        half = total_len // 2
        low, high = 0, len(nums1) # 자를 지점이라서 len(nums1) 포함

        MIN = -10**6
        MAX = 10**6

        while low <= high:
            mid1 = (low + high) // 2
            mid2 = half - mid1

            # mid가 자르고 난뒤 오른쪽 array의 시작점

            left1 = nums1[mid1 - 1] if mid1 > 0 else MIN
            right1 = nums1[mid1] if mid1 < len(nums1) else MAX

            left2 = nums2[mid2 - 1] if mid2 > 0 else MIN
            right2 = nums2[mid2] if mid2 < len(nums2) else MAX

            if left1 > right2:
                high = mid1 - 1
            elif right1 < left2:
                low = mid1 + 1
            else:
                left_max = max(left1, left2)
                right_min = min(right1, right2)
                return right_min if total_len % 2 != 0 else (left_max + right_min) / 2

변수 초기화

        if len(nums1) > len(nums2):
            return self.findMedianSortedArrays(nums2, nums1)
        
        total_len = len(nums1) + len(nums2)
        half = total_len // 2
        low, high = 0, len(nums1) # 자를 지점이라서 len(nums1) 포함

        MIN = -10**6
        MAX = 10**6
  • if 문 : nums1이 nums2보다 짧음을 보장하기 위해 추가. 짧은 array를 정해놔야 구현도 편하고 이진탐색 시간복잡도도 줄어든다.
  • total_len : nums1과 nums2의 길이를 합친 것. 둘을 merge하면 총 길이가 얼마나 될까에 대한 답
  • half : total_len의 반
  • low, high : nums1을 이진탐색하기 위한 하한선과 상한선. mid값이 잘려진 array의 시작점이 될것이므로 len(nums1)도 포함해야함
  • MIN, MAX : array의 값으로 들어올 수 있는 최소값과 최대값

이진탐색

				while low <= high:
            mid1 = (low + high) // 2
            mid2 = half - mid1

            # mid가 자르고 난뒤 오른쪽 array의 시작점

            left1 = nums1[mid1 - 1] if mid1 > 0 else MIN
            right1 = nums1[mid1] if mid1 < len(nums1) else MAX

            left2 = nums2[mid2 - 1] if mid2 > 0 else MIN
            right2 = nums2[mid2] if mid2 < len(nums2) else MAX

nums1을 둘로 잘랐을 때 두 번째 array의 시작점 = mid1

nums2를 둘로 잘랐을 때 두 번째 array의 시작점 = mid2

left1 = 잘려진 nums1의 왼쪽 array의 max값

right1 = 잘려진 nums1의 오른쪽 array의 min값

left2 = 잘려진 nums2의 왼쪽 array의 max값

right2 = 잘려진 nums2의 오른쪽 array의 min값

 

 

판단

            if left1 > right2:
                high = mid1 - 1
            elif right1 < left2:
                low = mid1 + 1
            else:
                left_max = max(left1, left2)
                right_min = min(right1, right2)
                return right_min if total_len % 2 != 0 else (left_max + right_min) / 2

left1이 right2보다 작아야되는데 커버리면 좀더 작게 만들기 위해 high를 낮춤

right1이 left2보다 작은 경우는 그 반대로.

left1보다 right2가 크고 left2보다 right1이 크다면 잘 잘려진 것이므로 경계값을 이용해 median을 구한다.

분석

시간복잡도는 O(log(min(n, m)) (n = len(nums1), m = len(nums2))

공간복잡도는 O(1)