## 【LeetCode刷題】【Binary Search 演算法學習筆記】34. Find First and Last Position of Element in Sorted Array * 學習時間:2025/01/28 (Tue.) ~ 01/30 (Thu.) * **題目連結(美服):**[34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/description/) * **題目連結(國服):**[34. 在排序数组中查找元素的第一个和最后一个位置](https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/) * **灵茶山艾府二分搜尋法影片解說:** [二分查找 红蓝染色法](https://www.bilibili.com/video/BV1AP41137w7/?vd_source=e0b39ef55fea35a347aa1da490ac5b01) ### A. 解題技術點 --- 1. [陣列(Array)](https://hackmd.io/@oliver-jhih-cs/ryO4s7MdJe) 2. [二分搜尋法(Binary Search)](https://hackmd.io/@oliver-jhih-cs/rJI1aXz_yg) 3. [時間複雜度(Time Complexity)](https://medium.com/appworks-school/初學者學演算法-從時間複雜度認識常見演算法-一-b46fece65ba5) 4. [二分法的區間](https://medium.com/@CecileLiu/二分法的區間-網路上最詳盡的解說-5adf3be3bc5e) ⭐️ ### B. 題目 --- > Given an array of integers ==nums== sorted in non-decreasing order, find the starting and ending position of a given ==target== value. If target is not found in the array, return ==[-1, -1]==. > You must write an algorithm with **O(log n)** runtime complexity. ### C. 題意(中文) --- >给你一个按照非递减顺序排列的整数数组 ==nums==,和一个目标值 ==target==。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 ==[-1, -1]==。 >你必须设计并实现时间复杂度为 **O(log n)** 的算法解决此问题。 ### D. 參考程式碼 :::spoiler Solution (閉區間) ``` python3 # reference: [灵茶山艾府](https://www.bilibili.com/video/BV1AP41137w7/?vd_source=e0b39ef55fea35a347aa1da490ac5b01) # 閉區間寫法 [left, right] # start: >= target # end: <= target from typing import List class Solution: def lower_bound(self, nums: List[int], target: int) -> int: left, right = 0, len(nums) - 1 # right 為 len(nums) - 1 # 循環終止條件; 區間不為空 # 修改為 while left <= right while left <= right: mid = left + (right - left) // 2 if nums[mid] < target: left = mid + 1 else: right = mid - 1 # 右邊界縮小為 mid - 1 return left # ⭐️left 是第一個 >= target 的索引 def searchRange(self, nums: List[int], target: int) -> List[int]: start = self.lower_bound(nums, target) if start == len(nums) or nums[start] != target: return [-1, -1] # >= x 即是 left = start # > x 即是 (>= x) + 1 # < x 即是 (>= x) - 1 # <= x 即是 ( > x) - 1 = end end = self.lower_bound(nums, target + 1) - 1 return [start, end] ``` ::: :::spoiler Solution (左閉右開區間) 詳見 ➡︎ [灵茶山艾府](https://www.bilibili.com/video/BV1AP41137w7/?vd_source=e0b39ef55fea35a347aa1da490ac5b01) ::: :::spoiler Solution (開區間) ```python from typing import List # 開區間寫法 (left, right) # left 指向 -1 # Right 指向 n (len(nums)) class Solution: # lower_bound return 最小滿足 nums[i] >= target的下標 " i " # 處理特別條件 ☞ 當陣列為空,或者所有數皆小於 target,則返回len(nums) # 要求 nums 非遞減,即 nums[i] < nums[i+1] def lower_bound(self, nums: List[int], target: int) -> int: left, right = -1, len(nums) # 左閉右開區間 [left, right) # 循環終止條件; 區間不為空 # 右開修改為 while left < right while left + 1 < right: mid = left + (right - left) // 2 # 循環不變量 # nums[left] < target # nums[right] >= target if nums[mid] < target: # 左邊界縮小為 (mid, right) left = mid else: right = mid # 右邊界縮小為 (left, mid) # ⭐️循環結束後,left + 1 = right # 此時,nums[left] < target; nums[right] >= target # 所以 right 第一個元素 即是 >= target 的元素下標 # start 即是 nums[i] >= target return right def searchRange(self, nums: List[int], target: int) -> List[int]: start = self.lower_bound(nums, target) if start == len(nums) or nums[start] != target: return [-1, -1] # nums 中不存在 target # 如果 start 存在,則 end 必存在 # end: 即是 nums[i] <= target # <= : 即是比 target 大的最小數,的前一項 # <= : 即是 (nums[i] > target) - 1 end = self.lower_bound(nums, target + 1) - 1 return [start, end] ``` ::: --- :::spoiler Thinking(思路) 有一個遞增排序的數組 nums,我們要找出某個數 target 出現的第一個和最後一個位置。如果 target 不在 nums 裡,就回傳 [-1, -1]。 ==解法== A. 直觀解法 ☞ **線性掃描** 1. 從頭開始找 target 出現的第一個位置 2. 從尾端來找 target 出現的最後一個位置 3. 時間複雜度是 O(n),不符合 O(log n) 的要求。 B. **二分搜尋法 (Binary Search)** 高效解法—用二分搜尋法找上下界 我們會做 兩次二分搜尋: * 第一次找左邊界 (first position) * 第二次找右邊界 (last position) --- **🔹 找左邊界(First Position)** * 二分搜尋時,如果 nums[mid] == target,不要馬上返 回,而是繼續往左找。 * 因此如果 nums[mid] == target,我們縮小範圍 right = mid,讓 mid 往左移動。 * 最後當 left == right 時,這個 left 就是 target 最早出現的位置。 **🔹 找右邊界(Last Position)** * 這次當 nums[mid] == target 時,我們往右邊找,所以 left = mid + 1。 * 最後 right - 1 會是 target 最後出現的位置。 ::: :::spoiler Note(筆記) 1. **Binary Search Basic** * It works only on sorted (已排序的) arrays. * The idea (方法) is divide (分割) the search range in half reapetedly (反覆). 2. **Efficiency (效率)** * Time complexity: **O(log n)** because the ==lower bound== is halved (一半) at each step. * Space complexity: **O(1)** Since no extra (額外的) space is used.