算法图解笔记
一 算法简介
def binary_search(list, item):
low = 0
high = len(list)—1
while low <= high:
mid = (low + high)/2
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return None
my_list = [1, 3, 5, 7, 9]
print binary_search(my_list, 3) # => 1
print binary_search(my_list, -1) # => None
-
对数:
2^3 = 8,log 8 = 3。 大O表示法讨论运行时间时,常用对数来计算
-
大O表示法
- 让你能够比较操作数,它指出了算法运行时间的增速。
- 大O法还能指出最糟情况下的运行时间
-
一些常见的大O运行时间
- O(log n),也叫对数时间,这样的算法包括二分查找。
- O(n),也叫线性时间,这样的算法包括简单查找。
- O(n * log n),这样的算法包括第4章将介绍的快速排序——一种速度较快的排序算法。
- O(n2),这样的算法包括第2章将介绍的选择排序——一种速度较慢的排序算法。
- O(n!),这样的算法包括接下来将介绍的旅行商问题的解决方案——一种非常慢的算法。
需要注意的是,算法的速度指的并非时间,而是操作数的增加速度。
一般情况下,算法速度O(log n)>O(n)>O(n*log n)>O(n2)>O(n!)
-
旅行商问题
旅行商前往各地,寻找在最短路径,此算法的复杂度为: O(n!)
可他别无选择。这是计算机科学领域待解的问题之
一。对于这个问题,目前还没有找到更快的算法,有些很聪明的人认为这个问题根本就没有更巧
妙的算法。面对这个问题,我们能做的只是去找出近似答案,更详细的信息请参阅第10章。 -
小结
- 二分查找的速度比简单查找快得多。
- O(log n)比O(n)快。需要搜索的元素越多,前者比后者就快得越多。
- 算法运行时间并不以秒为单位。
- 算法运行时间是从其增速的角度度量的。
- 算法运行时间用大O表示法表示。
二 选择排序
1. 数组和链表
-
数组
- 数组在内存中的地址是相连的,需要改变数组时,只能通过申请一个新的内存地址来解决
- 遇到元素超过容量的时候就需要频繁扩容,预留容量开辟大空间数组可以解决问题,但是
- 额外内存用不上时,浪费内存
- 超过上限的情况依旧需要开辟新的地址
-
链表
- 链表中的元素可存储在内存的任何地方。
- 链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起。
-
数组趣闻
排行榜网站使用卑鄙的手段来增加页面浏览量。它们不在一个页面中
显示整个排行榜,而将排行榜的每项内容都放在一个页面中,并让你单击
Next来查看下一项内容。例如,显示十大电视反派时,不在一个页面中显
示整个排行榜,而是先显示第十大反派(Newman)。你必须在每个页面中
单击Next,才能看到第一大反派(Gustavo Fring)。这让网站能够在10个页
面中显示广告,但用户需要单击Next 九次才能看到第一个,真的是很烦。
-
数组和链表的特点
-
数组查询快,修改慢
-
链表查询慢,修改快
-
运行时间 数组 链表 查询 O(1) O(n) 修改 O(n) O(1)
-
-
数组和列表相结合
- 数组作为索引,再结合链表,也就是散列表(哈希表),是大型网站使用的数据结构。
- 这种结构下查询速度略慢于数组,但是修改速度和链表相当
2. 选择排序
-
选择排序问题:
- 一个双列结构(键值对),要根据值的大小进行自然排序,首次遍历一次选出最小的,第二次遍历选出第二小的,以此类推,最后排序结束。
-
操作数:
O(n)意味着对列表中每个元素都操作一次,每次遍历的次数为n,一共有n个元素,因此要遍历n次。所以
需要的总操作时间为 O( n * n ) ,即 O(n^2)
第一次需要检查n个元素,但随后检查的元素
数依次为n - 1, n - 2, …, 2和1。平均每次检查的元素数为1/2 × n,因此运行时间为O(n × 1/2 × n)。
但大O表示法省略诸如1/2这样的常数(有关这方面的完整讨论,请参阅第4章),因此简单地写
作O(n × n)或O(n2)。选择排序是一种灵巧的算法,但其速度不是很快。快速排序是一种更快的排序算法,其运行
时间为O(n log n),这将在下一章介绍。 -
示例代码:
def findSmallest(arr):
smallest = arr[0]
smallest_index = 0
for i in range(1, len(arr)):
if arr[i] < smallest:
smallest = arr[i]
smallest_index = i
return smallest_index
#现在可以使用这个函数来编写选择排序算法了。
def selectionSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append(arr.pop(smallest))
return newArr
print selectionSort([5, 3, 6, 2, 10])
- python中的pop()方法可以移除列表中的一个元素并将其返回
- 因此每次对列表进行遍历,得到最小值的索引,将其pop,如此往复,添加到新的列表中,就完成了选择排序(每次选择一个元素进行操作)
3. 小结
- 计算机内存犹如一大堆抽屉。
- 需要存储多个元素时,可使用数组或链表。
- 数组的元素都在一起。
- 链表的元素是分开的,其中每个元素都存储了下一个元素的地址。
- 数组的读取速度很快。
- 链表的插入和删除速度很快。
- 在同一个数组中,所有元素的类型都必须相同(都为int、double等)。