两道O(n)面试题
1. 给你一个长度为N的链表。N很大,但你不知道N有多大。你的任务是从这N个元素中随机取出k个元素。你只能遍历这个链表一次。你的算法必须保证取出的元素恰好有 k个,且它们是完全随机的(出现概率均等)。
保存前k个元素于集S. 从第k+i(i>0)个开始, 令r1=rand(1,k+i), 如果r1>k, 则保持现有的选择集合S不变 . 如果r1<=k, 令r2=rand(1,k),并让第k+i元素(即当前元素)替换集合S里第r2个元素 .
Reservoir Sampling (蓄水池抽样)
Init : a reservoir with the size: k
for i= k+1 to N
M=random(1, i);
if( M < k)
SWAP the Mth value and ith value
end for
每个元素被选择的都是等概率k/n.
证明 :
P1=P2=…=Pk=∏i=1n-k [ i/(k+i)+(k/(k+i))*((k-1)/k) ] =(k/k+1) * (k+1)/(k+2) *...*(n-1)/n =k/n
设1<=i<=n-k
Pk+i= k/(k+i) *∏ j=1n-k[(i+j)/(k+i+j)+k/(k+i+j)*(k-1)/k)]
= k/(k+i) *[(k+i)/(k+i+1) * (k+i+1)/(k+i+2) *...*(n-1)/n]
= k/(k+i) * (k+i)/n
= k/n
2. 给你一个数组A[1..n],请你在O(n)的时间里构造一个新的数组B[1..n],使得B[i]=A[1]*A[2]*…*A[n]/A[i]。你不能使用 除法运算。
可拆分为B[i]=A[1]*A[2]*…*A[i-1] +A[i+1]*A[i+2]*…*A[n],遍历2次即可求得.