深夜来临我还不想入睡,无聊的我想起已经好几天没有更新文章,不知道写点什么,我突然想到我对于算法这方面的知识还是太浅
所以我决定在开一个专栏,就叫做 数据结构和算法的混合双打
二分查找
二分法是一个查找算法,就是在一堆数据集里面找到你想要的那个数据
条件:
数据必须是有序序列
例如: [12,15,86,95,105,334,911,1000] 可以不是连续的,但是必须要是从小到大排列的,不能违背这个原则,这同样也是它的一个小小小小小 缺点
核心思想:
掐头结尾取中间
优点:
我称它为查找界的鼻祖,查找速度快,效率极高
好的概念就说这么多,接下来代码 showtime!!
一、最最最low 的写法
# 我们先来搞一个数组吧
list01 = [20, 45, 56, 79, 90, 110, 221, 285, 356, 618, 985, 1004]
# 接收要查找的数字
num = int(input("请输入你要查找的数字:"))
## 假如我们不用算法的情况下,是不是就像下面这样写
for item in list01:
if num == item:
print("啊我找到了")
break
这样就是我把列表的每一项拿出来,一个一个对比,如果相等就找到了,没找到就一直找,什么时候找到算什么时候
这种查找为什么low呢,因为,你数据量少的时候,你可以这样写,那假如要是从几亿几十亿数据里面找,那么你这样就要找几亿几十亿次,就显得效率特别低
二、高级的算法:二分法查找
# 二分法查找核心思想:掐头结尾取中间
# 位置 left mid right
# 索引 0 1 2 3 4 5 6 7 8 9 10 11
# list01 = [20, 45, 56, 79, 90, 110, 221, 285, 356, 618, 985, 1004]
# 找到 头 和 尾
left = 0 # 取得列表的头部,这个最开始是 20 ,那么它的索引就是 0
right = len(list01) -1 # 尾部是1004, 那么索引就是 11
# 取中间
mid = (left + right) // 2 # 左边加右边等于索引0+11,然后整除于2
注意: 必须整除,因为你要是 / 2 的话,就容易出现小数点 .5 这样的,在我们 索引中是没有点几 这个概念的
接下来我们为了演示输入一个存在的数字90,然后和中间的mid这个数做比较,那么我们在现在中间的是110
90比110小,且列表是从小到大排列的,所以这个数字绝对不可能出现在列表的右边
一定会出现在列表的左边,那么我们就应该去移动右边界到mid的前一位
也就是:
# 位置 left right mid
# 索引 0 1 2 3 4 5 6 7 8 9 10 11
# list01 = [20, 45, 56, 79, 90, 110, 221, 285, 356, 618, 985, 1004]
那么反之就是比中间数字大,就是right不变,left向右移动呗
根据上面的分析所以接下来我们就写算法过程了:
if num > list01[mid]:
left = mid + 1
elif num < list01[mid]:
right = mid - 1
else:
print("我找到啦,这个数字在%s位置" % mid)
这样我们的大致的算法主体逻辑就写完了,但是这样写只是写了一步,我只是计算了第一次
也就是第一次移动,我们不能保证它移动一次就找到了
所以接下来我们应该是类似于这样写,加一个循环,然后让它一直找,找到直接break
while 1:
# 要一直定义中间值
mid = (left + right) // 2
if num > list01[mid]:
left = mid + 1
elif num < list01[mid]:
right = mid - 1
else:
print("我找到啦,这个数字在%s位置" % mid)
break
那么我们千算万算还是欠点考虑,就是那你输入的这个数字不存在怎么办,我输入了一个60
它就会一直找找找,不断循环,因为left是要一直加1,而right是要一直减1
总有一次 left会到right左边,right会到left右边,这样就代表左右都找过了,我们在找下去就没有意义了
# 位置 right left
# 索引 0 1 2 3 4 5 6 7 8 9 10 11
# list01 = [20, 45, 56, 79, 90, 110, 221, 285, 356, 618, 985, 1004]
所以我们代码最终应该是这样的:
list01 = [20, 45, 56, 79, 90, 110, 221, 285, 356, 618, 985, 1004]
num = int(input("请输入你要查找的数字:"))
left = 0
right = len(list01) - 1
while left <= right: # 当左边比右边小的时候才有意义,反之不存在
# 要一直定义中间值
mid = (left + right) // 2
if num > list01[mid]:
left = mid + 1
elif num < list01[mid]:
right = mid - 1
else:
print("我找到啦,这个数字在%s位置" % mid)
break
else:
print("抱歉,你找数字不存在")
我上面说它的效率是非常高的,我们举个例子:
# 一亿数据,用二分查找
1、100000000
2、50000000
3、25000000
4、.....
最终我们可以计算出查找一亿数据最多只要二分查找27次,就可以全部找一遍
也就是2的几次方,我们去转换成log以2为底的对数进行计算