算法之二分搜索

经典二分搜索法:

先找到目标值的第一个位置,如果没找到则返回-1

最后一个位置以及其他位置怎么处理?

找第一个位置:将==条件成立改为right = mid

找最后一个位置:将==条件成立改为left = mid

当遇到一个数组的时候→

     先想  这个数组是不是排好序的

      再看  这个数组里面有没有重复的数字,如果有,是找第几个

      再看 这个数组里面有没有负数

一、找目标值的第一个位置/最后一个位置/一个区间/第一个大于等于目标值/最后一个小于等于目标值

Find 1st position of target, return -1 if not found

How about last position, any position?

 1 def binarysearch(nums, item):
 2     if len(nums) == 0:
 3         return -1
 4     left, right = 0, len(nums) - 1
 5     while left + 1 < right:
 6         # 跳出循环体: LR相邻的时候, LR相同的时候
 7         mid = left + (right - left) // 2
 8         if nums[mid] == item:
 9             right = mid
10         elif nums[mid] < item:
11             left = mid
12         else:
13             right = mid
14     if nums[left] == item:
15         return left
16     if nums[right] == item:
17         return right
18     return -1

进阶:找一个区间

 1 def search_range(alist, target):
 2     if len(alist) == 0:
 3         return (-1, -1)   
 4     lbound, rbound = -1, -1
 5     # search for left bound 
 6     left, right = 0, len(alist) - 1
 7     while left + 1 < right: 
 8         mid = left + (right - left) // 2
 9         if alist[mid] == target:
10             right = mid
11         elif (alist[mid] < target):
12             left = mid
13         else:
14             right = mid          
15     if alist[left] == target:
16         lbound = left
17     elif alist[right] == target:
18         lbound = right
19     else:
20         return (-1, -1)
21     # search for right bound 
22     left, right = 0, len(alist) - 1        
23     while left + 1 < right: 
24         mid = left + (right - left) // 2
25         if alist[mid] == target:
26             left = mid
27         elif (alist[mid] < target):
28             left = mid
29         else:
30             right = mid     
31     if alist[right] == target:
32         rbound = right
33     elif alist[left] == target:
34         rbound = left
35     else:
36         return (-1, -1)           
37     return (lbound, rbound)

Java版本

 1 package my0511;
 2 
 3 public class BinarySearch1 {
 4     public static void main(String[] args) {
 5 
 6     }
 7     //查找第一个等于目标值的元素
 8     public int binarysearch_first(int[] a, int n, int value){
 9         int low = 0;
10         int high = n - 1;
11         while (low <= high){
12             int mid = low + ((high - low) >> 1);
13             if (a[mid] > value){
14                 high = mid - 1;
15             } else if (a[mid] < value){
16                 low = mid + 1;
17             } else {
18                 if ((mid == 0) || (a[mid - 1] != value)) return mid;
19                 else high = mid - 1;
20             }
21         }
22         return -1;
23     }
24     //查找最后一个等于目标值的元素
25     public int binarysearch_last(int[] a, int n, int value){
26         int low = 0;
27         int high = n - 1;
28         while (low <= high){
29             int mid = low + ((high - low) >> 1);
30             if (a[mid] > value){
31                 high = mid - 1;
32             } else if (a[mid] < value){
33                 low = mid + 1;
34             } else {
35                 if ((mid == n - 1) || (a[mid - 1] != value)) return mid;
36                 else low = mid + 1;
37             }
38         }
39         return -1;
40     }
41     //查找第一个大于等于目标值的元素
42     public int binarysearch_firstbigoreq(int[] a, int n, int value){
43         int low = 0;
44         int high = n - 1;
45         while (low <= high){
46             int mid = low + (high - low) >> 1;
47             if (a[mid] >= value){
48                 if ((mid == 0) || (a[mid - 1] < value)) return mid;
49                 else high = mid - 1;
50             } else {
51                 low = mid + 1;
52             }
53         }
54         return -1;
55     }
56     //查找最后一个小于等于目标值的元素
57     public int binarysearch_lastsmalloreq(int[] a, int n, int value){
58         int low = 0;
59         int high = n - 1;
60         while (low <= high){
61             int mid = low + (high - low) >> 1;
62             if (a[mid] > value){
63                 high = mid - 1;
64             } else {
65                 if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
66                 else low = mid + 1;
67             }
68         }
69         return -1;
70     }
71 }

二、在旋转有序数列中查找最小值****************

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. Find the minimum element.

思路一:min  O(n)

思路二:排序  O(nlogn)

思路三:二分,用left与mid相比,看是否左半边是否是有序的

              用left与mid相比,

              left、mid、right,找拐点

              一半有拐点,一段排好序

logn复杂度的解法只有二分法

 1 def search(nums):
 2     if len(nums) == 0:
 3         return -1
 4     left, right = 0, len(nums) - 1
 5     while left + 1 < right:
 6         if nums[left] < nums[right]:
 7             return nums[left]
 8         mid = left + (right - left) // 2
 9         if nums[mid] >= nums[left]:
10             left = mid + 1
11         else:
12             right = mid
13     return nums[left] if nums[left] < nums[right] else nums[right]

进阶:在旋转数组中查找

思路:在有序部分查找

 1 def search(alist, target):
 2     if len(alist) == 0:
 3         return -1    
 4     left, right = 0, len(alist) - 1
 5     while left + 1 < right: 
 6         mid = left + (right - left) // 2
 7         if alist[mid] == target:
 8             return mid  
 9         if (alist[left] < alist[mid]):
10             if alist[left] <= target and target <= alist[mid]:
11                 right = mid
12             else:
13                 left = mid
14         else:
15             if alist[mid] <= target and target <= alist[right]:
16                 left = mid
17             else: 
18                 right = mid                           
19     if alist[left] == target:
20         return left
21     if alist[right] == target:
22         return right      
23     return -1

进阶:在有重复元素的旋转排序数组中查找

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

输入: nums = [2,5,6,0,0,1,2], target = 0

输出: true

重复元素,影响了判断拐点和有序部分,故只需要去除直到能找到拐点区和有序区即可。

 1 class Solution:
 2     def search(self, nums: List[int], target: int) -> bool:
 3         if len(nums) == 0:
 4             return False
 5         left = 0
 6         right = len(nums) - 1
 7         while left + 1 < right:
 8             mid = (left + right) // 2
 9             if nums[mid] == target:
10                 return True
11             if nums[left] == nums[mid]:
12                 left += 1
13                 continue
14             if nums[mid] >= nums[left]:
15                 if nums[mid] >= target and target >= nums[left]:
16                     right = mid
17                 else:
18                     left = mid
19             else:
20                 if nums[mid] <= target and target <= nums[right]:
21                     left = mid
22                 else:
23                     right = mid
24         if nums[left] == target:
25             return True
26         if nums[right] == target:
27             return True
28         return False

进阶:在有重复元素的旋转排序数组中查找最小值*************

1 class Solution:
2     def findMin(self, nums: List[int]) -> int:
3         left, right = 0, len(nums) - 1
4         while left < right:
5             mid = (left + right) // 2
6             if nums[mid] > nums[right]: left = mid + 1
7             elif nums[mid] < nums[right]: right = mid
8             else: right = right - 1 # key
9         return nums[left]
 1 class Solution:
 2     def findMin(self, nums: List[int]) -> int:
 3         left, right = 0, len(nums) - 1
 4         while left + 1 < right:
 5             mid = (left + right) // 2
 6             if nums[mid] == nums[right]:
 7                 right -= 1
 8                 continue
 9             if nums[mid] < nums[right]:
10                 right = mid
11             elif nums[mid] > nums[right]:
12                 left = mid
13         return min(nums[left], nums[right])

问题:通过但感觉有问题,多次旋转?

搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。

 1 class Solution:
 2     def search(self, arr: List[int], target: int) -> int:
 3         if len(arr) == 0:
 4             return -1
 5         left = 0
 6         right = len(arr) - 1
 7         while left + 1 < right:
 8             mid = (left + right) // 2
 9             if arr[left] == target:
10                 return left
11             if arr[mid] == target:
12                 right = mid
13             elif arr[left] == arr[mid]:
14                 left += 1
15                 continue
16             elif arr[mid] < arr[left]:
17                 if arr[mid] < target and target <= arr[right]:
18                     left = mid
19                 else:
20                     right = mid
21             elif arr[mid] > arr[left]:
22                 if arr[mid] > target and arr[left] <= target:
23                     right = mid
24                 else:
25                     left = mid
26         if arr[left] == target:
27             return left
28         if arr[right] == target:
29             return right
30         return -1

三、搜索插入位置

bisect:找到返回索引;没找到,返回应该插入的位置

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order. You may assume no duplicates in the array

 1 #本质是找到第一个大于等于目标值的索引
 2 def bisect_prac(nums, target):
 3     if len(nums) == 0:
 4         return 0
 5     left = 0
 6     right = len(nums) - 1
 7     while left + 1 < right:
 8         mid = (left + right) // 2
 9         if nums[mid] == target:
10             return mid
11         elif nums[mid] < target:
12             left = mid
13         else:
14             right = mid
15     if nums[left] > target:
16         return left
17     if nums[right] > target:
18         return right
19     return right + 1

四、在用空字符串隔开的字符串的有序列中查找

Given a sorted array of strings which is interspersed with empty strings, write a meth­od to find the location of a given string.

 1 #最差情况下O(n)
 2 #如果往前面找,就要要求前面没有空的字符串,要找到第一个非空字符串#作为left
 3 def search_empty(alist, target):
 4     if len(alist) == 0:
 5         return -1   
 6     left, right = 0, len(alist) - 1 
 7     while left + 1 < right:
 8         while left + 1 < right and alist[right] == "":
 9             right -= 1
10         if alist[right] == "":
11             right -= 1
12         if right < left:
13             return -1        
14         mid = left + (right - left) // 2
15         while alist[mid] == "":
16             mid += 1          
17         if alist[mid] == target:
18             return mid
19         if alist[mid] < target:
20             left = mid + 1
21         else:
22             right = mid - 1            
23     if alist[left] == target:
24         return left
25     if alist[right] == target:
26         return right            
27     return -1   

五、在无限序列中找到某元素的第一个出现位置

数据流

不知道序列长度

1 def search_first(nums, target):
2     left, right = 0, 1
3     while nums[right] < target:
4         left = right
5         right *= 2
6         if right > len(nums):
7             right = len(nums) - 1
8             break
9     return left + search(nums[left:right+1], 1)[0]

六、供暖

bisect是找到第一个大于等于target的元素

 1 import bisect
 2 def radius(houses, heaters):
 3     heaters.sort()
 4     ans = 0
 5     for house in houses:
 6         hi = bisect(heaters, house)
 7         left = heaters[hi-1] if hi - 1 >= 0 else float('-inf')
 8         right = heaters[hi] if hi < len(heaters) else float('inf')
 9         ans = max(ans, min(house - left, right - house))
10     return ans

七、sqrt(x)

Implement int sqrt(int x).

Compute and return the square root of x.

x is guaranteed to be a non-negative integer.

 1 def sqrt_(x):
 2     if x == 0:
 3         return 0
 4     left, right = 0, x
 5     while left + 1 < right:
 6         mid = left + (right - left) // 2
 7         if mid == x // mid:
 8             return mid
 9         if mid < x // mid:
10             left = mid
11         else:
12             right = mid
13     if right <= x:
14         return right
15     return left
 1 def sqrt(x):
 2     if x == 0:
 3         return 0
 4     left, right = 1, x
 5     while left <= right:
 6         mid = left + (right - left) // 2
 7         if (mid == x // mid):
 8             return mid
 9         if (mid < x // mid):
10             left = mid + 1
11         else:
12             right = mid - 1
13     return right

八、矩阵搜索

在一个NxM的矩阵里,每一行都是排好序的,每一列也都是排好序的,请设计一个算法在矩阵中查找一个数。

思路一:去每一行二分法,nlogm  去每一列二分法:mlogn

思路二:从右上角或左下角开始,找一个大于或小于只有一个方向的位置

 1 def matrixsearch(matrix, target):         #O(m+n)
 2     m = len(matrix)
 3     n = len(matrix[0])
 4     row = 0
 5     column = n - 1
 6     while row < m and column >= 0:
 7         if matrix[row][column] == target:
 8             return (row, column)
 9         elif matrix[row][column] > target:
10             column -= 1
11         else:
12             row += 1
13     return -1
14 matrix = [[1, 3, 5, 7, 9],
15           [2, 4, 6, 8, 10],
16           [12, 13, 18, 21, 30],
17           [20, 25, 30, 35, 40]]
18 print(matrixsearch(matrix, 400))

矩阵搜索Ⅱ  Kth Smallest Element in a Sorted Matrix******

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

 1 from bisect import bisect
 2 def kthSmallest(matrix, k):
 3     low, high = matrix[0][0], matrix[-1][-1]
 4     while low < high:
 5         mid = low + (high - low) >> 1
 6         if sum(bisect(row, mid) for row in matrix) < k:
 7             low = mid + 1
 8         else:
 9             high = mid
10     return low 

 

九、找到重复数******

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

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.

思路:找中间数m,找左边有多少数小于m,右边有多少数大于m

 1 def findDuplicate(nums):
 2     low = 0
 3     high = len(nums) - 1
 4     while low < high:
 5         mid = (low + high) // 2
 6         count = 0
 7         for i in nums:
 8             if i <= mid:
 9                 count += 1
10         if count <= mid:
11             low = mid + 1
12         else:
13             high = mid
14     return low

十、地板和鸡蛋

假设有一个100层高的建筑,如果一个鸡蛋从第N层或者高于N层坠落,会摔破。如果鸡蛋从任何低于N层的楼层坠落,则不会破。现在给你两个鸡蛋,请在最少次数下找到N。

思路:一个鸡蛋用于找范围,另一个鸡蛋用于精确定位

n+(n-1)+(n-2)+......+1 > 100  第一次从14层摔,第二次从27层摔

进阶:1000层楼,3个鸡蛋

十一、找两个有序数组的中值

两个数组相同大小

进阶:两个有序数组长度分别为N1和N2,请用logn的方法找到中值

十二、合并区间

给定一个区间的集合,将所有存在交叉范围的区间进行合并。

输入:[[1,3], [2,6], [8,10], [15,18]]  输出:[[1,6], [8,10], [15,18]]

 1 def merge(nums):
 2     nums.sort(key=lambda x: x[0])
 3     res = []
 4     for i in nums:
 5         if not res or res[-1][1] < i[0]:
 6             res.append(i)
 7         else:
 8             res[-1][1] = max(res[-1][1], i[1])
 9     return res
10 print(merge([[1, 3], [2, 6], [8, 10], [15, 18]]))

进阶:插入区间LeetCode57******

给出一个无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

 1 #插入+合并
 2 def merge(nums):
 3     nums.sort(key=lambda x: x[0])
 4     res = []
 5     for i in nums:
 6         if not res or res[-1][1] < i[0]:
 7             res.append(i)
 8         else:
 9             res[-1][1] = max(res[-1][1], i[1])
10     return res
11 
12 def insert_Matrix(nums, target):
13     for i in range(len(nums)):
14         if target[0] < nums[i][0]:
15             nums.insert(i, target)
16             break
17     return merge(nums)
18 
19 print(insert_Matrix([[1, 3], [6, 9]], [2, 5]))
 1 #贪心
 2 def insertGreedy(nums, target):
 3     res = []
 4     i = 0
 5     while i < len(nums):
 6         if nums[i][0] < target[0]:
 7             res.append(nums[i])
 8             i += 1
 9         else:
10             break
11     if not res or res[-1][1] < target[0]:
12         res.append(target)
13     else:
14         res[-1][1] = max(res[-1][1], target[1])
15     while i < len(nums):
16         if res[-1][1] < nums[i][0]:
17             res.append(nums[i])
18         else:
19             res[-1][1] = max(res[-1][1], nums[i][1])
20         i += 1
21     return res
22 
23 print(insertGreedy([[1, 3], [6, 9]], [2, 5]))

 

 

 

posted @ 2020-03-15 17:39  LinBupt  阅读(303)  评论(0编辑  收藏  举报