二分法查找
算法思想
二分查找又称折半查找、 对数搜索 ,是一种在有序数组中查找某一特定元素的搜索算法。
-
假设表中元素是按升序排列
-
选择数组中的元素,将数组分为前后两个区间
- 如果要找的元素比中间的元素大,那么要找的元素在后半区间,更新后半区间为查找的区间
- 如果要找的元素比中间的元素小,那么要找的元素在前半区间,更新前半区间为查找的区间
-
直到最后找到要找的元素,或者整个数组分完
优缺点
优点:比较次数少,查找速度快,平均性能好;
缺点:要求待查表为有序表,且插入删除困难。折半查找方法适用于不经常变动而查找频繁的有序列表。
<\cente>
二分法查找实现
非递归实现
def binary_search(alist, item):
left = 0
right = len(alist) - 1
while left <= right:
mid = left - (left - right) // 2 # 地板除 取中间的值的索引
if alist[mid] == item:
return True
elif item < alist[mid]:
right = mid - 1
else:
left = mid + 1
return False
testlist = [2, 2]
print(binary_search(testlist, 2))
print(binary_search(testlist, 13))
递归实现
def binary_search(alist, item):
if len(alist) == 0:
return False
else:
mid = len(alist)//2
if alist[mid]==item:
return True
else:
if item<alist[mid]:
return binary_search(alist[:mid],item)
else:
return binary_search(alist[mid+1:],item)
testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]
print(binary_search(testlist, 3))
print(binary_search(testlist, 13))
时间复杂度
- 最优时间复杂度: O ( 1 ) O(1) O(1)
- 最坏时间复杂度: O ( l o g n ) O(logn) O(logn)
Leetcode
153. 寻找旋转排序数组中的最小值
题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例 1:
输入: [3,4,5,1,2]
输出: 1
示例 2:
输入: [4,5,6,7,0,1,2]
输出: 0
题解
class Solution(object):
def findMin(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 特判
if len(nums) == 1:
return nums[0]
# 左右指针
left = 0
right = len(nums) - 1
# 如果最后一个元素大于第一个元素,那么该数组没有旋转,直接返回最小值
if nums[right] > nums[0]:
return nums[0]
while right >= left:
# 找中间元素
mid = (left + right)//2
# 如果中间的元素大于下一个元素,那么下一个元素就是旋转之后的部分 最小的元素就是mid+1
if nums[mid] > nums[mid + 1]:
return nums[mid +1]
# 如果中间的元素小于前一个元素 那么最小的元素就是中间的元素
if nums[mid - 1] > nums[mid]:
return nums[mid]
# 如果中间的元素大于数组的第一个元素,那么最小的元素就在0-mid之间,改变left
if nums[mid] > nums[0]:
left = mid +1
# 如果中间的元素小于数组的第一个元素,那么最小的元素就在mid-right之间,改变left
else:
right = mid + 1
350. 两个数组的交集 II
题目描述
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
题解
class Solution(object):
def intersect(self, nums1, nums2):
"""
快慢指针
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
# 对两个数组进行排序
nums1.sort()
nums2.sort()
# 利用两个指针来表示nums1与nums2的索引
p_nums1,p_nums2 = 0 , 0
# 保存交集
result = []
size1,size2 = len(nums1),len(nums2)
# 循环
while p_nums1 < size1 and p_nums2 < size2:
# p_nums1比较小 移动p_nums1指针
if nums1[p_nums1] < nums2[p_nums2]:
p_nums1 += 1
#如果相等 添加到result 并且移动p_nums1、p_nums2
elif nums1[p_nums1] == nums2[p_nums2]:
result.append(nums1[p_nums1])
p_nums1 += 1
p_nums2 += 1
# p_nums1比较大 移动p_nums2指针
else:
p_nums2 += 1
return result
56. 合并区间
题目描述
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
题解
class Solution(object):
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype: List[List[int]]
"""
# 对区间进行排序
intervals = sorted(intervals)
n = len(intervals)
i = 0
res = []
while i < n:
left = intervals[i][0]
right = intervals[i][1]
# 如果一个区间的最小值小于前一个区间的最大值,两个区间合并
while i < n - 1 and intervals[i+1][0] <= right:
i += 1
# 更新区间的最大值
right = max(intervals[i][1],right)
# 添加到res
res.append([left,right])
i+= 1
return res
69. x 的平方根
题目描述
实现 int sqrt(int x)
函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
题解
class Solution:
def mySqrt(self, x ) :
if x == 0:
return 0
left = 1
right = x // 2
while left < right:
# 注意:这里一定取右中位数,如果取左中位数,代码可能会进入死循环
mid = left + (right - left + 1) // 2
# 判断square与x的大小,如果小的话 直接取mid(右中位数)
square = mid * mid
if square > x:
right = mid - 1
else:
left = mid
# 因为一定存在,因此无需后处理
return left