《算法图解——像小说一样有趣的算法书》
本书在此只记录结果,算法。
1 二分法
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: //item为要搜索的值,guess>item:规定了上界。
high=mid-1
else: //item为要搜索的值,guess<item:规定了下界。
low=mid+1
my_list=[1,3,5,7,9]
print(binaty_search(my_list,3)) # =>1
print(binaty_search(my_list,-1)) # =>None
2 选择排序
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]))
3 递归
- 用递归的方法,在盒子中找钥匙的步骤:
① 检查盒子中的每样东西。
② 如果是盒子,就回到第一步。
③ 如果是钥匙,就大功告成!
def look_for_key(box):
for item in box:
if item.is_a_box():
look_for_key(item) //递归
elif item.is_a_key():
print("found the key! ")
- 基线条件(base case) 和 递归条件(recursive case)
编写递归函数时,必须要告诉它何时停止递归。正因为如此,每个递归函数都有两部分:基线条件(base case) 和 递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则指的是函数不再调用自己,从而避免形成无限循环。
def countdown(i):
print(i)
if i<=0: //==>基线条件(base case)
return
else: //==>递归条件(recursive case)
countdown(i-1)
- 递归调用栈
递归函数factorial的调用栈( 5! )
5!=5 x 4 x 3 x 2 x 1
def fact(x):
if x==1: //==>基线条件(base case)
return 1
else: //==>递归条件(recursive case)
return x*fact(x-1)
原来盒子堆存储在栈中!这个栈包含未完成的函数调用,每个函数调用都包含还未检查完的盒子。使用栈很方便,因为你无需自己跟踪盒子堆——栈替你这样做了。
4 快速排序
分而治之(divide and conquer,D&C)——一种著名的递归式问题解决办法。
快速排序
def quicksort(array):
if len(arr)<2:
return array
else:
pivot=array[0]
less=[i for i in array[1:] if i<=pivot]
greater=[i for i in array[1:] if i>pivot]
return quicksort(less)+[pivot]+quicksort(greater)
print(quicksort([10,5,2,3]))
合并排序
这个函数遍历列表中的每个元素并将其打印出来。它迭代整个列表一次,并在打印每个元素前休眠一秒。
from time import sleep
def print_item2(list):
for item in list:
sleep(1)
print(item)
5 散列表
使用函数 dict( )来创建散列表。散列表由键和值组成。散列表将键映射到值,键不可重复。 在散列表book中,键为商品名,值为商品价格。
book=dict()
book["apple"]=0.67
book["milk"]=1.49
book["avocado"]=1.49
print(book)
>>>{'apple':0.67,'milk':1.49,'avocado':1.49}
print(book["avocado"])
>>>1.49
练习:请问下面哪些散列函数是一致的?
① f(x)=1 <-------------------无论输入是什么,都返回1
② f(x)=rand( ) <-------------------每次都返回一个随机数
③ f(x)=next_empty_slot( ) <---------------返回散列表中下一个空位置的索引
④ f(x)=len(x) <-------------------将字符串的长度用作索引
答案:① ③
应用案例
- 将散列表用于查找
手机都内置了方便的电话簿,其中每个姓名都有对应的电话号码。假设你要创建一个类似这样的电话簿,将姓名映射到电话号码。该电话簿需要提供如下功能。
- 添加联系人及其电话号码。
- 通过输入联系人来获悉其电话号码。
- 创建映射。
- 查找。
phone_book=dict() //新建一个散列表,phone_book={},与phone_book=dict()等效
phone_book["jenny"]=8675309
phone_book["emergency"]=911
print(phone_book["jenny"])
>>>8675309
DNS解析(DNS resolution)
散列表能让你能够轻松地模拟映射关系。散列表被用于大海捞针的查找。例如你在访问http://adit.io这样的网站,计算机必须将adit.io转换成IP地址。
ADIT.IO→173.255.248.55
无论你访问哪个网站,其网址都必须转换成IP地址。
GOOGLE.COM→74.125.239.133
FACEBOOK.COM→173.252.120.6
SCRIBD.COM→23.235.47.175
DNS解析(DNS resolution),散列表是提供这种功能的方式之一。
- 防止重复
假设你负责管理一个投票站。显然,每人只能投一票,但如何避免重复投票呢?有人来投票时,你询问他的全名,并将其与已投票者名单进行比对。如果名字在名单中,就说明这个人已经投过票了,因此将他拒之门外!否则,就将他的姓名加入到名单中,并让他投票。现在假设有很多人来投过了票,因此名单非常长。每次有人来投票时,你都得浏览这个常常的名单,以确定他是否投过票。但有一种更好的办法,就是用散列表。
voted={} //创建一个散列表,记录已投票的人
def check_voter(name):
if voted.get(name): //name在散列表中,返回value
print("kick them out ! ")
else: //name在散列表中,返回None
voted[name]=True
print("let them vote ! ")
>>>check_voter("tom")
>let them vote !
>>>check_voter("mike")
>let them vote !
>>>check_voter("mike")
>kick them out !
//value=voted.get("tom")
//如果"tom"在散列表中,函数get将返回它;否则返回None。
- 将散列表用作缓存
散列表在缓存方面的应用。
① 你将Facebook服务器发出请求。
② 服务器作出处理,生成一个网页并将其发送给你。
③ 你获取一个网页。
如果你登录了Facebook,你看到的所有内容都是为你定制的。你每次访问facebook.com,其服务器都须考虑你感兴趣什么内容。如果你没有登录,看到的将是登录页面。每个人看到的登录页面都相同。Facebook被反复要求做同样的事情:“当我注销时,请向我显示主页。”有鉴于此,它不让服务器去生成主页,而是将主页储存起来,并在需要时将其直接发送给用户。
这就是缓存,具有以下两个优点:
- 用户能够更快看到网页,就像你记住了月球与地球之间的距离一样,下次你侄女再问你时,你就不用再使用Google搜索,立刻就可以告诉她答案。
- Facebook需要做的工作更少。
缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中。
具体代码如下:
cache={}
def get_page(url):
if cache.get(url):
return cache(url)<------------------返回缓存中的数据
else:
data=get_data_from_server(url)
cache[url]=data<----------------先将数据保存在缓存中
return data
仅当URL不在缓存中时,你才让服务器做些处理,并将处理生成的数据存储到缓存中,再返回它。当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进行处理了。
你几乎根本不用自己去实现散列表,因为你使用的编程语言提供了散列表实现。你可使用Python提供的散列表,并假定能够获得平均情况下的性能。