有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。写一个函数实现。复杂度是什么。

本文介绍了一种统计数组中所有元素被随机访问次数的方法,并通过使用位图减少内存占用,最终实现了平均时间复杂度为O(nlogn)的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java代码 复制代码 收藏代码
  1. import java.util.Random;
  2. import java.util.Set;
  3. import java.util.TreeSet;
  4. /**
  5. * http://weibo.com/1915548291/z7HtOF4sx
  6. * #面试题#有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。
  7. * 写一个函数实现。复杂度是什么。
  8. *
  9. * 我认为这道题的考点是如何节省空间。
  10. * 当然可以用一个与原数组长度相同的数组来标记对应元素是否被取过,但这样太浪费空间
  11. * 用bitmap的话,只要一个bit就可以标记是否被取过,可参考《编程珠玑》的位图排序
  12. *
  13. * 时间复杂度的话,我不太会算,以下是引用https://github.com/vyan/test/blob/master/accessTimes.cpp:
  14. * 使用bit打点记录已经取的数,
  15. * 复杂度分析,假设数组总长度为n
  16. * 取到第1个之前未被取到的数的期望 E(1)=1
  17. * 取到第2个之前未被取到的数的期望 E(2)=n/n-1
  18. * 取到第3个之前未被取到的数的期望 E(3)=n/n-2
  19. * ...
  20. * 取到第n个之前未被取到的数的期望 E(n)=n/1
  21. * 总得期望次数E=n+n/(n-1)+n/(n-2)+...+n/1;
  22. * =n(1+1/(n-1)+1/(n-2)+...+1/1)
  23. * =nln(n)
  24. * 所以算法平均复杂度为nlogn
  25. *
  26. * 下面的代码里面,除法和求模运算我都用位运算来实现(为了练习位运算),事实上直接用java提供的(/,%)也可以
  27. * 同时,为了验证是否真的取到了数组的所有元素,我用了TreeSet保存已选中的下标(去重)
  28. *
  29. * 最后,还有一个问题是,可能取了很多次,都没能全选中,这个时候可以设置一个最长时间或者最大尝试次数,超过则结束程序,
  30. * 避免程序长时间运行甚至死循环
  31. *
  32. * @author lijinnan
  33. *
  34. */
  35. public class TimesOfAccessArray {
  36. public static final int SHIFT = 5;
  37. public static final int BLOCK_SIZE = (1 << SHIFT); //32
  38. public static final int MASK = (1 << SHIFT) - 1; //31
  39. public static void main(String[] args) {
  40. int[] array = new int[200];
  41. long times = accessTimes(array);
  42. System.out.println(times);
  43. }
  44. public static long accessTimes(int[] array) {
  45. if (array == null || array.length == 0) {
  46. return -1L;
  47. }
  48. long result = 0L;
  49. int len = array.length;
  50. int[] bitmap = new int[divide(len, BLOCK_SIZE) + 1];
  51. int setTimes = 0;
  52. Set<Integer> set = new TreeSet<Integer>();
  53. while (setTimes < len) {
  54. int pos = new Random().nextInt(len);
  55. set.add(pos);
  56. if (set(bitmap, pos)) {
  57. setTimes++;
  58. }
  59. result++;
  60. }
  61. System.out.println(set);
  62. return result;
  63. }
  64. public static boolean set(int[] bitmap, int pos) {
  65. boolean result = false;
  66. int blockNo = divide(pos, BLOCK_SIZE);
  67. int index = mod(pos, BLOCK_SIZE);
  68. boolean notExist = (bitmap[blockNo] & (1 << index))== 0;
  69. if (notExist) {
  70. bitmap[blockNo] |= (1 << index);
  71. result = true;
  72. }
  73. return result;
  74. }
  75. public static boolean exist(int[] bitmap, int pos) {
  76. int blockNo = divide(pos, BLOCK_SIZE);
  77. int index = mod(pos, BLOCK_SIZE);
  78. return (bitmap[blockNo] & (1 << index)) != 0;
  79. }
  80. private static int divide(int x, int y) {
  81. return x >> offSet(y);
  82. }
  83. private static int mod(int x, int y) {
  84. int z = x;
  85. int i = offSet(y);
  86. return z - ((z >> i) << i);
  87. }
  88. //e.g. 32=2^5, return 5 只考虑2的幂
  89. private static int offSet(int y) {
  90. return howManyBits(y) - 1;
  91. }
  92. //二进制的表示里面,有多少位。例如32是6位
  93. private static int howManyBits(int y) {
  94. int bitNum = 0;
  95. while (y != 0) {
  96. y = (y >> 1);
  97. bitNum++;
  98. }
  99. return bitNum;
  100. }
  101. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值