刚开始看见这个题目,觉得很容易,不是疯狂的比较么,于是设计的大致思路如下;
从100个数中拿出1个然后跟98个比较 如果有相同的说明这个没有被拿掉.这样在最极端情况下需要比较 100*98次
首先写一个数据类,产生100个不相同的数,随机拿走两个数,然后把剩余的98个数拿到新的数组里面
package com.koopa.searchNum;
public class DataInit {
private int totalNum;
private int findNum;
private int []totalNums; //存放 n个不同的数字
private int []findNums; //存放取出的m个数
private int remainNums[];//存放取出m个数字后的 n个不同数字
private static DataInit dataInit = null;
private DataInit(int totalNum,int findNum) throws InputErrorException{
if(totalNum<=findNum){
throw new InputErrorException();
}
this.totalNum = totalNum;
this.findNum = findNum;
totalNums = new int[totalNum];
findNums = new int[findNum];
remainNums = new int[totalNum - findNum];
this.init();
}
private void init(){
for(int i = 0;i<totalNum;i++){
totalNums[i] = i;
}
for(int i = 0; i<findNum;i++){
int random = this.getRandom(totalNum);
if(this.isIn(random, findNums)){
i--;
}else{
findNums[i] = random;
System.out.println("remove: "+random);
}
}
int index=0;
for(int i:totalNums){
if(!this.isIn(i, findNums)){
remainNums[index++] = i;
}
}
}
public int getRandom(int max){
int r = (int)(Math.random()*max);
return r;
}
public boolean isIn(int i,int[]a){
for(int j:a){
if(j==i){
return true;
}
}
return false;
}
public static DataInit getDataInit(int totalNum,int findNum) throws InputErrorException{
if(DataInit.dataInit == null){
DataInit.dataInit = new DataInit(totalNum,findNum);
}
return DataInit.dataInit;
}
public static void main(String[] args) {
}
class InputErrorException extends Exception{
private static final long serialVersionUID = 1L;
public InputErrorException(){
super("总的数目不得小于查找数的数目");
}
}
public int getFindNum() {
return findNum;
}
public void setFindNum(int findNum) {
this.findNum = findNum;
}
public int[] getRemainNums() {
return remainNums;
}
public void setRemainNums(int[] remainNums) {
this.remainNums = remainNums;
}
public int getTotalNum() {
return totalNum;
}
public void setTotalNum(int totalNum) {
this.totalNum = totalNum;
}
public int[] getTotalNums() {
return totalNums;
}
public void setTotalNums(int[] totalNums) {
this.totalNums = totalNums;
}
}
为了方便 这里的100个数是按顺序取的.(时候发现,这个顺序差点欺骗了我)
方法1:这也是我最初的想法,一个一个比较
package com.koopa.searchNum;
public class Test1 {
private DataInit dataInit = null;
public Test1(DataInit dataInit){
this.dataInit = dataInit;
}
public void action() {
int findNum = this.dataInit.getFindNum();
int totalNum = this.dataInit.getTotalNum();
int []total = this.dataInit.getTotalNums();
int []remain = this.dataInit.getRemainNums();
long begin = new java.util.Date().getTime();
int num = 0;
for(int i = 0;i<totalNum;i++){
num = total[i];
if(!this.dataInit.isIn(num,remain)){
System.out.println("test1 find: "+num);
findNum--;
if(findNum == 0){
break;
}
}
}
long end = new java.util.Date().getTime();
System.out.println("begin: "+begin);
System.out.println("end: "+end);
System.out.println("time: "+(end-begin)/1000);
}
}
当运行100个数里面找2个时看不出来的... 当输入1W个数找2个时,简直慢了夸张...因为自己都无法接受这个速度,苦思冥想,想到了用数组标记,利用下标来表示被拿走的2个数.
方法2:数组标记
package com.koopa.searchNum;
public class Test2 {
private DataInit dataInit = null;
public Test2(DataInit dataInit){
this.dataInit = dataInit;
}
public void action() {
int findNum = this.dataInit.getFindNum();
int totalNum = this.dataInit.getTotalNum();
int []total = this.dataInit.getTotalNums();
int []remain = this.dataInit.getRemainNums();
long begin = new java.util.Date().getTime();
int []temp = new int[totalNum];
for(int i=0;i<totalNum;i++){
temp[i]=0;
}
for(int i=0;i<totalNum-findNum;i++){
temp[remain[i]]=1;
}
for(int i:total){
if(temp[i]==0){
System.out.println("test2 find: "+i);
findNum--;
if(findNum == 0){
break;
}
}
}
long end = new java.util.Date().getTime();
System.out.println("begin: "+begin);
System.out.println("end: "+end);
System.out.println("time: "+(end-begin)/1000);
}
}
当 DataInit(99999,2)时,用了不到1s 而上面那中方法却用了3K多秒(这个时间是有朋友让我骑车出去接他,接完人后我回到家记得计算机还没关,于是走过来看看,发现刚才的找数计算完成了),本以为到这里时可以了,却发现了新的问题..这里的99999个数是按顺序来的,也就是说数组的下标比最大数小1,如果题目的100个数是从 99999 到 99999+100 而不是自己方便写程序取的0到100,显然方法2出问题了,数组会越界的,要么就得做一个超级大的临时数组标记.那也着实太浪费空间了(虽然现在计算机内存超大).这时想到了面试者的提醒,用HashMap.看看API,和HashMap的源码.原来HashMap相当于是矩阵运算,当往里面添加时,按矩阵排放,当取数时,先确定行.里面有一个array数组,数组里面存放的是一个Entry类,有类似于LinkedList链表的功能.
方法3:用HashMap
package com.koopa.searchNum;
import java.util.HashMap;
public class Test3 {
private DataInit dataInit = null;
public Test3(DataInit dataInit ){
this.dataInit = dataInit;
}
public void action(){
int findNum = this.dataInit.getFindNum();
int totalNum = this.dataInit.getTotalNum();
int []total = this.dataInit.getTotalNums();
int []remain = this.dataInit.getRemainNums();
long begin = new java.util.Date().getTime();
HashMap hm = new HashMap();
for(int i:remain){
hm.put(i,null);
}
for(int i:total){
if(!hm.containsKey(i)){
//System.out.println("test3 find: "+i);
}
}
long end = new java.util.Date().getTime();
System.out.println("time3: "+(end-begin)/1000);
}
}
只需要2s就能在10W个数中找到拿走的2个数.HashMap的原理是首先将key生成hashCode与array数组的length进行与运算,得到一个0到length的确定数,那么确定一个这个key所在的行,当往里面put时,实际上实在该行的Entry链里面添加,从Entry链表前到后逐一比较是否已经有相同的key 有的话更新,没有的话就在最后添加.当进行get时,同样根据hashCode找到数组里面的行,然后逐一比较到Entry的末尾,如果找到返回,否则返回null.
用HashMap的很明显比第2中方法用数组强,不再受里面数的影响了,而且计算时间也很短.如此想来,自己以前的学习太不踏实了,只浏览了个表面,没有深入学习啊.其实这我面试时的一个题目,我当时想到的就是第1中方法,面试者还提示了我用HashMap,结果因为对HashMap的不了解,错过了这道题.其实是错过了很多......