二分搜索算法题

当给定一个数组,要想到一些点:

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

 

posted @ 2019-11-25 21:58  oldby  阅读(1225)  评论(0编辑  收藏  举报