ImageVerifierCode 换一换
格式:DOCX , 页数:25 ,大小:33.49KB ,
资源ID:5372122      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5372122.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(手机分配短讯id的面试题目.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

手机分配短讯id的面试题目.docx

1、手机分配短讯id的面试题目问题分析原来的问题是要从一个无序ids数组里分配一个id。我们可以用数学方式去更清楚地说明这个问题。设m = 256 为所有id的个数,集合为所有id的集合。那么,给定一个已分配id的集合,(即参数ids),本题目可表示为,求一个(即传回的id),符合条件:减号是补集的意思,即x属于U但不属于A。上回的对答已确定,即必然存在。此外,这个条件又可以写成:以上两种表达式可说明此问题的两种解法,一种编程方向是查找U集里有没有不属于A的id,而另种是计算A的补集再取出其中一个id。纯函数API的解实现程序之前,如果可以,应先写测试函数。笔者认为,若面试者在情况容许下,也可在解

2、答题目之前,写下测试程序。如果有多个面试者能同样解题,或许同时写下测试程序的面试者能脱颖而出。测试函数为了简单起见,笔者使用了assert()来检测正确性,只于Debug版本有效。而Release版本则用来测试效能。由于U集合的子集合很多,不可能穷举所有可能集合。所以,只能够举出随机的集合以作测试。以下是一些常数(宏)及类型声明,TEST_COUNT是测试次数,而TEST_REPEATCOUNT是为了测试效能时,重覆测试的次数(即Release版本会调用测试函数一百万次):12345678910111213#define M 256 / ID的数目,且所有ID在0, M)的区间内#define

3、 TEST_COUNT 10000 #ifdef NDEBUG #define TEST_REPEATCOUNT 100 #else #define TEST_REPEATCOUNT 1 #endif typedefunsigned charbyte; typedefunsigned longdword; typedefbyte (*idalloc_func)(byte*, size_t); 首先,写一个帮助函数测试某id是否在ids集合之内(不熟C+的读者可参考C版本):123456/ 检测ids里是否含id (C+ 版本) inlineboolcontain(byte* ids, size

4、_tn, byte id) assert(ids != NULL); returnfind(ids, ids + n, id) != ids + n; 123456789/ 检测ids里是否含id (C 版本) inlineboolcontain(byte* ids, size_tn, byte id) assert(ids != NULL); for(size_ti = 0; i n; i+) if(idsi = id) returntrue; returnfalse; 笔者首先写了一个测试平均情况的测试平台函数:123456789101112131415161718192021222324

5、25262728/ 测试平均情况 voidtest_average(idalloc_func idalloc) assert(idalloc != NULL); byte idsM; for(size_ti = 0 ; i M; i+) idsi = (byte)i; srand(0); / 使每次测试的伪随机数相同 size_tn = 0; for(inttest = 0; test TEST_COUNT; test+) random_shuffle(ids, ids + M); / 把整个数组洗牌 for(intrepeat = 0; repeat TEST_REPEATCOUNT; re

6、peat+) byte id = idalloc(ids, n); (void)id; assert(!contain(ids, n, id); / 测试是否最小的id for(size_ti = 0; i id; i+) assert(contain(ids, n, (byte)i); n = (n + 1) % M; 简单解释。首先,把ids数组填入所有id值。利用random_shuffle()把把整个ids数组洗牌,而n则是在0, M)区间里循环递增。由于笔者给出的解,都能传回最小的id,所以也会测试这条件。而最坏情况,就是ids含无序的0, 1, . M - 2,分配到的id为M-1

7、,笔者也为此编了一个最坏情况的效能测试函数。12345678910111213141516171819202122/ 测试最坏情况(ids为无序的0, M - 2, 结果必然是id = M - 1) voidtest_worst(idalloc_func idalloc) assert(idalloc != NULL); constsize_tn = M - 1; byte idsn; srand(0); / 使每次测试的伪随机数相同 for(size_ti = 0 ; i n; i+) idsi = (byte)i; for(inttest = 0; test TEST_COUNT; tes

8、t+) random_shuffle(ids, ids + n); for(intrepeat = 0; repeat TEST_REPEATCOUNT; repeat+) byte id = idalloc(ids, n); (void)id; assert(id = M - 1); 线性查找最简单的想法,可能是遍历所整个U集合(即0至M-1),并使用contain()函数检测该id是否不包含在ids数组里。12345678910111213/ 线性查找 (总是传回最小id) / 时间复杂度: O(n2) / 临时内存大小: 0 字节 / 注: 因为n M,无论ids内的值为何(甚至有重复元

9、素),必然可找到一个id,所以id的for不用边界检查。 byte linear_search(byte* ids, size_tn) assert(ids != NULL); assert(n M); / 逐个id检查是否存在于ids, ids + n) for(byte id = 0; ; id+) if(!contain(ids, n, id) returnid; 二分查找网友Doyle在TL里提出了用二分查找的主意。笔者实现了两种形式,以下这个是不需额外内存。原理是把U集合分割为两个各占一半的区间,分别数算两个区间内的已分配元素数目,若元素数目少于区间大小,即代表该区间内有未分配的id

10、。再继续分割该区间,直至区间内都是可分配的id(即找到的元素是零)。123456789101112131415161718192021222324252627282930313233343536373839/ 数ids内有多少个id在min, max)的区间内 inlinesize_tcount_interval(byte* ids, size_tn, size_tmin, size_tmax) size_tcount = 0; for(size_ti = 0; i = min & idsi max) count+; returncount; / 二分查找 (总是传回最小id) / 时间复杂度

11、: O(n lg n) / 临时内存大小: 0 字节 byte binary_search(byte* ids, size_tn) assert(ids != NULL); assert(n M); size_tl = 0, r = M; for(;) size_tc = (l + r) / 2; / 把id范围从l, r)分割为l, c), c, r)两个区间 size_tcount; / 以下的条件测试次序保证了传回最小id if(count = count_interval(ids, n, l, c) c - l) if(count = 0) return(byte)l; r = c;

12、elseif(count = count_interval(ids, n, c, r) r - c) if(count = 0) return(byte)c; l = c; elseassert(false); / 因为n M,不可能找不到任何id 这算法在最坏情况比线性查找快,但平均情况下却不一定。排序以上两个解,都是查找的方式,毋需改动数据。相反,另一类解用的算法需改动ids数组内的元素,或是把ids复制到另一个临时数组里进行更改型的算法。最简单的算法,是把无序的ids排序。之后就可以从头开始扫描未分配的id。123456789101112131415161718/ 排序 (总是传回最小i

13、d) / 时间复杂度: O(n lg n) / 临时内存大小: M 字节(如果可改变ids则是0) byte sort_stl(byte* ids, size_tn) assert(ids != NULL); assert(n M); byte bufferM; memcpy(buffer, ids, n); sort(buffer, buffer + n); / 平均 O(n lg n) for(size_ti = 0; i n; i+) if(bufferi != i) return(byte)i; return(byte)n; 但读者可能会想到,把整个数组排序可能会做了很多无用工。而且,

14、快速排序(quicksort)的最坏时间复杂度是O(n2)。因此,就有了下一个解。堆笔者想到的另一个解是使用堆(heap)数据结构。堆可保证第一个元素是最小的元素(通常是最大的,但这题目里我们希望取得最小的),而每次弹出这个元素,取出第二小的元素只需要O(lg n)的时间。 sort_stl()需要完整排序,而使用堆则是逐步进行的,中途找到没用到的id就可以停下来,所以平均来说会省下很多时间。123456789101112131415161718192021/ 堆 (总是传回最小id) / 时间复杂度: O(n lg n) / 临时内存大小: M 字节(如果可改变ids则是0) byte he

15、ap_stl(byte* ids, size_tn) assert(ids != NULL); assert(n M); byte bufferM; memcpy(buffer, ids, n); byte* end = buffer + n; make_heap(buffer, end, greater(); / O(n) for(byte id = 0; buffer != end; id+, end-) if(buffer0 != id) returnid; pop_heap(buffer, end, greater(); / O(lg n) return(byte)n; 最坏的情况,是

16、要把最小的M-1个元素最弹出,才能求得id=M-1。这情况其实等价于堆排序(heapsort)。剖分另一个方法和二分查找相似,就是把数组剖分(partition)为两部分,这应该是Doyle提出的原意。原理是,设一个中间c=M/2,用它把无序ids集合剖分为两个无序集合,前一个集合的元素小于c,后一个的元素大于或等于c。那么,应该有一个集合的元素数量少于id区间的大小,再把该集合继续剖分,直至变成空集。1234567891011121314151617181920212223242526272829303132333435/ 剖分 (总是传回最小id) / 时间复杂度: O(n) / 临时内存

17、大小: M 字节(如果可改变ids则是0) byte partition_stl(byte* ids, size_tn) assert(ids != NULL); assert(n M); byte bufferM; memcpy(buffer, ids, n); byte *first = buffer, *last = buffer + n; size_tl = 0, r = M; for(;) size_tc = (l + r) / 2; byte* middle = partition(first, last, bind2nd(less(), c); / O(n) / 后置条件: l

18、= first, middle)内元素 c 及 c = middle, last)内元素 r/ 以下的条件测试次序保证了传回最小id if(first = middle) return(byte)l; elseif(size_t)distance(first, middle) c - l) last = middle; r = c; elseif(middle = last) return(byte)c; elseif(size_t)distance(middle, last) r - c) first = middle; l = c; elseassert(false); 此算法的妙处在于,

19、时间复杂度仅为O(n)!为什么呢?因为partition()的时间复杂度是O(n),而此算法中每个迭代需处理的元素是n, n/2, n/4, .,把这个几何数列求和,得出2n,所以此算法为线性时间。布尔集合也许,最多网友都想到的解,就是把ids无序数组变换为另一个集合表示方式,能更快地测试A是否不含某id。这种表达方式是使用一个布尔数组(boolean array),储存某id是否在ids无序数组里。用数学方式,可以称这个数组为一个函数:建立这个数组之后,再扫描一次,找出没使用到的id。1234567891011121314151617181920212223/ 布尔集合 (总是传回最小id)

20、 / 时间复杂度: O(n) / 临时内存大小: M 字节 byte boolset(byte* ids, size_tn) assert(ids != NULL); assert(n M); boolid_usedM = false; / 填充 id_used for(size_ti = 0; i n; i+) assert(!id_usedidsi); / 此处断言失败代表ids有重复元素 id_usedidsi = true; / 扫描id_used去找出最小未用id for(size_ti = 0; i M; i+) if(!id_usedi) return(byte)i; asser

21、t(false); return0; 这类解法在纯函数API中是最快的,但必须使用额外内存。位集合上述的解,每个数组元素由于只需储存1个位(bit),可以把8个布尔值置于字节里,减少额外内存。这种集合称为位集合(bit set)或位图(bitmap)。此外,在32位CPU上,可一次检查32位是否全0或全1,这可是一个优化。这次,我们直接储存补集A,即是那些分配了的id会把位设为0,那么在扫描时就不需做一个not位元运算。123456789101112131415161718192021222324252627282930313233343536/ 位集合 (总是传回最小id) / 时间复杂度:

22、 O(n) / 临时内存大小: floor(M + 31) / 32) * 4 字节 byte bitset_standard(byte* ids, size_tn) assert(ids != NULL); assert(n M); constsize_tdword_count = (M + 31) / 32; dword id_unused_bitsdword_count; / 开始时设全部id为未用(即设位为1) memset(id_unused_bits, 0, sizeof(id_unused_bits); / 填充id_unused_bits (ids内的位清为0) for(siz

23、e_ti = 0; i n; i+) size_tindex = idsi / 32; dword bitIndex = idsi % 32; assert(id_unused_bitsindex & (1 bitIndex); id_unused_bitsindex = (1 bitIndex); / 扫描id_unused_bits,找出最小未用id for(size_tindex = 0; index dword_count; index+) if(dword bits = id_unused_bitsindex) for(dword bitIndex = 0; bitIndex 32; bitIndex+) if(bits & (1 bitIndex) dword id = index * 32 + bitIndex; assert(id M); return(byte)id; assert(false); return0; 在某些CPU上,还会支持一个汇编指令bsf(bit scan forward),可扫描一个32位值里,第一个为1的位索引(从LSB至MSB)。这正正是我们想要的。以下使用了Visual C+的内

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1