问题描述
- 给定一个长度为n的整型数组,已知其中一个数字的出现次数超过数组长度的一半,找出这个元素
输入格式
- 一个长度为n的数组,其中某个元素的出现次数大于n/2
输出格式
- 一个整数
输入样例
- [1,3,8,2,3,1,3,3,3]
输出样例
- 3
数据范围
- 任意长度为n整数数组,其中某个元素的出现次数大于n/2
思路分析
对于该问题我想到了两种基本的解题方式:
1.使用计数数组,首先便利原数组Array求得元素最大的值Max,以方便后续建立一个新数组NewArray[max],新数组NewArray的下标为原数组中的元素,然后重新遍历原数组Array,执行语句NewArray[Array[i]]++语句,在新数组NewArrra中记录原数组Array元素的个数,最后进行遍历新数组NewArray,能够得到最大元素(个数)的下标(原初始数据),记作ans,最后将ans返回,这种方法很有局限性,因为如果说数组中的最大的数字和其他数字差距很大,则会造成新创建的计数数组浪费大量的空间,并且也是很费时间
代码如下:
public class Main {
public static int solution(int[] array) {
if (array == null || array.length == 0) {
return -1; // 返回-1表示输入无效
}
// 找出最大值
int max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
// 创建一个计数数组,统计每个数字出现的次数
int[] count = new int[max + 1]; // 假设数组中的值从0到max之间
for (int i = 0; i < array.length; i++) {
count[array[i]]++;
}
// 找到出现次数最多的元素
int result = array[0];
int maxCount = count[result]; // 初始化为第一个元素的计数
for (int i = 1; i < count.length; i++) {
if (count[i] > maxCount) {
maxCount = count[i];
result = i; // 更新结果为当前出现次数最多的元素
}
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{4, 5, 6, 4, 4, 6}) == 4);
System.out.println(solution(new int[]{7, 7, 7, 2, 2, 3}) == 7);
System.out.println(solution(new int[]{1, 1, 1, 1, 1}) == 1);
}
}
2.利用java中Map中HashMap类,通过 HashMap 来记录数组中每个元素出现的次数,最终遍历这个 HashMap 来找出出现次数最多的元素。这种方法对数组中的元素没有限制,可以处理任意大小的数字。
- 优点:能够处理任意范围的元素,数组中的元素不需要是连续的整数。
- 缺点:空间复杂度相对较高,需要额外的存储空间来保存
HashMap,适用于元素数量比较多的情况。
代码如下:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static int solution(int[] array) {
if (array == null || array.length == 0) {
return -1; // 返回-1表示输入无效
}
// 使用HashMap来统计每个元素的出现次数
Map<Integer, Integer> frequencyMap = new HashMap<>();
for (int num : array) {
frequencyMap.put(num, frequencyMap.getOrDefault(num, 0) + 1);
}
// 遍历频率Map,找到出现次数最多的元素
int result = array[0];
int maxCount = frequencyMap.get(result);
for (Map.Entry<Integer, Integer> entry : frequencyMap.entrySet()) {
if (entry.getValue() > maxCount) {
maxCount = entry.getValue();
result = entry.getKey();
}
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{4, 5, 6, 4, 4, 6}) == 4);
System.out.println(solution(new int[]{7, 7, 7, 2, 2, 3}) == 7);
System.out.println(solution(new int[]{1, 1, 1, 1, 1}) == 1);
}
}
3.排序法,首先你可以通过对数组Array进行排序,然后再一次扫描一次已经排序后的数组,从排序好的数组中找出出现最多的元素。通过排序,元素相同的部分会在一起,然后我们可以定义一个变量result来代表结果,让Array[0]元素赋值给result,再定义新的变量MaxCount和CurrentCount,一个代表最大数量,一个代表当前数字数量,然后遍历已经排序过的数组Array,如果该元素和其后一个元素相同,CurrentCount++,反之CurrentCount=1,然后进行判断当前元素的数量CurrentCount和当前最大元素个数MaxCount比较,最终将result返回
- 优点:实现简单,适用于数据量较小的情况。通过排序后,复杂度较低。
- 缺点:需要 O(n log n) 的时间复杂度来排序,对于大规模数据集,效率较低。
代码如下:
import java.util.Arrays;
public class Main {
public static int solution(int[] array) {
if (array == null || array.length == 0) {
return -1; // 返回-1表示输入无效
}
// 对数组进行排序
Arrays.sort(array);
// 遍历排序后的数组,统计每个元素的出现次数
int result = array[0];
int maxCount = 1;
int currentCount = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == array[i - 1]) {
currentCount++;
} else {
currentCount = 1;
}
if (currentCount > maxCount) {
maxCount = currentCount;
result = array[i];
}
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{4, 5, 6, 4, 4, 6}) == 4);
System.out.println(solution(new int[]{7, 7, 7, 2, 2, 3}) == 7);
System.out.println(solution(new int[]{1, 1, 1, 1, 1}) == 1);
}
}
4.使用优先队列来查找出现频率最多的元素是一种非常高效的方式。我们可以通过以下几个步骤实现这一目标:
- 统计元素的频率:使用哈希表(或字典)来记录每个元素出现的频率。
- 将频率和元素存入优先队列:将频率和元素组成的元组插入优先队列中。优先队列会根据频率排序,频率最高的元素会排在队列的顶部。
- 从队列中取出频率最高的元素:取出优先队列中频率最高的元素。
- 优点:可以灵活地进行排序,且代码较为简洁。
- 缺点:相较于其他方法,空间复杂度较高,并且可能比
HashMap方案略慢。
代码如下
import java.util.Arrays;
public class Main {
public static int solution(int[] array) {
if (array == null || array.length == 0) {
return -1; // 返回-1表示输入无效
}
// 对数组进行排序
Arrays.sort(array);
// 遍历排序后的数组,统计每个元素的出现次数
int result = array[0];
int maxCount = 1;
int currentCount = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == array[i - 1]) {
currentCount++;
} else {
currentCount = 1;
}
if (currentCount > maxCount) {
maxCount = currentCount;
result = array[i];
}
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{4, 5, 6, 4, 4, 6}) == 4);
System.out.println(solution(new int[]{7, 7, 7, 2, 2, 3}) == 7);
System.out.println(solution(new int[]{1, 1, 1, 1, 1}) == 1);
}
}
5.在 Java 8 及以上版本中,Stream API 是处理集合数据的一种非常强大和灵活的方式。它能够简化许多常见的集合操作,包括对数据进行分组、过滤、排序、聚合等操作。使用 Stream API (适用于 Java 8 及以上) ,如果你使用的是 Java 8 或更高版本,可以利用 Java 的 Stream API 来实现更加简洁的代码,可以通过 Collectors.groupingBy 和 Collectors.counting 来计算频率。
- 优点:代码简洁,易于理解;能够利用
StreamAPI 提高可读性。 - 缺点:需要 Java 8 或以上版本,性能上可能稍逊于直接使用
HashMap(但在一般情况下,这差异不大)。
import java.util.stream.Collectors;
public class Main {
public static int solution(int[] array) {
if (array == null || array.length == 0) {
return -1; // 返回-1表示输入无效
}
// 使用Stream API统计频率
Map<Integer, Long> frequencyMap = Arrays.stream(array)
.boxed()
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
// 找到出现次数最多的元素
return frequencyMap.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(array[0]); // 如果输入数组为空,返回默认值
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(new int[]{1, 3, 8, 2, 3, 1, 3, 3, 3}) == 3);
System.out.println(solution(new int[]{4, 5, 6, 4, 4, 6}) == 4);
System.out.println(solution(new int[]{7, 7, 7, 2, 2, 3}) == 7);
System.out.println(solution(new int[]{1, 1, 1, 1, 1}) == 1);
}
}
题后总结
无论是简单的题还是难度比较大的题,去背后都值得我们去钻研,一道题不仅仅地展示给我们让我们解出来而是去指导我们去努力的思考,一种题可能有一种、两种甚至多种解题方法,不同的方法对应着不同的思路和思考的方向,我们可以从算法的好坏的标准时间复杂程度和空间复杂程度两方面进行结合分析,从这道题中,我们使用了五种解决方法,我们可以总结出:
- 如果你不在乎额外的空间消耗且对性能要求不高,可以使用
HashMap或Stream API,这两者都能处理任意类型的输入数据。 - 如果数组是有限范围的整数并且你关心空间的优化,可以选择使用 计数数组。
- 对于简单和小规模的数据集,排序 也是一个不错的选择,代码实现简洁且直观。
其实并不是每次我们都能一开始就想到多种解决方案。AI的最大优势之一,正是在于它能够为我们提供多样化的思路和方法,帮助我们更高效地解决问题。在我解决这道题的过程中,豆包AI为我提供了许多启发性的思路,这让我能够从不同角度审视问题,并最终找到一个有效的解法。相比于在浩如烟海的网上资料中苦苦查找,豆包AI不仅能迅速提炼出有价值的信息,还能够分享新的思维方式,帮助我更好地理解和应用各种解决方案。因此,豆包AI无疑是我查阅资料和碰撞思维的最佳伙伴,它为我提供的帮助,让我能够更快地总结出一个清晰的解法。