當給定一個數組,要想到一些點:
1、是否已排序
2、是否有重復數字
3、是否有負數
一:常規二分搜索
def bi_search_iter(alist, item): left, right = 0, len(alist) - 1 while left <= right: mid = (left + right) // 2 if alist[mid] < item: left = mid + 1 elif alist[mid] > item: right = mid - 1 else: # alist[mid] = item return mid return -1
二:二分搜索模板
def binarysearch(alist, item): if len(alist) == 0: return -1 left, right = 0, len(alist) - 1 while left + 1 < right: mid = left + (right - left) // 2 if alist[mid] == item: right = mid elif alist[mid] < item: left = mid elif alist[mid] > item: right = mid if alist[left] == item: return left if alist[right] == item: return right return -1
三、在旋轉數列中尋找最小值
題:假設一個升序排列的數組在某個未知節點處被前后調換,請找到數列中的最小值。
def searchlazy(alist): alist.sort() return alist[0] def searchslow(alist): mmin = alist[0] for i in alist: mmin = min(mmin, i) return mmin def search(alist): if len(alist) == 0: return -1 left, right = 0, len(alist) - 1 while left + 1 < right: if (alist[left] < alist[right]): return alist[left]; mid = left + (right - left) // 2 if (alist[mid] >= alist[left]): left = mid + 1 else: right = mid return alist[left] if alist[left] < alist[right] else alist[right]
四、在旋轉數組中查找某數
def search(alist, target): if len(alist) == 0: return -1 left, right = 0, len(alist) - 1 while left + 1 < right: mid = left + (right - left) // 2 if alist[mid] == target: return mid if (alist[left] < alist[mid]): if alist[left] <= target and target <= alist[mid]: right = mid else: left = mid else: if alist[mid] <= target and target <= alist[right]: left = mid else: right = mid if alist[left] == target: return left if alist[right] == target: return right return -1
五、搜索插入位置
題:給定一有序數組和一目標值,如果在數組中找到此目標值,返回index,否則返回插入位置。注,可假設無重復元素。
def search_insert_position(alist, target): if len(alist) == 0: return 0 left, right = 0, len(alist) - 1 while left + 1 < right: mid = left + (right - left) // 2 if alist[mid] == target: return mid if (alist[mid] < target): left = mid else: right = mid if alist[left] >= target: return left if alist[right] >= target: return right return right + 1
六、搜索一個區間
題:找到一個目標值的開始和結束位置
def search_range(alist, target): if len(alist) == 0: return (-1, -1) lbound, rbound = -1, -1 # search for left bound left, right = 0, len(alist) - 1 while left + 1 < right: mid = left + (right - left) // 2 if alist[mid] == target: right = mid elif (alist[mid] < target): left = mid else: right = mid if alist[left] == target: lbound = left elif alist[right] == target: lbound = right else: return (-1, -1) # search for right bound left, right = 0, len(alist) - 1 while left + 1 < right: mid = left + (right - left) // 2 if alist[mid] == target: left = mid elif (alist[mid] < target): left = mid else: right = mid if alist[right] == target: rbound = right elif alist[left] == target: rbound = left else: return (-1, -1) return (lbound, rbound)
七、在用空字符串隔開的字符串的有序列中查找
題:給定一個有序的字符串序列,這個序列中的字符串用空字符隔開,請寫出找到給定字符串位置的方法
def search_empty(alist, target): if len(alist) == 0: return -1 left, right = 0, len(alist) - 1 while left + 1 < right: while left + 1 < right and alist[right] == "": right -= 1 if alist[right] == "": right -= 1 if right < left: return -1 mid = left + (right - left) // 2 while alist[mid] == "": mid += 1 if alist[mid] == target: return mid if alist[mid] < target: left = mid + 1 else: right = mid - 1 if alist[left] == target: return left if alist[right] == target: return right return -1
八、在無限序列中找到某元素的第一個出現位置
題:數據流,無限長度中尋找某元素第一個出現位置
def search_first(alist): left, right = 0, 1 while alist[right] == 0: left = right right *= 2 if (right > len(alist)): right = len(alist) - 1 break return left + search_range(alist[left:right+1], 1)[0]
九、供暖設備
題:冬季來臨!你的首要任務是設計一款有固定供暖半徑的供暖設備來給所有的房屋供 暖。 • 現在你知道所有房屋以及供暖設備在同一水平線上的位置分布,請找到能給所有房 屋供暖的供暖設備的最小供暖半徑。 • 你的輸入是每個房屋及每個供暖設備的位置,輸出應該是供暖設備的最小半徑。
from bisect import bisect def findRadius(houses, heaters): heaters.sort() ans = 0 for h in houses: hi = bisect(heaters, h) left = heaters[hi-1] if hi - 1 >= 0 else float('-inf') right = heaters[hi] if hi < len(heaters) else float('inf') ans = max(ans, min(h - left, right - h)) return ans
十、sqrt(x)
def sqrt(x): if x == 0: return 0 left, right = 1, x while left <= right: mid = left + (right - left) // 2 if (mid == x // mid): return mid if (mid < x // mid): left = mid + 1 else: right = mid - 1 return right
十一、找到重復數
題:給定一個包含n+1個整數的數組,其中每個元素為1到n閉區間的整數值,請證明至少 存在一個重復數。假設只有一個重復數,請找到這個重復數。
Note:
You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than O(n2).
There is only one duplicate number in the array, but it could be repeated more than once.
def findDuplicate(nums): low = 1 high = len(nums)-1 while low < high: mid = low + (high - low) // 2 count = 0 for i in nums: if i <= mid: count+=1 if count <= mid: low = mid+1 else: high = mid return low
def findDuplicate(nums): # The "tortoise and hare" step. We start at the end of the array and try # to find an intersection point in the cycle. slow = 0 fast = 0 # Keep advancing 'slow' by one step and 'fast' by two steps until they # meet inside the loop. while True: slow = nums[slow] fast = nums[nums[fast]] if slow == fast: break # Start up another pointer from the end of the array and march it forward # until it hits the pointer inside the array. finder = 0 while True: slow = nums[slow] finder = nums[finder] # If the two hit, the intersection index is the duplicate element. if slow == finder: return slow
十二、地板和雞蛋
題:假設有一個100層高的建築,如果一個雞蛋從第N層或者高於N層墜落,會摔破。如果 雞蛋從任何低於N層的樓層墜落,則不會破。現在給你2個雞蛋,請在摔破最少雞蛋 的情況下找到N
十三、找到兩個有序數組的中值
題:給定兩個長度分別為N1和N2的有序數組,請用時間復雜度為 O(log N) 的方法找到所 有元素的中值,這里N=N1+N2。
def findMedianSortedArrays(A, B): l = len(A) + len(B) if l % 2 == 1: return kth(A, B, l // 2) else: return (kth(A, B, l // 2) + kth(A, B, l // 2 - 1)) / 2. def kth(a, b, k): if not a: return b[k] if not b: return a[k] ia, ib = len(a) // 2 , len(b) // 2 ma, mb = a[ia], b[ib] # when k is bigger than the sum of a and b's median indices if ia + ib < k: # if a's median is bigger than b's, b's first half doesn't include k if ma > mb: return kth(a, b[ib + 1:], k - ib - 1) else: return kth(a[ia + 1:], b, k - ia - 1) # when k is smaller than the sum of a and b's indices else: # if a's median is bigger than b's, a's second half doesn't include k if ma > mb: return kth(a[:ia], b, k) else: return kth(a, b[:ib], k)
def find(nums1, s1, e1, nums2, s2, e2, k): if e1 < s1: return nums2[k + s2] if e2 < s2: return nums1[k + s1] if k < 1: return min(nums1[k + s1], nums2[k + s2]) ia, ib = (s1 + e1) // 2 , (s2 + e2) // 2 ma, mb = nums1[ia], nums2[ib] if (ia - s1) + (ib - s2) < k: if ma > mb: return find(nums1, s1, e1, nums2, ib + 1, e2, k - (ib - s2) - 1) else: return find(nums1, ia + 1, e1, nums2, s2, e2, k - (ia - s1) - 1) else: if ma > mb: return find(nums1, s1, ia - 1, nums2, s2, e2, k) else: return find(nums1, s1, e1, nums2, s2, ib - 1, k) def findMedianSortedArrays(nums1, nums2): l = len(nums1) + len(nums2) if l % 2 == 1: return find(nums1, 0, len(nums1) - 1, nums2, 0, len(nums2) - 1, l // 2) else: return (find(nums1, 0, len(nums1) - 1, nums2, 0, len(nums2) - 1, l // 2) + find(nums1, 0, len(nums1) - 1, nums2, 0, len(nums2) - 1, l // 2 - 1)) / 2.0
十四、矩陣搜索
(1)在一個N*M的矩陣中,每一行,每一列都有序,設計一個算法尋找矩陣中的一個數
(2)尋找第K大的數
十五、合並區間
題:給定一個區間的集合,將所有存在交叉范圍的區間進行合並。 • 輸入: [[1,3],[2,6],[8,10],[15,18]] • 輸出: [[1,6],[8,10],[15,18]] • 說明: 因為區間 [1,3] 和 [2,6] 存在交叉范圍, 所以將他們合並為[1,6].
class Interval: def __init__(self, s=0, e=0): self.start = s self.end = e def __str__(self): return "[" + self.start + "," + self.end + "]" def __repr__(self): return "[%s, %s]" % (self.start, self.end)
def merge(intervals): intervals.sort(key=lambda x: x.start) merged = [] for interval in intervals: # if the list of merged intervals is empty or if the current # interval does not overlap with the previous, simply append it. if not merged or merged[-1].end < interval.start: merged.append(interval) else: # otherwise, there is overlap, so we merge the current and previous # intervals. merged[-1].end = max(merged[-1].end, interval.end) return merged
十六、合並區間
題:給定一個沒有交叉范圍的區間集合,在這個集合中插入一個新的區間(如果需要,請進行合並)。 • 你可以認為這些區間已經初始時根據他們的頭元素進行過排序 • 輸入:區間集合=[[1,3],[6,9]], 新區間 = [2,5] • 輸出:[[1,5],[6,9]]
def insert(intervals, newInterval): merged = [] for i in intervals: if newInterval is None or i.end < newInterval.start: merged += i, elif i.start > newInterval.end: merged += newInterval, merged += i, newInterval = None else: newInterval.start = min(newInterval.start, i.start) newInterval.end = max(newInterval.end, i.end) if newInterval is not None: merged += newInterval, return merged
def insert2(intervals, newInterval): left, right = [], [] for i in intervals: if i.end < newInterval.start: left += i, elif i.start > newInterval.end: right += i, else: newInterval.start = min(newInterval.start, i.start) newInterval.end = max(newInterval.end, i.end) return left + [Interval(newInterval.start, newInterval.end)] + right