我的java之路1 -- 有100个不相同数,从里面随机拿出两个,现在只剩余98个数了,求找出拿掉的那两个数

这篇博客探讨了一种解决从100个不相同数中找出被拿掉两个数的问题。作者首先介绍了初始的暴力比较方法,然后通过创建数据类生成随机数,并逐步改进算法,使用数组标记法提高效率。最终,通过引入HashMap,实现了在大量数据中快速找到缺失数值的解决方案,这种方法不受数值顺序影响且计算时间短。

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

刚开始看见这个题目,觉得很容易,不是疯狂的比较么,于是设计的大致思路如下;

从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的不了解,错过了这道题.其实是错过了很多......

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值