有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。写一个函数实现。复杂度是什么。
2013-02-25 20:26
721 查看
Java代码
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
/**
* http://weibo.com/1915548291/z7HtOF4sx * #面试题#有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。
* 写一个函数实现。复杂度是什么。
*
* 我认为这道题的考点是如何节省空间。
* 当然可以用一个与原数组长度相同的数组来标记对应元素是否被取过,但这样太浪费空间
* 用bitmap的话,只要一个bit就可以标记是否被取过,可参考《编程珠玑》的位图排序
*
* 时间复杂度的话,我不太会算,以下是引用https://github.com/vyan/test/blob/master/accessTimes.cpp:
* 使用bit打点记录已经取的数,
* 复杂度分析,假设数组总长度为n
* 取到第1个之前未被取到的数的期望 E(1)=1
* 取到第2个之前未被取到的数的期望 E(2)=n/n-1
* 取到第3个之前未被取到的数的期望 E(3)=n/n-2
* ...
* 取到第n个之前未被取到的数的期望 E(n)=n/1
* 总得期望次数E=n+n/(n-1)+n/(n-2)+...+n/1;
* =n(1+1/(n-1)+1/(n-2)+...+1/1)
* =nln(n)
* 所以算法平均复杂度为nlogn
*
* 下面的代码里面,除法和求模运算我都用位运算来实现(为了练习位运算),事实上直接用java提供的(/,%)也可以
* 同时,为了验证是否真的取到了数组的所有元素,我用了TreeSet保存已选中的下标(去重)
*
* 最后,还有一个问题是,可能取了很多次,都没能全选中,这个时候可以设置一个最长时间或者最大尝试次数,超过则结束程序,
* 避免程序长时间运行甚至死循环
*
* @author lijinnan
*
*/
public class TimesOfAccessArray {
public static
final int SHIFT = 5;
public static
final int BLOCK_SIZE = (1 << SHIFT);
//32
public static
final int MASK = (1 << SHIFT) -
1; //31
public static
void main(String[] args) {
int[] array = new
int[200];
long times = accessTimes(array);
System.out.println(times);
}
public static
long accessTimes(int[] array) {
if (array == null || array.length ==
0) {
return -1L;
}
long result = 0L;
int len = array.length;
int[] bitmap = new
int[divide(len, BLOCK_SIZE) + 1];
int setTimes = 0;
Set<Integer> set = new TreeSet<Integer>();
while (setTimes < len) {
int pos = new Random().nextInt(len);
set.add(pos);
if (set(bitmap, pos)) {
setTimes++;
}
result++;
}
System.out.println(set);
return result;
}
public static
boolean set(int[] bitmap,
int pos) {
boolean result = false;
int blockNo = divide(pos, BLOCK_SIZE);
int index = mod(pos, BLOCK_SIZE);
boolean notExist = (bitmap[blockNo] & (1 << index))==
0;
if (notExist) {
bitmap[blockNo] |= (1 << index);
result = true;
}
return result;
}
public static
boolean exist(int[] bitmap,
int pos) {
int blockNo = divide(pos, BLOCK_SIZE);
int index = mod(pos, BLOCK_SIZE);
return (bitmap[blockNo] & (1 << index)) !=
0;
}
private static
int divide(int x, int y) {
return x >> offSet(y);
}
private static
int mod(int x, int y) {
int z = x;
int i = offSet(y);
return z - ((z >> i) << i);
}
//e.g. 32=2^5, return 5 只考虑2的幂
private static
int offSet(int y) {
return howManyBits(y) - 1;
}
//二进制的表示里面,有多少位。例如32是6位
private static
int howManyBits(int y) {
int bitNum = 0;
while (y != 0) {
y = (y >> 1);
bitNum++;
}
return bitNum;
}
}
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
/**
* http://weibo.com/1915548291/z7HtOF4sx * #面试题#有一个数组,每次从中间随机取一个,然后放回去,当所有的元素都被取过,返回总共的取的次数。
* 写一个函数实现。复杂度是什么。
*
* 我认为这道题的考点是如何节省空间。
* 当然可以用一个与原数组长度相同的数组来标记对应元素是否被取过,但这样太浪费空间
* 用bitmap的话,只要一个bit就可以标记是否被取过,可参考《编程珠玑》的位图排序
*
* 时间复杂度的话,我不太会算,以下是引用https://github.com/vyan/test/blob/master/accessTimes.cpp:
* 使用bit打点记录已经取的数,
* 复杂度分析,假设数组总长度为n
* 取到第1个之前未被取到的数的期望 E(1)=1
* 取到第2个之前未被取到的数的期望 E(2)=n/n-1
* 取到第3个之前未被取到的数的期望 E(3)=n/n-2
* ...
* 取到第n个之前未被取到的数的期望 E(n)=n/1
* 总得期望次数E=n+n/(n-1)+n/(n-2)+...+n/1;
* =n(1+1/(n-1)+1/(n-2)+...+1/1)
* =nln(n)
* 所以算法平均复杂度为nlogn
*
* 下面的代码里面,除法和求模运算我都用位运算来实现(为了练习位运算),事实上直接用java提供的(/,%)也可以
* 同时,为了验证是否真的取到了数组的所有元素,我用了TreeSet保存已选中的下标(去重)
*
* 最后,还有一个问题是,可能取了很多次,都没能全选中,这个时候可以设置一个最长时间或者最大尝试次数,超过则结束程序,
* 避免程序长时间运行甚至死循环
*
* @author lijinnan
*
*/
public class TimesOfAccessArray {
public static
final int SHIFT = 5;
public static
final int BLOCK_SIZE = (1 << SHIFT);
//32
public static
final int MASK = (1 << SHIFT) -
1; //31
public static
void main(String[] args) {
int[] array = new
int[200];
long times = accessTimes(array);
System.out.println(times);
}
public static
long accessTimes(int[] array) {
if (array == null || array.length ==
0) {
return -1L;
}
long result = 0L;
int len = array.length;
int[] bitmap = new
int[divide(len, BLOCK_SIZE) + 1];
int setTimes = 0;
Set<Integer> set = new TreeSet<Integer>();
while (setTimes < len) {
int pos = new Random().nextInt(len);
set.add(pos);
if (set(bitmap, pos)) {
setTimes++;
}
result++;
}
System.out.println(set);
return result;
}
public static
boolean set(int[] bitmap,
int pos) {
boolean result = false;
int blockNo = divide(pos, BLOCK_SIZE);
int index = mod(pos, BLOCK_SIZE);
boolean notExist = (bitmap[blockNo] & (1 << index))==
0;
if (notExist) {
bitmap[blockNo] |= (1 << index);
result = true;
}
return result;
}
public static
boolean exist(int[] bitmap,
int pos) {
int blockNo = divide(pos, BLOCK_SIZE);
int index = mod(pos, BLOCK_SIZE);
return (bitmap[blockNo] & (1 << index)) !=
0;
}
private static
int divide(int x, int y) {
return x >> offSet(y);
}
private static
int mod(int x, int y) {
int z = x;
int i = offSet(y);
return z - ((z >> i) << i);
}
//e.g. 32=2^5, return 5 只考虑2的幂
private static
int offSet(int y) {
return howManyBits(y) - 1;
}
//二进制的表示里面,有多少位。例如32是6位
private static
int howManyBits(int y) {
int bitNum = 0;
while (y != 0) {
y = (y >> 1);
bitNum++;
}
return bitNum;
}
}
相关文章推荐
- .定义两个数组,首先把两个数组合并成一个新数组,然后把新数组中的所有元素逆序排列,需要实现的效果如图-2所示。
- JavaScript数组对象实现增加一个返回随机元素的方法
- Java编程:定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,然后求出所有元素的最大值,最小值,平均值,和值,并输出出来。
- 实现一个容器,每次调用select()方法时,随机返回一个元素
- 用js实现随机返回数组的一个元素
- 设计函数返回一个数组中所有元素被第一个元素除的结果
- 使用jQuery匹配文档中所有的li元素,返回一个jQuery对象,然后通过数组下标的方式读取jQuery集合中第1个DOM元素,此时返回的是DOM对象,然后调用DOM属性innerHTML,读取该元素 包含的文本信息
- [编程之美]写一个函数,返回一个数组中所有元素被第一个元素除的结果
- 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,然后求出所有元素的最大值,最小值,平均值,和值,并输出出来。
- 写一个函数,返回一个数组中所有元素被第一个元素相除的结果
- 编程之美 - 写一个函数,返回数组中所有元素被第一个元素除的结果
- 笔试题目1:写一个函数,返回一个数组中所有元素被第一个元素除的结果
- 用js实现随机返回数组的一个元素
- 一道值得深思的面试题:写一个函数,返回一个数组中所有元素被第一个元素除的结果。
- 编程之美_001写一个函数,返回一个数组中所有元素被第一个元素除的结果
- 面试题-->写一个函数,返回一个数组中所有元素被第一个元素除的结果
- .定义两个数组,首先把两个数组合并成一个新数组,然后把新数组中的所有元素逆序排列,需要实现的效果如图-2所示。
- 写一个函数实现数组中的元素随机乱序排序
- 定义一个int型的一维数组,包含10个元素,分别赋一些随机整数,然后求出所有元素的最大值,最小值,平均值,和值,并输出出来。
- [编程之美]写一个函数,返回一个数组中所有元素被第一个元素除的结果