LeetCode발행일 2025. 3. 27.원본 https://blog.naver.com/jword_/223812259264 ↗

이분 탐색 응용 - 중복 타겟 찾기

이분 탐색 응용 - 중복 타겟 찾기 — #LeetCode #개발자의도구들 #이분탐색 #이분탐색중복 only 파이썬 목표 참고 : 여기 전략 생각하기 1차 ...

#LeetCode#Naver Blog

#LeetCode #개발자의도구들 #이분탐색 #이분탐색중복

​

​

  • only 파이썬
  • 목표 참고 : 여기

전략 생각하기

LeetCode(medium 110. Find First and Last Position of Element in Sorted Array) 46.3%

text 코드 예제
                                    🗝️ 선형 탐색보다 빠른 탐색을 요구한다. O(log n)

✅ 이미 정렬되어 있다.
✅ 이분탐색을 사용하면 풀릴 것이다.

⚠️ n (0 .. 100000), find fault return [-1, -1], one found = [idx, idx]

1차 시도

python 코드 예제
                                    class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        result = []

        start = 0
        end = len(nums) - 1

        while start <= end:
            mid = (start + end) / 2
            print("mid: {}".format(mid))

            if nums[mid] > target:
                end = mid - 1
            elif nums[mid] < target:
                start = mid + 1
            else:
                result.append(mid)

        return result if len(result) != 0 else [-1, -1]
  • 🤔 찾은후에는 어떻게 start, end를 업데이트 해야하는가?
text 코드 예제
                                    [... target ...]
      == target

➡️ case1
[  ...  target target  ... ]
          v == found! -> start = mid + 1

➡️ case2
[  ...  target target  ... ]
                v == found -> end -= 1

➡️ case 3
[  ...  target target  target ... ]
                v == found -> start? end ?

min, max 사용하기?

python 코드 예제
                                    🤔 어차피 묻는 값이 target의 idx의 최대, 최소값이므로,
   이를 기억해두었다가 어디로 이동할지 결정할 수 있을 것 같다.

min = len(nums)
max = 0
➡️ [... target ...]
          mid = i 라고 가정
>>> 발견 위치에 대한 min, max값 업데이트
>>> min = min(min, mid) = i
>>> max = max(max, mid) = i

➡️ [... target ... (checked target) ... target ]
          i - k                          i + j
>>> i - k와 i + j에도 target이 존재한다.
  • 근데 i - k 부터 i + j까지 모두 target이긴하다.
  • 복잡하다.

​

분할정복 사용하기

2차시도

javascript 코드 예제
                                    🤔 어차피 mid에서 target이 발견되었으면, mid에서부터 양쪽으로 탐색해도 logn의 시간복잡도를 가진다.

✅ mid를 찾는다 => 이분 탐색 기법

✅ target을 찾았다면 target위치(=mid)부터 양쪽으로 선형 탐색을 시작한다.

✅ target이 아닌 값이 나오는 구간까지 각각 찾아주면된다.
>>> logn + logn으로 logn이된다.
python 코드 예제
                                    class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        result = []

        start = 0
        end = len(nums) - 1

        while start <= end:
            mid = (start + end) / 2
            print("mid: {}".format(mid))

            if nums[mid] > target:
                end = mid - 1
            elif nums[mid] < target:
                start = mid + 1
            else:
                # add min
                for i in range(start, mid + 1):
                    if nums[i] == target:
                        result.append(i)
                        break

                # add max
                added = False
                for j in range(mid, end + 1):

                    if nums[i] != target:
                        result.append(i - 1)
                        break

                break

        return result if len(result) != 0 else [-1, -1]
  • 최소 위치는 잘 찾는데 최대 위치를 못찾는다.
  • target이 아닌 값이 하나도 없는 경우 아무런 값도 넣지 못하기 때문이다.
  • \[1, 2\]에서 2가 target이고 1이 mid인 상황
  • mid값 넣는 것을 고려해줘야한다.

​

text 코드 예제
                                    🖊️ 로직을 변경하자

✅ 아닌 값이 나오는 것을 확인하지 말고, 맞는 값을 모두 넣고 가장 큰 idx만 빼서 result에 넣자
>>> stack을 사용하면 될 것 같다.

최종코드

python 코드 예제
                                    class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        result = []

        start = 0
        end = len(nums) - 1

        while start <= end:
            mid = (start + end) / 2
            print("mid: {}".format(mid))

            if nums[mid] > target:
                end = mid - 1
            elif nums[mid] < target:
                start = mid + 1
            else:
                # add min
                for i in range(start, mid + 1):
                    if nums[i] == target:
                        result.append(i)
                        break

                # add max
                stack = []
                for j in range(mid, end + 1):
                    if nums[j] == target:
                        stack.append(j)
                    else:
                        break
                result.append(stack.pop())
                break

        return result if len(result) != 0 else [-1, -1]
  • t: O(log n) 100% Beats이다.
  • 시간 복잡도에 대한 이해가 없었다면 떠올리지 못했을 것이다.
  • s: O(n) 99.92%
  • stack을 사용하기 때문에 1/2 n 개의 값이 추가될 수 있다.