The smallest free number

The smallest free number

介绍

    假设给定了一个自然数的集合Set X,现在要求找出最小的一个不在集合Set X中的自然数。

    现在我们假设如果集合Set X中的数据是按非降序给定的,那此时解法也非常的直观,我们只要从头开始比较X和集合[0..],第一个不同的数即为所求,但是,当Set X是以一个无序给定时,例如:

[08,23,09,00,12,11,01,10,13,07,41,04,14,21,05,17,03,19,02,06]

    此时我们要在怎么找出最小数呢?

解法一

    我们可以根据有序时的思想,我们只要从头开始判断[0..]是否在Set X中,Haskell代码如下:

vs \\ xs = filter (`notElem` xs) vs

minFree xs = head $ [0..] \\ xs

    中序操作符\\和集合运算符\功能差不多,vs \\ xs 是在vs中去除在xs中的元素,此时复杂度为Θ(n2)。

解法二

    我们再次分析下,我们是从集合[0..]的0开始找的,然后每个元素去比对是否在Set X中,假设Set X的长度为n,则此时如果元素不重复,并且充满[0..n-1],则此时满足条件的自然数为n,而且此时n是满足条件的自然数中最大的数了,因为假如Set X的n个元素中有比n大的数,则此时必然满足条件的数是[0..n-1]中的数了,于是根据上面的想法,我们可以根据Set X产生一个长度为n+1序列b,其中每个元素的含义为bi:自然数i在Set X中是否存在,存在为True,此时我们就可以在序列b中从头开始找有多少个连续的True了,此时True的长度即为所求的自然数了。Haskell代码如下:

search :: Array Int Bool -> Int
search = length . takeWhile id . elems

checklist    :: [Int] -> Array Int Bool
checklist xs =  accumArray (||) False (0,n)
				(zip (filter (<=n) xs) (repeat True))
minFree = search . checklist

    上面主要有两个函数,一个是search,另一个是checklist,其中search主要是传入一个Bool的数组,然后从头找出最长的True序列的长度,而checklist则是根据数组的Int list来得到一个Bool的数组,即上面的序列b.

解法三

    解法三是运用分治的方法,首先我们给出一些式子:

  • (as++bs)\\cs =(as\\cs)++(bs\\cs)

  • as\\(bs++cs)=(as\\bs)\\cs

  • (as\\bs)\\cs =(as\\cs)\\bs

    现在我们假设as和vs不相关,则此时我们可以得到as\\vs=as,bs和us也不相关,则有bs\\us=bs,此时我们可以得到:

(as++bs)\\(us++vs)=(as\\us)++(bs\\vs)

    此时,我们让as=[0..b−1] ,bs =[b..],并且让us=filter(<b)xs,vs=filter(≥b),则此时我们可以得到

[0..] \\ xs = [0..b-1] \\ us ++ [b..] \\ vs
				where (us,vs) = partition (<b) xs 
				
head (xs++ys) = if null xs then head ys else head xs -- when is the recursive is out??

    然后我们分析指导 null xs的含义即 length [0..b-1] == b,于是我们可以定义下函数如下:

minfree xs = minfrom 0 xs

minfrom a xs	                | null xs 		     = a  -- 递归结束的条件
				| length us == b - a = minfrom b vs
				| otherwise          = minfrom a us
				  where (us,vs) = partition (<b) xs

    接下去我们要确定b的值,我们希望确定的b值,能够将xs进行等分,这样则对问题的规模可以有效的减小,一个直观想法是

b =  a + n / 2 where n = length xs

    但是上面是有问题的,为什么呢?

    因为上面我们有个假设是这样的 us = [ x | x < b] = [a,b)的,而 vs = [ x | x >= b ] = [b..],故我们应该要的是

b =  a + 1 + n / 2 where n = length xs

    这样我们就可以保证上面的式子成立了,并且递归后的问题规模为:

length us ≤n div 2 < n 或者 length vs =n−ndiv 2−1≤n div 2

    于是问题的复杂度为 

T(n) = T(ndiv 2) + Θ(n)

minfrom a (n,xs)	| n == 0 	 = a
			| m == b - a     = minfrom b (n-m,vs)
			| otherwise      = minfrom a (m,us)
			where    (us,vs) = partition (<b) xs
				 b       = a + 1 + (n / 2)
				 m	 = length us

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值