Commit a7db8908 by 彭凯

Upload New File

parent 00ecb840
#### 二分法
#### 二分法
二分法,是一种常用的查找算法,主要针对于有序数组,用二分法的最大好处是每查找一次就可以扔掉一半的数据,从而大大减少需要查找的总数据,进而提高查找速度。
> 对于区间[a,b]上连续不断且f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法叫二分法。
>
> [百度百科词条]([https://baike.baidu.com/item/%E4%BA%8C%E5%88%86%E6%B3%95/1364267?fr=aladdin](https://baike.baidu.com/item/二分法/1364267?fr=aladdin))
下面是二分法的一个简单的实现
```python
# 给定一个数组,元素按照正序排列,范围为1-100
# 查找43是否在该数组中,如果是,返回它的索引;如果不是,返回-1
class Solution():
# 暴力法,遍历整个数组
def search(self, nums, target):
n = len(nums)
if n == 0:
return -1
for i in range(n):
if nums[i] == target:
return i
return -1
# 二分法
def dichotomy(self, nums, target):
n = len(nums)
if n == 0:
return -1
left, right = 0, n - 1
while left <= right:
medium = (left + right) // 2
if nums[medium] == target:
return medium
elif nums[medium] < target:
left = medium + 1
elif target < nums[medium]:
right = medium - 1
return -1
s = Solution()
print(s.search([i for i in range(1, 101)], 43))
print(s.dichotomy([i for i in range(1, 101)], 43))
```
#### 二分法的刨析与变种
二分法看似简单,很容易被理解,而且使用也较为平凡,但是你是否想过为什么它只对有序数组有效呢?
其实,二分的原理就在于二分,即它每次都能找到一个有序数组最中间的那个数字,这个数字具有以下两个特征
- 比最左边的数字小
- 比最右边的数字大
明白了这一点之后,就可以阅读二分法的进阶方法了,现有如下的一个题目
> 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
>
> ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
>
> 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
>
> 你可以假设数组中不存在重复的元素。
>
对于这个题目,看到了**按照升序排序**应该很自然的联想到二分法查找。但是这个题目有一个不同之处在于它并不是全排序的二分排序,那么可以很自然的想到:先将它转换成为一个全排序的有序数组,再进行查找。除此之外,利用暴力法也可以解决本题,只是耗时比较大,现将两种方法展示如下
```python
# 暴力法
class Solution:
def search(self, nums, target):
for num in nums:
if num == target:
return nums.index(target)
return -1
```
采用先将数组转换为有序数组之后,再进行二分查找
```python
# 先排序,再进行二分查找
class Solution:
def search(self, nums, target):
tmp = []
# 这里要使用深拷贝,要不然之后求得的索引会出错
# 这与Python自身的语言特点有关
tmp[:] = nums
tmp.sort()
left, right = 0, len(nums) - 1
while left <= right:
medium = (left + right) // 2
if target == tmp[medium]:
return nums.index(target)
elif target < tmp[medium]:
right = medium - 1
else:
left = medium + 1
return -1
```
直接二分法,根据之前讲到的二分法的数字特征,可以直接使用二分法,分析与算法步骤如下
- 数组是有序的,只是从某个位置进行了一次旋转,那么一定存在一段有序的数字,即以数组中间的数为界,左半边或者右半边必定有序,原因是旋转数组相当于交换某两个部分,可以自己多写几组实验一下
- 由于数组一定存在某一部分有序,即可能是左半部分有序,也可能是有半部分有序。此时,有序的这半部分就可以构造二分查找
- 当左半部分有序时,那么数组中间的数一定有如下特征
- 比最左边的数字大
- 比该有序部分的最后一个数小
根据该特征,可以判断出目标值是否在该范围:如果在,则缩小右边界到数组中间的数;如果不在,则到右半部分进行查找
- 当右半部分有序时,那么数组中间的数一定有如下特征
- 比该有序部分的第一个数字大
- 比最右边的数字小
根据该特征,可以判断出目标值是否在该范围:如果在,则缩小左边界到数组中间的数;如果不在,则到左半部分进行查找
```python
# 变种二分法
class Solution:
def search(self, nums, target):
if not nums:
return -1
left, right = 0, len(nums) - 1
while left <= right:
medium = (left + right) // 2
# 中间值就是要查找的值
if nums[medium] == target:
return medium
# 左半部分有序
elif nums[0] <= nums[medium]:
# 在该范围
if nums[0] <= target < nums[medium]:
right = medium - 1
# 不在该范围
else:
left = medium + 1
# 右半部分有序
else:
# 在该范围
if nums[medium] < target <= nums[len(nums) - 1]:
left = medium + 1
# 不在该范围
else:
right = medium - 1
return -1
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment