一.前言
1.算法的威力 最小可用ID
题目:在系统中找到最小可用ID分配给新用户.
暴力解法:
嵌套循环,外部循环为从0开始的整数,每次加一,内层循环遍历已有系统中的所有ID,直到找到系统中不存在的ID。
function Min-Free(A)
x <-- 0
loop
if x ∉ A then
return x
else
x <-- x + 1
其中,∉定义如下:
function ∉(x, X)
for i <-- 1 to len(X) do
if x = X[i] then
return False
else
return True
则代码的复杂度为O(n^2)
改进一
考虑当最小ID存在时,则在n个非负整数[0,n),必存在某一个x不在其中,因此可以建立一个长度为n+1的数组,来标记这些整数。
function Min-Free(A)
F <-- [False, False,...,False] where len(F) = n+1
for x ∈ A do
if x < n then
F[x] <-- True
for i <-- [0,n] do
if F[i] = Fales then7
return i
上述代码中,A为现有的ID系统数组,初始化数组中所有元素为False需要O(n)的时间,遍历A中的所有元素,只要小于n就标记为True,需要O(n)时间,最后线性查找标志数组中第一个值为False的位置。故复杂度为O(n)。
minfree(A) = search(A, 0, len(A)-1)
search(A,l,u) = l : A = ∅
search(A'', m+1,u): len(A')=m-l+1
search(A', l, m): 其他
其中有:
m = (l + u) / 2
A' = {x ∈ A ∩ x <= m}
A'' = {x ∈ A ∩ x > m}
l和u分别为查找上下界。
举例:
A = {0,1,2,3,4,5,6,7,9,10}
①执行search(A,0,9),m=5,则A’={0,1,2,3,4,5},A”={6,7,9,10},此时len(A’)=5-0+1=6,因此执行search(A”,6,10);
②m=8,则A’={6,7},A”={9,10},此时len(A’)≠8-6+1,因此执行search(A’,6,8);
③m=7,则A’={6,7},A”=∅,此时len(A’)=7-6+1=2,因此执行search(A”,8,8);
④此时A为空,因此最小ID为8。
该方法相较于方法一更加节省空间,因为方法一需要维护一个长度为n+1的标志数组,当n很大时,空间上的性能代价很高。而方法二中,第一次需要O(n)次比较来划分子序列A’和A”,第二次仅需要比较O(n/2)次……因此总的的时间复杂度为O(n+n/2+n/4…)=O(2n)=O(n)
function Get-Number(n)
x <-- 1
i <-- 0
loop
if Valid(x) then
i <-- i+1
if i=n then
return i
x <-- x + 1
function Valid(x)
while x mod 2 = 0 do
x <-- x/2
while x mod 3 = 0 do
x <-- x/3
while x mod 5 = 0 do
x <-- x/5
if x = 1 then
return True
else
return False
(1)初始状态,队列仅含有唯一元素1,计算1*2,1*3,1*5,此时队列变为{1,2,3,5};
(2)计算2*2,2*3,2*5新产生的元素4、6、10按顺序插入队列,此时队列变为{1,2,3,4,5,6,10};
(3)计算3*2,3*3,3*5产生元素6,9,15,由于6重复,故舍弃,队列变为{1,2,3,4,5,6,9,10,15};
...
算法复杂度O(1+2+3+…+n)=O(n^2)
function Get-Number(n)
if n=1 then
return 1
else
Q2 <-- {2}
Q3 <-- {3}
Q5 <-- {5}
while n > 1 do
x <-- min(Head(Q2), Head(Q3), Head(Q5))
if x = Head(Q2) then
Dequeue(Q2)
Enqueue(Q2, 2x)
Enqueue(Q3, 3x)
Enqueue(Q5, 5x)
else if x = Head(Q3) then
Dequeue(Q3)
Enqueue(Q3, 3x)
Enqueue(Q5, 5x)
else
Dequeue(Q5)
Enqueue(Q5, 3x)
n <-- n-1
return x
算法循环n次,每次循环都从队列中取出最小的一个元素,需要常数时间,接着根据取出取出元素所在的队列,产生1~3个新元素放入队列,这一步也是常数时间,因此复杂度为O(n)