目录
题目描述
给你三个整数数组 nums1
、nums2
和 nums3
,请你构造并返回一个元素各不相同的数组,且由至少在两个数组中出现的所有值组成。数组中的元素可以按任意顺序排列。
示例 1
输入:nums1 = [1,1,3,2], nums2 = [2,3], nums3 = [3] 输出:[3,2] 解释:至少在两个数组中出现的所有值为: - 3 ,在全部三个数组中都出现过。 - 2 ,在数组 nums1 和 nums2 中出现过。示例 2
输入:nums1 = [3,1], nums2 = [2,3], nums3 = [1,2] 输出:[2,3,1] 解释:至少在两个数组中出现的所有值为: - 2 ,在数组 nums2 和 nums3 中出现过。 - 3 ,在数组 nums1 和 nums2 中出现过。 - 1 ,在数组 nums1 和 nums3 中出现过。示例 3
输入:nums1 = [1,2,2], nums2 = [4,3,3], nums3 = [5] 输出:[] 解释:不存在至少在两个数组中出现的值。提示
1 <= nums1.length, nums2.length, nums3.length <= 100
1 <= nums1[i], nums2[j], nums3[k] <= 100
思路解析
题目给出三个整数数组 nums1、nums2 和 nums3。现在我们需要求一个元素各不相同的数组,其中的元素为至少在数组 nums1、nums2 和 nums3 中两个数组出现的全部元素。
我们可以用「哈希表」来实现——由于只有三个数组,所以我们一个整数的最低三个二进制位来标记某一个数在哪几个数组中,1 表示该数在对应的数组中的,反之 0 表示不在。最后我们只需要判断每一个数对应的标记数字中二进制位个数是否大于 1 即可。
下面我们介绍一下本题代码用到的几个知识点:
map.getOrDefault()
在JDK8之后,对 map 新增了 getOrDefault() 方法
格式:
Map.getOrDefault(key,默认值);
Map中会存储一一对应的 key 和 value。
如果在 Map 中存在 key,则返回 key 所对应的的 value。
如果在 Map 中不存在 key,则返回默认值。
public class Demo {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("四宫辉夜", 20);
map.put("凛凛蝶", 24);
map.put("远坂凛", 23);
String age= map.getOrDefault("四宫辉夜", 30);
System.out.println(age);// 20,map中存在"四宫辉夜",使用其对应值20
String age = map.getOrDefault("雪之下雪乃", 25);
System.out.println(age);// 25,map中不存在"雪之下雪乃",使用默认值25
}
}
v & (v - 1)
根据这个二级制数如何判断是否出现 2 次以上,代码巧妙采用了 v&(v-1) 这个公式,它的作用是将 v 的二进制的最右边的一个 1 变为 0,例如 10010100 进行该操作后变成 10010000,所以只有在经历过一个该操作后依旧大于 0 则说明至少在两个数组中出现过,则可以放入答案列表。
这是一个很重要的计算公式,需要牢记哦
Map.Entry()
Map 是 Java 中的接口,Map.Entry 是 Map 的一个内部接口。
Map 提供了一些常用方法,如 keySet()、entrySet() 等方法,keySet() 方法返回值是 Map 中 key 值的集合;entrySet() 的返回值也是返回一个 Set 集合,此集合的类型为 Map.Entry。
Map.Entry 是 Map 声明的一个内部接口,此接口为泛型,定义为 Entry<K,V>。它表示 Map 中的一个实体(一个 key-value 对)。接口中有 getKey(),getValue 方法。
解题代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyTest {
public static void main(String[] args) {
MyTest m = new MyTest();
int[] nums1 = {1, 1, 3, 2};
int[] nums2 = {2, 3};
int[] nums3 = {3};
System.out.println(m.twoOutOfThree(nums1, nums2, nums3));
}
public List<Integer> twoOutOfThree(int[] nums1, int[] nums2, int[] nums3) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i : nums1) {
map.put(i, 1);
}
for (int i : nums2) {
map.put(i, map.getOrDefault(i, 0) | 2);
}
for (int i : nums3) {
map.put(i, map.getOrDefault(i, 0) | 4);
}
List<Integer> res = new ArrayList<Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int k = entry.getKey(), v = entry.getValue();
if ((v & (v - 1)) != 0) {
res.add(k);
}
}
return res;
}
}
首先第一个循环,我们把 nums1 内的值出现次数记为 1 ,放入 map ,第二个循环我们查看是否 nums2 中的值也在 nums1 中出现,具体用到 Map.getOrDefault() 函数,如果值存在,则 1 | 2,不存在则 0 | 2,第三个循环同理。
解释一下为什么 | 2 和 | 4,1 的二进制是 001,也就是说如果数在第一个数组中存在,我们就把它放到三位中的第三位上,2 的二进制是 010,也就是说如果数在第二个数组中存在,我们就把它放到三位中的第二位上,4 的二进制是 100,也就是说如果数在第三个数组中存在,我们就把它放到三位中的第一位上。
然而 | 运算的运算法则是,0|0=0; 0|1=1; 1|0=1; 1|1=1;
即:参加运算的两个对象只要有一个为 1,其值为 1。
例如:3|4 即 00000011 | 0000 0100 = 00000111 因此,3|4 的值得 7。
另,负数按补码形式参加按位或运算。
所以我们就可以知道,如果在第二个数组中又出现了第一个数组中的值,则 001 | 010 = 011,以此类推,我们就可以把数字出现的状态记录下来。
最后使用 v&(v-1) 计算出现的次数,如果出现 1 次,001&000=000,值即为 0,出现两次,011&010=010,值不为 0。