本章主要讲述了磁盘排序算法和针对特殊数据我们可以作出的特殊算法.
习题2:主要思路就是将整数进行映射,a[i<<5] <->1<<(i&31)。这样就实现了将一个整数数组当作位向量来使用了.
习题4:
def fast_generate(N,k):
random_list = []
x = [i for i in range(0,N)]
for i in range(0,k):
t = int(random()*(N-i)+i)
x[i],x[t] = x[t],x[i]
random_list.append(x[i])
return random_list
这是一个O(k)效率的巧妙算法,我自己的思路是将所有没有选作随机数的加入到一个集合里面,然后从这个集合找随机数,开始每找到一个随机数,就从集合里面删除.但是删除这个操作也是很消耗时间的,对于一个list,删除的效率与元素位置有关,越靠近末尾消耗时间越少.但是逆序删除一个元素的效率是O(N)!!!而在C/C++里面,如果我们使用数组存储数据,就不能执行删除操作了.
所以上面这个算法的巧妙之处在于通过交换间接的执行了删除的效果.而交换是一个常数时间的操作.
[5]习题5,进行两次排序,分别解决0-4999999,和5000000-100000000的排序.
下面的代码对分两趟进行排序
class bit_vector(object):
def __init__(self):
self.__vector = []
self.__shift = 5
self.__mask = 0x1f
def set(self,i):
self.__vector[i>>self.__shift] |= 1<<(i&self.__mask)
def test(self,i):
return self.__vector[i>>self.__shift] & 1<<(i&self.__mask)
def clc(self,max):
self.__vector = [0]*(1+max//self.__mask)
def sort(self,L,min,max):
print('*'*10,'start sorting')
self.clc(max-min)
for item in L:
self.set(item-min)
for i in range(min,max+1):
if(self.test(i-min)):print(i)
start = time.clock()
random = fast_generate(10000000,20)
sort_vec = bit_vector()
sort_vec.sort([i for i in random if i<5000000],0,5000000)
sort_vec.sort([i for i in random if i>=5000000],5000000,10000000)
end = time.clock()
print(end-start)
习题6,假如每个号码会重复10次,应该怎么做?
解决方法是用4位来表示一个数,最多可以表示15次重复出现.
class bit_vector(object):
def __init__(self):
self.__vector = []
self.__shift = 0b11
self.__mask = 0b111
def set(self,i):
self.__vector[i>>self.__shift] += 1<<(i&self.__mask)*4
def test(self,i):
return self.__vector[i>>self.__shift] & 0b1111<<(i&self.__mask)*4
def dec(self,i):
self.__vector[i>>self.__shift] -= 1<<(i&self.__mask)*4
def clc(self,max):
self.__vector = [0]*(1+max//self.__mask)
def sort(self,L,min,max):
print('*'*10,'start sorting')
self.clc(max-min)
for item in L:
self.set(item-min)
for i in range(min,max+1):
while(self.test(i-min)):
print(i)
self.dec(i)
习题7:如果有重复的数出现,第1次以后出现的数会被忽略掉.当输入负数,会访问a[-k]的32个bit.当超过n,则会访问大于数组的最后一个元素.输入的不是整数:比如浮点数.但是这样是不太符合题意的,3.2和3.1一样都被转换为3.
习题8:存储:直接分成3组来进行存储.将800,877和888后面的区号分别存储.而这样需要的空间应该是1.25MB*(前缀个数)。
或者:
1、首先扫描整个文件,看有哪个免费号码前缀。以及每个免费号码前缀下的号码个数。
2、设置区间映射表:比如800前缀有125个免费号码,找到最大的数,与最小的数,差值做为该索引下的位数.这种方法比较节省空间.
排序:分多次(前缀个数)读入文件排序是可以的,还可以分两次读入数据,然后先做两次排序,然后进行归并.这里的归并稍微特殊一点,先比较前缀再看是否需要比较后面7位.
习题9:这个题目的思路非常巧妙,利用了两个n元向量和一个整数top来解决问题,首先看核心代码:
data[i] = 1;
from[i] = top;
to[top++] = i;
这个代码是什么意思呢?
首先我们要检测一个位置是不是被初始化了(访问了),可以查看from[i] 中的值,这里有几个判断:首先from[i]<top且from[i]>=0,这个语句的意思是,from[i]至少不是负数的随机值且对应的to肯定被处理过,保证访问to的时候不会出错,现在有两种情况,from[i]如果不是随机值,那么data[i]就被访问(赋值)过了.所以关键是判断是不是随机值,这就看to[from[i]]==i了,如果from[i]是一个随机值,那么to[from[i]]就不可能等于i了.因为i根本就没有被放入to这个向量里面.