背景

工作多年,语言经历过C#,JAVA。但是做过的项目大多以业务系统为主,曾经做过一些基础架构的工作,但算法一直在工作中应用的比较少,导致多年之后基本都忘记完了。上一次面试过程中就有一个算法题,我能做对,但是感觉不是最优方案就放弃了。最近想想做为一个程序员,算法还是有必要再补习补习。

案例

有两个数组,int[] arrayA=new int[]{1,3,1.....},int[] arrayB=new int[]{11,3,10.....},数组元素无序且有可能存在重复元素,请输出两个数组的交集。原题大意是这样,细节可能有出入。

面试时我的方案

不用想,采用两个for循环基本就能解决问题,但我又想不出来其它优化方法,想来想去,时间白白浪费最后居然连能做对的答案都没去写。

public void testArrayIntersectionA() {

int[] arrayA = new int[]{1, 1, 2, 3, 4, 4, 5, 1, 1};

int[] arrayB = new int[]{11, 1, 22, 3, 43, 4, 5, 11, 1, 22};

Set intersectionSet = new HashSet();

for (int i = 0; i < arrayA.length; i++) {

for (int j = 0; j < arrayB.length; j++) {

if (arrayA[i] == arrayB[j]) {

intersectionSet.add(arrayA[i]);

}

}

}

}

当时曾经想过将数组排序然后比较,但放弃了,感觉增加了排序之后性能会不一定比上面的两层for要优化。思路如下:

排序原数组

选择数组元素小的数组去与大数组做比较

验证上面的指针比较法

比如有这样的两个数组:

具体的做法如下:

排序数组

初始化两数组的指针,均从0开始

将小数组的指针做为外层循环,在大数组中以大数组指针位置开始比较

如果找到相等的,记录结果,同时将大小数组的指针向后移动

如果在大数组中找到末尾都没有找到,那么小数组的指针向后移动

当小数组的指针移动到最后一个元素后结束算法

public void testArrayIntersectionB() {

int[] arrayA = new int[]{1, 1, 2, 3, 4, 4, 5, 1, 1};

int[] arrayB = new int[]{11, 1, 22, 3, 43, 4, 5, 11, 1, 22};

Set intersectionSet = new HashSet();

Arrays.sort(arrayA);

Arrays.sort(arrayB);

int indexArrayA = 0;

int indexArrayB = 0;

int sizeArrayA = arrayA.length;

int sizeArrayB = arrayB.length;

while (indexArrayA < sizeArrayA) {

for (int i = indexArrayB; i < sizeArrayB; i++) {

if (arrayA[indexArrayA] == arrayB[i]) {

intersectionSet.add(arrayA[indexArrayA]);

indexArrayA++;

indexArrayB++;

break;

} else if (i == sizeArrayB - 1) {

indexArrayA++;

}

}

}

}

为了测试的准确性,可以将数组的元素增多,文中只是示意的写了几个元素,实际测试过程中可以增大元素个数。同时将方法重复执行500次或者更多来测试。得到的结论是排序之后的指针方法要快于简单的两层for,具体的数据我就不贴了,因为与数组元素的组成有一定的关系。

指针比较法的优化

上面的逻辑是,从大数组的某个位置开始比较至到数组的最后一个元素,但因为我们的数组已经经过排序,实际上我们只需要比较到第一个大于的数就可以结束比较,因为后面的元素一定比前面的元素要大。

public void testArrayIntersectionC() {

int[] arrayA = new int[]{1, 1, 2, 3, 4, 4, 5, 1, 1};

int[] arrayB = new int[]{11, 1, 22, 3, 43, 4, 5, 11, 1, 22};

Set intersectionSet = new HashSet();

Arrays.sort(arrayA);

Arrays.sort(arrayB);

int indexArrayA = 0;

int indexArrayB = 0;

int sizeArrayA = arrayA.length;

int sizeArrayB = arrayB.length;

while (indexArrayA < sizeArrayA) {

for (int i = indexArrayB; i < sizeArrayB; i++) {

if (arrayA[indexArrayA] == arrayB[i]) {

intersectionSet.add(arrayA[indexArrayA]);

indexArrayA++;

indexArrayB++;

break;

} else if (arrayA[indexArrayA] < arrayB[i]) {

indexArrayA++;

break;

} else if (i == sizeArrayB - 1) {

indexArrayA++;

}

}

}

}

测试结论是此方法优化有效,特别是在特定的数据场景下。

利用java已有结构Set如何?

继承了Collection接口的,包含一个retainAll的方法,我们利用Set可以非常轻松的来完成两个数组的交集。但它只能处理对象类型的Integer,所以我们先要将int[] 转换成Integer[],然后利用addAll以及retailAll来计算数组的交集。

public void testArrayIntersectionD() {

int[] arrayA = new int[]{1, 1, 2, 3, 4, 4, 5, 1, 1};

int[] arrayB = new int[]{11, 1, 22, 3, 43, 4, 5, 11, 1, 22};

int sizeArrayA=arrayA.length;

int sizeArrayB=arrayB.length;

Integer[] arrayA2=new Integer[sizeArrayA];

Integer[] arrayB2=new Integer[sizeArrayB];

for(int i=0;i

arrayA2[i]=new Integer(arrayA[i]);

}

for(int i=0;i

arrayB2[i]=new Integer(arrayB[i]);

}

Set intersectionSet = new HashSet();

intersectionSet.addAll(Arrays.asList(arrayA2));

intersectionSet.retainAll(Arrays.asList(arrayB2));

}

同样也是执行500次,利用Set求交集的性能最好。下面是retainAll的源码:应该是利用了遍历最快的迭代器的原因,后续再找时间求证下。

public boolean retainAll(Collection> c) {

Objects.requireNonNull(c);

boolean modified = false;

Iterator it = iterator();

while (it.hasNext()) {

if (!c.contains(it.next())) {

it.remove();

modified = true;

}

}

return modified;

}

利用队列  (此方法有数量级的优势,比较的数组元素扩大到随机生成的10000个int)

将原数组进行排序,然后将数组加入到队列中,拿元素个数较小的做为循环条件,比较两个队列peek数值。相等则输出并出队列,否则将较小值所在的队列进行出队列操作至到某个队列为空结束循环。

public void testArrayIntersectionE(int[] arrayA,int[] arrayB) {

int sizeArrayA=arrayA.length;

int sizeArrayB=arrayB.length;

Arrays.sort(arrayA);

Arrays.sort(arrayB);

Queue queueA=new ArrayBlockingQueue(sizeArrayA);

Queue queueB=new ArrayBlockingQueue(sizeArrayB);

for(int i=0;i

queueA.add(arrayA[i]);

}

for(int i=0;i

queueB.add(arrayB[i]);

}

Set intersectionSet = new HashSet();

while (!queueA.isEmpty()){

Integer valueA=queueA.peek();

Integer valueB=queueB.peek();

if(null==valueA||null==valueB){

break;

}

if(valueA.equals(valueB)){

intersectionSet.add(valueA);

queueA.poll();

queueB.poll();

}

else if(valueA>valueB){

queueB.poll();

}

else if(valueA

queueA.poll();

}

}

}

示意过程如下:

扩展问题,如果数组不是int[],而直接是Integer[],数据结果会有变化吗?

上面有提到,当时面试时我考虑的是数组排序,经过测试int[]的排序要快于Integer[]排序,数组的复制也是一样。这在一定程序上会引起测试结果的变化。同时数组内元素的内容也会影响测试结果。

是否有更好的方案?

java计算两个数组的交集_回顾面试题:计算两个数组交集相关推荐

  1. python数组切片教程_手把手numpy教程【二】——数组与切片

    今天是Numpy专题的第二篇,我们来进入正题,来看看Numpy的运算. 上一篇文章当中曾经提到过,同样大小的数据,使用Numpy的运算速度会是我们自己写循环来计算的上百倍甚至更多.并且Numpy的AP ...

  2. php 统计二维数组次数最多_前端面试题(数组篇)

    前端面试中,数组是少不了的.因为数组使用度比较频繁,我在项目中就经常使用.JavaScript拼接Html,数据结构计算,等等. 汇总一下面试中数组会问到的几个问题,这个问题在面试经常被问到,但是一般 ...

  3. android给数组添加新元素_重磅!超详细的 JS 数组方法整理出来了

    作者:Yushiahttps://juejin.cn/post/6907109642917117965 数组是 js 中最常用到的数据集合,其内置的方法有很多,熟练掌握这些方法,可以有效的提高我们的工 ...

  4. c#数组赋初值_【自学C#】|| 笔记 13 数组

    上一章最后讲的是正则表达式做一下总结. 总之就是通过"/d"这样的符号代替数字或字符什么的,然后通过"{n}"这种方式确定重复多少个,再通过"|&qu ...

  5. excel两个表格数据对比_快速对比excel表格两个sheet中不同的数据,极大的降低重复工作量...

    两个Excel表格应该比较,公式不会被设置,而vba是一本天书.有简单的方法吗? 有!有!有!!! Excel有一个不是所有人都常用的功能:合并计算.使用它,我们可以快速比较两个表之间的差异. 示例: ...

  6. opencv计算两数组的乘积_#剑指Offer#12. 构建乘积数组

    题目描述:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1]. 其中B中的元素B[i]=A[0] * A[1]... * A[i-1] * A[i+1]... * A ...

  7. 如何快速找出找出两个数组中的_找出JavaScript中两个数组之间的差异

    LeetCode今天面临的挑战是在数组中查找所有消失的数字. 蛮力 我们的输入包括一个缺少数字的实际数组.我们想将该数组与相同长度的数组进行比较,其中没有遗漏的数字.所以如果给定的话[4,3,2,7, ...

  8. c++ 取两个链表的交集_使用C ++程序查找两个链表的交集

    c++ 取两个链表的交集 Problem statement: Write a C++ program to find the intersection of two single linked li ...

  9. python求两数之和的命令_数学建模:科学计算Python2小时-Python基础

    这一部分主要面向数模活动中的python基础知识进行讨论 作者 系列文章(科学计算Python2小时)目录:李似:科学计算Python2小时-前言与目录​zhuanlan.zhihu.com 首先要说 ...

最新文章

  1. 【C++】【四】企业链表
  2. Beep()之我迷糊了……
  3. msf principle
  4. mysql教程 api_Mysql入门系列:MySQL可用的API
  5. centos+php+nginx的php.ini无法加载的问题
  6. ch4 MySQL 安全管理
  7. 算法应用一:【指纹识别】+【图像分割】一种面向“感兴趣区域”的指纹图像分割算法
  8. 解决Vue的表格中,expand只有某些行需要展开的问题。
  9. Android 手机和盒子遥控器
  10. 级联引用完整性约束ON DELETE { NO ACTION | CASCADE | SET NULL | SET DEFAULT }
  11. 阿里旗下的咸鱼是什么盈利模式?很多人真的不知道!
  12. 电商系统购物车设计思路
  13. 小米登录协议分析_小米智能家居设备流量分析及脚本控制
  14. 微型计算机百度云,STONE_百度云资源_盘多多如风搜_盘搜搜_哎哟喂啊
  15. win10怎样获得计算机管理员权限,详细教你获取win10管理员权限
  16. jQuery获取、设置标签属性值
  17. 什么是绩效管理?企业如何做好绩效管理
  18. 如何写个优秀的Github项目Readme文档?经典模版拿去不谢~
  19. 跟踪`极点`五笔变半行.顺便学习Process Monitor的使用
  20. 当前时间实时刷新js

热门文章

  1. java 继承 封装 多态 详解
  2. 揭晓AI算力池化的五大场景
  3. Python官方软件包存储库成恶意软件大本营?
  4. Kotlin 1.5 新特性:密封接口有啥用?
  5. 深度剖析 Linux 的 3 种“拷贝”命令
  6. 滴滴回应高额抽成:确实存在;抖音火山版被判赔腾讯 800 万元;华为鸿蒙系统有望下月规模化推送|极客头条...
  7. Rust 越来越香了!AWS 雇佣 Rust 编译器团队负责人 Felix Klock
  8. 疫情之后,人工智能该如何走?
  9. 你正在学 Web 自动化测试?Selenium 基本操作你了解嘛? | 原力计划
  10. 面对疫情等群体性危机,程序员如何在家高效办公?