如何解决增强Java中获取数组中元素最高出现次数的方法
我在面试时遇到了以下问题。
问题: 您需要找到数组中出现频率最高的元素。该数组由整数组成。例如,如果你有一个这样的整数序列:1,2,9,3,4,1,5,8,2,解将是字符串:
获胜者是第 3 名,其出现频率为 6
这个问题的答案在面试过程中得到了改进。
以下是源代码:
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
public class RepeatedElement {
public static void main(String[] args) {
int[] arr = {1,2};
Arrays.sort(arr);
int len = arr.length;
int count = 1;
Map<Integer,Integer> map = new TreeMap<>();
int i=0;
while(i < len) {
for(int j=i+1;j<len;j++) {
if(arr[i]==arr[j]) {
count++;
i++;
}else {
break;
}
}
map.put(arr[i],count);
count = 1;
i++;
}
int max = 0;
int key = 0;
for(int k:map.keySet()) {
int temp = map.get(k);
if(temp>max) {
max = temp;
key = k;
}
}
System.out.println("Winner is: "+key+" and it's occurrences: "+max);
}
}
我也在 stackoverflow 中搜索了其他类似的问题。我只是想了解这是否是解决此问题的好方法。我相信代码可以更好地解释我尝试过的方法。同时,我很想知道将提供更大输入的时间和空间复杂性。伙计们,如果它有一些缺陷,请提供您的想法来改进此代码。提前致谢。
解决方法
您可以使用 Map 中的计算函数将第一个循环简化为如下所示:
for (int i=0; i < arr.length; i++) {
map.compute(arr[i],(k,v) -> v == null ? 1 : v+1);
}
或者,如果您对 3rd 方库开放,则可以使用 Bag 中的 Eclipse Collections 类型,并将整个方法替换为以下内容:
public static void main(String[] args) {
Integer[] arr = {1,2,9,3,4,1,5,8,2};
MutableBag<Integer> bag = Bags.mutable.of(arr);
ObjectIntPair<Integer> top = bag.topOccurrences(1).getFirst();
System.out.println("Winner is: "+top.getOne()+" and it's occurrences: "+ top.getTwo());
}
甚至还有一个 PrimitiveBag 类型来避免 int
的装箱:
public static void main(String[] args) {
int[] arr = {1,2};
MutableIntBag bag = IntBags.mutable.of(arr);
IntIntPair top = bag.topOccurrences(1).getFirst();
System.out.println("Winner is: "+top.getOne()+" and it's occurrences: "+ top.getTwo());
}
,
从代码中的这个语句 map.put(arr[i],count);
来看,您似乎没有认识到可以使用现有值更新地图中现有键的事实。这就是为什么您似乎通过遍历所有键来计算元素的原因。
您可以在再次看到密钥时使用 map.put(arr[i],map.get(arr[i]) + 1)
更新密钥的计数。
此外,您不需要再次遍历地图的 keySet 以获得最大频率,您可以在填充地图本身时获得它。
代码将是:
public static void main(String args[]) {
int[] arr = {1,2};
Map<Integer,Integer> map = new HashMap<>();
int max = 1;
int maxKey = arr[0];
for ( int k : arr ) {
map.put(k,map.getOrDefault(k,0) + 1);
if ( map.get(k) > max ) {
max = map.get(k);
maxKey = k;
}
}
System.out.println("Winner is: "+ maxKey+" and it's occurrences: "+max);
}
,
你正在做一个排序:不需要引入时间复杂度 >= O(NLogN)
也不需要使用TreeMap。使用 HashMap 来将 get 或 put 操作减少到常量而不是 log(N)。
您可以合并两个循环为一个。
以下解决方案的复杂性:
- 以下代码的时间复杂度为 O(N)。
- 空间复杂度为 O(N)。
使用 map.compute (Java 8+) 增加现有频率计数。这样你就不需要
- 首先获取现有值,
- 检查它是否存在,如果它确实增加或如果不存在则将其设置为 1。
您也可以使用 map.merge (Java 8+) 操作。
merge 和 compute 的示例在下面的代码中都有说明。
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
public class MostFrequentElementInArray {
public static void main(String[] args) {
int[] arr = { 1,2 };
int len = arr.length;
int mostFreqKey = arr[0];
Map<Integer,Integer> map = new HashMap<>();
for (int elem : arr) {
// you can use any of the 3:
int newVal = map.compute(elem,(K,oldVal) -> oldVal == null ? 1 : oldVal + 1 );
// int newVal = map.merge(elem,(oldVal,valWhichIs1) -> oldVal + valWhichIs1);
// int newVal = map.merge(elem,Integer::sum);
if (map.get(mostFreqKey) < newVal) {
// this is the new frequently occurring element
mostFreqKey = elem;
}
}
System.out.println("Winner is: " + mostFreqKey + " and it's occurrences: " + map.get(mostFreqKey));
}
}
,
使用哈希表记录每个整数的计数。空间和时间复杂度都是 O(N)。遍历数组中的每个元素并将其插入到哈希映射中,数组 [i] 作为键,如果不存在则值为 1。当您看到它已经存在时,只需增加该值。然后在此之后的另一个循环获得最大值键。
您的解决方案类似,但请注意,在使用 Map 对象时无需进行排序。当您使用 Map 时,无需从当前索引中查找相似元素。你的解决方案会让面试官觉得你没有完全理解地图的概念。还要注意排序会让你的方法变成 O(N logN)。从面试的角度来看,我确信这个问题是为了测试你对哈希表的理解。
这是我的方法:
public static void main(String[] args) {
int[] arr = {1,2};
Map<Integer,Integer> map = new HashMap<>();
for (int i=0; i < arr.length; i++) {
if(map.containsKey(arr[i])) {
map.put(arr[i],map.get(arr[i]) + 1);
} else {
map.put(arr[i],1);
}
}
int max = 0;
int key = 0;
int temp;
for(int k: map.keySet()) {
temp = map.get(k);
if(temp > max) {
max = temp;
key = k;
}
}
System.out.println("Winner is: "+key+" and it's occurrences: "+max);
}
,
以下代码是我能想到的最优化的方式。它不仅大大缩短了代码,而且还以相同的时间复杂度处理信息。复杂性并不重要,使用这种方法,我们只迭代一个数据结构一次。
int[] arr = {1,2};
Map<Integer,Integer> occurrenceMap = new HashMap<>();
int winnerNumber = -1;
int maxOccurrence = 0;
for (int i : arr) {
if (occurrenceMap.merge(i,Integer::sum) > maxOccurrence) {
winnerNumber = i; maxOccurrence = occurrenceMap.get(i);
}
}
System.out.format("The winner is number " + winnerNumber
+ ",its frequency of occurrence is " + maxOccurrence + ".");
,
呈现的代码有点复杂,因为它对输入数组进行排序,然后使用嵌套循环计算排序映射中的频率,然后找到映射中的最大频率。
如果使用地图来计算频率,则预排序是多余的。去掉它就可以删除初始算法中最复杂的部分 O(N log N)
。
此外,在迭代输入数组时,应立即识别具有最大频率的值。
接下来,由于 Java 8 Map
具有诸如 Map::compute
和 Map::merge
之类的方法来方便修改映射中的值,在这种情况下是增量。
话虽如此,代码可以重构为:
public static void main(String args[]) {
int[] arr = {1,2};
Map<Integer,Integer> freqMap = new HashMap<>();
Integer mostFrequent = null;
Integer highestFreq = null;
for (int x : arr) {
Integer currFreq = freqMap.compute(x,v) -> null == v ? 1 : v + 1);
// Integer currFreq = freqMap.merge(x,Integer::sum); // merge example
if (null == mostFrequent || currFreq > highestFreq) {
mostFrequent = x;
highestFreq = currFreq;
}
}
System.out.printf("Winner is %d and its occurrences %d%n",mostFrequent,highestFreq);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。