Donald Knuth教授曾在《 The Art of Computer Programming》中提到:有很多计算机花了超过一半的计算时间在排序上。可以理解为:
- 确实有很多非常重要的和排序有关的应用;
- 有很多人在进行一些不必要的排序;
- 低效的排序算法被广泛应用造成了计算时间的浪费。
在Python中,对于排序的策略:
- 当需要排序的时候,尽量设法使用内建Python列表的sort方法;
- 当需要搜索的时候,尽量设法使用内建的字典。
最常用的是decorate-sort-undecorate(DSU)模式,这是一种通用的方法:通过创建一个辅助的列表,将问题转化为列表的排序,从而可以使用默认的快速的sort方法。
对字典的键排序
- 根据键将值排序:
def sortedDictValues1(adict):
keys = adict.keys()
keys.sort()
return [adict[key] for key in keys]
----------
def sortedDictValues2(adict):
keys = adict.keys()
keys.sort()
return map(adict.get,keys)
不区分大小写对字符串列表排序
- 采用DSU方式:先创建一个辅助列表,每个元素都是元组,元组的元素则来自原列表,并当做“键”处理。
def case_insenseitive_sort1(string_list):
alist = [(x.lower(),x) for x in string_list]
alist.sort()
return [x[1] for x in alist]#不改变原列表
----------
def case_insenseitive_sort2(string_list):
alist = [(x.lower(),x) for x in string_list]
alist.sort()
string_list[:] = [x[1] for x in alist]#直接作用于原列表,改变原列表
- 更快捷的方式:
def case_insensitive_sort(string_list):#适用于普通字符串和unicode对象
return sorted(string_list,key=lambda s:s.lower())
根据对应值将键或索引排序
- 柱状图,实际上就是基于各种不同元素出现的次数,根据对应值将键或索引排序。下面是dict的一个子类,它为了这种应用加入了两个方法:
class hist(dict):
def add(self,item,increment=1):
'''为item的条目增加计数'''
self[item] = 1 + self.get(item,0)
def counts(self,reverse=False):
'''返回根据对应值排序的键的列表'''
aux = [(self[k],k) for k in self]
aux.sort()
if reverse:
aux.reverse()
return [k for v,k in aux]
----------
#跑个小例子:
if __name__ == '__main__':
sentence = 'hello world hello python'
words = sentence.split()
c = hist()
for word in words:
c.add(word)
print 'ascending count:'
print c.counts()
print 'decending count:'
print c.counts(reverse=True)
#输出:
ascending count:
[(1, 'python'), (1, 'world'), (2, 'hello')]
decending count:
[(2, 'hello'), (1, 'world'), (1, 'python')]
- 如果想将元素的统计结果放到一个列表中,做法也非常类似:
class hist(list):
def __init__(self,n):
'''初始化列表,统计n个不同项的出现'''
list.__init__(self,n*[0])
def add(self,item,increment=1):
'''为item的条目增加计数'''
self[item] += increment
def counts(self,reverse=False):
'''返回根据对应值排序的键的列表'''
aux = [(v,k) for k,v in enumerate(self)]
aux.sort()
if reverse:
aux.reverse()
return [k for v,k in aux]
- 更简单快速的方法:
def sorted_keys(the_dict,keys,reverse):
return sorted(keys,key=the_dict.__getitem__,reverse=reverse)
根据内嵌的数字将字符排序
- 首先将字符串按照数字与非数字切割开,然后将序列中的str型数字转化为int型数字,最后将其放在元组作为排序的键。例如下面这个字符串列表进行排序:
files = ['weibo9.txt','weibo6.txt','weibo2.txt','weibo8.txt']
按照上述思路:
import re
def find_numbers(elem):
re_digits = re.compile(r'(\d+)')
pieces = re_digits.split(elem)
pieces[1] = int(pieces[1])
return pieces[1]
----------
def sort_by_find_numbers1(the_list):
aux = [(find_numbers(i),i) for i in the_list]
aux.sort()
return [j for i,j in aux]
files = ['weibo9.txt','weibo6.txt','weibo2.txt','weibo8.txt']
print ' '.join(sort_by_find_numbers1(files))#更美观的输出
print sort_by_find_numbers1(files)#输出为列表
#输出:
weibo2.txt weibo6.txt weibo8.txt weibo9.txt
['weibo2.txt', 'weibo6.txt', 'weibo8.txt', 'weibo9.txt']
- 更快捷的操作(使用上面定义的find_numbers函数):
def sort_by_find_numbers2(the_list):
return sorted(the_list,key=find_numbers)
以随机顺序处理列表的元素
- 最简单和最高效的方法:首先对序列洗牌,然后线性访问洗完牌的序列:
import random
def process_all_in_random(data,process):
data = list(data)
random.shuffle(data)
for elem in data:
process(elem)
- 假如要从列表中随机挑选出一个元素,进行相应的处理后,将该元素删除。
import random
def process_random_and_moveing(data,process):
while data:
elem = random.choice(data)
a = process(elem)
print a
data.remove(elem)
if __name__ == '__main__':
d = ['1','2','3','4','5','5','5']
process_random_and_moving(d,int)#
#输出:
2
5
1
5
3
4
5
- process_random_and_moving函数运行效率不高,因为每个data.remove都会线性地搜索整个列表以获取要删除的元素。
在添加元素时保持序列顺序
某序列需要在不断加入新元素后,扔处于排序完毕状态,以便检查或者删除当前序列中最小的元素。
- 首先会想到,可以调用
the_list.sort()
排序,然后用the_list.pop(0)
来获取和删除最小的元素。 - 引入堆的组织数据的结构。这是一种简洁的二叉树,能确保父节点总是比子节点小。在Python中维护一个堆的最好方式是使用列表,这个列表无需完成排序,却能够保证得到当前最小的元素,然后所有节点会被调整,以确保堆特性仍然有效:
the_list = [4,1,7,5,2,9,0]
import heapq
heapq.heapify(the_list)
heapq.heappop(the_list)#返回并删除最小元素
heapq.heapreplace(the_list,10)#加入一个新元素后,返回并删除之前最小的元素
获取序列中最小的几个元素
- 可以先将序列排序,再使用sep[:n]获取最小的n个元素。
- 或者使用简单可行的生成器:
import heapq
def sort_(data):
data = list(data)
heapq.heapify(data)
while data:
yield heapq.heappop(data)
- 还有更快捷的方法:
import heapq
def n_smallest(n,data):#n个最小
return heapq.nsmallest(n,data)
----------
def n_largest(n,data):#n个最大
return heapq.nlargest(n,data)
----------
#也可不通过函数
heapq.heapify(data)
heapq.nsmallest(n,data)
heapq.nlargest(n,data)