每天学习一点儿算法--二分查找
算法是什么?
算法就是完成一组特定任务的方法。
比如将大象放进冰箱需要三步:
- 打开冰箱
- 将大象放进冰箱
- 关闭冰箱
这就是一种算法。
如果用计算机语言来叙述,就是任何实现某种功能的代码片段都可以称之为算法。
一个程序员应该掌握大概50种基本算法,但目前我们属于初级阶段,先掌握一些简单有趣的算法,为日后进一步的算法学习打下基础。
二分查找
比如我要在字典(这里是真实的字典,不是Python的dict类型)中查找以O为拼音首字母的汉字,我会从字典的中间附近开始翻阅,因为我知道字母O在26个字母的中间附近,这会提高我的查找效率。在这里,我就运用了二分查找的思想。
二分查找是一种算法,它的思想就是:
1.在一个有序的元素列表中,每次将查找的元素与元素列表的中间元素作比较
2.如果等于中间元素,则查找完毕
3.若比中间元素大,则在大于中间元素的一半中重复步骤1
4.若比中间元素小,则在小于中间元素的一半中重复步骤1
5.重复步骤2或3或4,直至查找完毕
注释:仅当列表是有序的时候,二分查找才管用。
一般而言,对于包含n个元素的有序列表,用二分查找最多需要㏒₂n步,而简单查找最多需要n步。
我们一般使用大O表示法来表征算法的运行时间,其中㏒一般指的是㏒₂
现在我们来看看如何使用Python来编写二分查找的代码,这里以一个简单的数组示例:
def binary_search(list, item): """定义一个二分查找的函数"""
# low 和 high用于跟踪要在其中查找的列表部分
low = 0
high = len(list) - 1
while low <= high: global n
mid = int((low + high)/2) # 检查中间的元素
guess = list[mid]
n = n + 1 # 时间复杂度计数
if guess == item: # 找到了元素
return mid elif guess > item: # 猜的数大了
high = mid - 1
else: # 猜的数小了
low = mid + 1
return None # 没有查询到指定的元素
n = 0
my_list = [1, 3, 5, 7, 9, 10]
result = binary_search(my_list, 7) # 指定元素的位置
print("查询结果:", result)
print("时间复杂度:", n)
执行结果:
查询结果: 3
时间复杂度: 3
需要说明的是,二分查找的查询结果是返回待查找元素在列表中的位置,当然,列表是以0开始标记的。
大O表示法
大O表示法是一种特殊的表示法, 它指出了算法的运行速度有多快。例如,假设列表包含n个元素,简单查找需要检查每个元素,因此需要执行n次操作,使用大O表示法,它的运行时间为O(n),它的单位是秒么?不是,它没有单位。
大O表示法指的并非以秒为单位的速度。它指的是算法运行时间的增速(强调增速)。
比如,为检查长度为n的列表,二分查找需要执行㏒n次操作。使用大O表示法,它的运行时间为O(㏒n)。
我们来对比一下简单查找和二分查找的增速差异(假设检查一个元素需要1毫秒):
这里就能看出两者运行时间的增速有着天壤之别
大O表示法指出的是平均情况下的运行时间
比如,简单查找的运行时间用大O表示法是O(n),但是如果列表的第一个元素就是待查找元素,那么简单查找的运行时间就是O(1)。但这只是特殊情况,一般而言,简单查找的运行时间是O(n)。
一些常见的大O运行时间
下面从快到慢列举了5种常见的大O运行时间
- O(㏒ n), 也叫对数时间,这样的算法包括二分查找
- O(n), 也叫线性时间,这样的算法包括简单查找
- O(n*㏒n), 这样的算法包括快速排序—一种速度较快的排序算法
- O(n²), 这样的算法包括选择排序—一种速度较慢的排序算法
- O(n!), 这样的算法包括旅行商问题的解决方案—一种非常慢的算法
旅行商问题
旅行商问题是计算机科学领域一个十分著名的问题。这个问题是怎样的呢?
有一位旅行商,他要前往5个城市。
画的图好丑
同时要确保旅程最短。为此,可以考虑去往这些城市的各种可能顺序。高中学过的排列组合的知识告诉我们,5个城市有120种不同组合。因此,在涉及5个城市时,需要执行120次操作。涉及6个城市时,需要执行720次操作。
推而广之,涉及n个城市时,需要执行n!(n的阶乘)次操作才能计算出结果。因此运行时间为O(n!),即阶乘时间。在涉及的城市较多时,这个算法根本无法在有效的时间内计算出结果。面对这种问题,我们只能去找出近似答案~
小结
- 二分查找的速度比简单查找快得多
- 算法的运行时间不是以秒为单位的
- 算法的运行时间是从其增速的角度衡量的
- 算法的运行时间使用大O表示法表示
每天学习一点点,每天进步一点点。
- (Head First 设计模式)学习笔记(3) --装饰者模式(StarBuzz咖啡店实例)
- 我的Js代码-按钮按下时判断是否选择了最后一行,给出提示
- (Head First 设计模式)学习笔记(2) --观察者模式(气象站实例)
- Spring Boot使用HandlerInterceptorAdapter和WebMvcConfigurerAdapter实现原始的登录验证
- 一条视频获C+融资 两个域名神助攻
- ExtJs与WCF交互:生成树
- JavaScript大略
- 加点的心得
- Markdown
- 介绍linux下利用编译bash设置root账号共用的权限审计设置
- 分享一例脚本发版和tomcat重启脚本
- 2018年小程序的红利趋势预测,或许你将成为下个富翁
- 分布式监控系统Zabbix-3.0.3-完整安装记录(5)-邮件报警部署
- label自定义的惨痛教训
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- php 与 nginx 的处理方式及nginx与php-fpm通信的两种方式
- Thinkphp 5.0实现微信企业付款到零钱
- 实现php删除链表中重复的结点
- YII分模块加载路由的实现办法
- ThinkPHP5.0框架实现切换数据库的方法分析
- php微信公众号开发之微信企业付款给个人
- tp5框架的增删改查操作示例
- PHP使用函数用法详解
- 微信企业转账之入口类分装php代码
- 多个Laravel项目怎么共用migrations详解
- layui数据表格自定义每页条数limit设置
- Laravel 集成微信用户登录和绑定的实现
- PHP实现微信对账单处理
- Laravel5.1框架路由分组用法实例分析
- PHP的HTTP客户端Guzzle简单使用方法分析