找出数组中出现次数大于n/2、n/3,n/k的数

本文介绍三种高效算法,用于查找数组中出现次数超过特定比例的元素,包括n/2、n/3及n/k的场景。算法采用线性时间和空间复杂度,通过迭代更新候选元素和计数,最后验证结果的有效性。

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

1.找出数组中次数大于n/2的元素
说明:算法空间复杂度o(n),时间复杂度o(1)
数组中次数超过n/2的数最多1个,设置一个频率数,前一个数和后一个数相同,频率+1,
否则-1,只要有数频率超过数组长度一半,最终这个频率肯定大于0.

public static int solve2(int [] array) {
    	if(array == null || array.length == 0) return -1;
    	int temp = array[0],time = 0;
    	for(int i= 0;i<array.length;i++) {
    		if(array[i] == temp) time++;
    		else if(time <=0) {
    			temp = array[i];
    			time = 0;
    		}else {
    			time --;
    		}
    	}
    	 time = 0;
    	for(int i = 0;i<array.length;i++) {
    		if(temp == array[i]) time++;
    	}
    	if(time>array.length/2) return temp;
    	return -1;
    }

2.找出数组中出现次数超过n/3的数
超过n/2的数,最多2个,可以设置两个临时变量保存数组元素,两个频率变量

public static ArrayList solve(int [] array) {
    ArrayList<Integer> list = new ArrayList<Integer>();
    if(array == null ||array.length == 0) return null;
    int maj1 = array[0],maj2 = array[0];
    int count1 = 0,count2 = 0;
    int len = array.length;
    for(int i = 0;i<len;i++) {
    	if(array[i] == maj1) count1++;
    	else if(array[i] == maj2) count2++;
    	else if(count1<=0) {
    		count1 = 1;
    		maj1 = array[i];
    	}else if(count2<=0) {
    		count2 = 1;
    		maj2 = array[i];
    	}else {
    		--count1;
    		--count2;
    	}
    }
    count1 = count2 =0;
    for(int i =0;i<len;i++) {
    	if(array[i] == maj1) count1++;
    	if(array[i] == maj2) count2++;
    }
    if(count1>len/3) list.add(maj1);
    if(count2>len/3) list.add(maj2);
    return list;
    }

3.找出数组个数大于n/k的
大于n/k的最多也就是k-1个,所以map集合每当满k-1个的时候,如果,前后不相等,集合内所有的元素个数都要-1,为0的清除掉。

public static void solve3(int [] arr,int k) {
    	Map<Integer,Integer> map = new HashMap<>();
    	//首先找到k-1个不同的数,并记录他们出现的次数,继续向后遍历
    	//若下一个数map里有则将该数个数即value+1,否则map里数的个数都减1
    	//否则加入该数,次数记为1
    	for(int anArr:arr) {
    	 Integer value;
    	 if((value = map.get(anArr))!=null) {
    		 map.replace(anArr, value+1);
    	 }else {
    		 if(map.size() == k-1) {
    			 subtrackAll(map,k);
    		 }else {
    			 map.put(anArr,1);
    		 }
    	 }
      }
     //将map里所有数的个数即value都清0,因为map中的数并非都符合要求
    	map.keySet().forEach(integer->map.replace(integer, 0));
     //再次循环统计map里数字的真正个数
    	for(int num:arr) {
    		Integer val;
    		if((val = map.get(num))!= null) {
    			map.replace(num, val+1);
    		}
    	}
    	map.forEach((key,value)->{
    		if(value>arr.length/k) {
    			System.out.println(key+" ");
    		}
    	});
    }
	private static void subtrackAll(Map<Integer, Integer> map, int k) {
		List<Integer> list = new ArrayList();
	// 将map里全部数字的value值减1,减1后若value==0,则删除该键
		map.forEach((integer,integer2)->{
			if(integer2 == 1) {
				list.add(integer);
			}else map.replace(integer, integer2-1);
		});
		if(list.size()!=0) {
			for(Integer key:list) {
				map.remove(key);
			}
		}	
	}  

为什么最后还要验证一下,例如找大于n/2的数,数组为 2 2 2 3 3 3 最终temp为3但是显然3个数并没有大于n/2

<think>我们有一个任务:在C++中找出数组中出现次数最多的元素。根据引用内容,有几种方法可以实现:1.双重循环法(暴力法):对每个元素,遍历整个数组统计其出现次数,然后比较得到最大次数的元素。2.使用辅助数组记录次数:例如引用[1]和[5]中,使用一个数组b来记录每个元素出现的次数(但注意,引用[1]的b数组记录的是每个元素之后与其相等的元素个,并不是完整的出现次数;而引用[5]则是统计每个元素的完整出现次数)。3.使用哈希表(字典)来记录每个元素的出现次数,然后找出最大值(如引用[4]的C#方法,但我们可以用C++的map或unordered_map实现)。考虑到效率,双重循环的时间复杂度为$O(n^2)$,在据量较大时效率较低。而使用哈希表的方法时间复杂度为$O(n)$,更优。因此,我们将采用哈希表的方法来实现。同时,如果有多个元素出现次数相同且都是最大,我们可能需要返回所有这样的元素(按从小到大排序)?但用户问题只要求找出出现次数最多的元素(可能是一个或多个?)。根据问题描述,用户要求的是“出现次数最多的元素”,如果多个元素出现次数相同且都是最大,那么这些元素都是答案。但用户没有明确要求返回多个还是只返回一个。在引用[4]中,返回了所有出现次数最多的元素(并按从小到大排序)。而其他引用中只返回了一个(当有多个时,返回的是第一个遇到的最大值对应的元素)。根据问题,用户说“找出数组中出现次数最多的”,通常如果出现多个,可能需要返回所有。但问题没有明确,我们可以先按返回一个(任意一个)来实现,然后也可以给出返回所有最大值元素的版本。步骤:1.使用哈希表(unordered_map)统计每个元素的出现次数2.遍历哈希表,找到最大的出现次数3.再次遍历数组(或者遍历哈希表)找出具有该最大次数的元素(可以只找一个,也可以找所有)。但注意:题目要求是C++实现。我们将提供两种版本:版本1:只返回一个出现次数最多的元素(如果有多个,返回第一个遇到的出现次数最多的元素,或者任意一个?)。根据引用[5]的方法,它返回的是第一个遇到的最大次数的元素(注意,它记录的是每个元素出现的总次数,然后遍历计数组b,找到最大值的下标,然后输出该元素)。但是,如果最大值有多个,它返回的是第一个遇到的最大值对应的元素(因为遍历时用c记录最大值,m记录下标,当遇到更大的才更新,所以最终m是最后一个最大值的位置?不对,它记录的是第一个最大值?也不对,它记录的是最后一个最大值的位置?因为每次更新最大值时记录下标,所以如果有多个相同的最大值,它会记录最后一个最大值的位置)。因此,引用[5]的代码输出的是最后一个出现次数最多的元素。例如:数组[1,2,2,1],对于元素1,出现2次;元素2,出现2次。那么引用[5]中,先统计每个元素的次数:b[0]=2(对应a[0]=1),b[1]=2(对应a[1]=2),b[2]=2(对应a[2]=2),b[3]=2(对应a[3]=1)。然后遍历b数组,c初始为0,然后:k=0:c=0 <b[0]=2 ->更新c=2,m=0 ->此时a[m]=1k=1:c=2等于b[1]=2->不更新k=2:c=2等于b[2]=2->不更新k=3:c=2等于b[3]=2->不更新所以输出a[0]=1。但实际上,2也是出现次数最多的。所以它返回了第一个出现次数最多的元素(即第一个元素1)?不对,这里统计的是每个元素的出现次数,然后遍历计数组时,第一个最大值在0位置,所以输出1。但如果我们希望输出2(因为2数组中先出现?)实际上,数组元素顺序不一定。因此,如果我们只要求返回一个,那么返回任意一个都可以。但通常,如果有多个,我们可能希望返回第一个出现次数最多的元素(按元素出现的顺序?)或者按元素大小?题目没有要求。版本2:返回所有出现次数最多的元素(并按从小到大排序),如引用[4]的做法。我们先实现版本1:只返回一个(任意一个最大值对应的元素,实际上我们可以在遍历哈希表时记录第一个最大值对应的元素,或者最后一个,但通常我们只关心最大值,所以任意一个都行,但要注意,可能有多个最大值,我们只返回其中一个)。但是,用户问题没有明确要求,我们先按照返回一个元素来实现,然后可以扩展。我们采用哈希表的方法:步骤:1.遍历数组,用unordered_map记录每个字出现的次数2.遍历哈希表,记录最大出现次数和对应的元素(注意:在遍历过程中,如果当前元素的出现次数大于最大值,则更新最大值,并记录当前元素;如果等于最大值,我们是否记录?如果只要求一个,那么我们可以选择记录第一个最大值,或者最后一个,或者任意。但通常,我们只记录一个,所以当大于时更新,等于时不更新,这样我们记录的是最后一个最大值?或者我们可以在等于时也不更新,这样记录的是第一个最大值?这取决于我们如何遍历哈希表。而哈希表是无序的,所以无法保证顺序。所以我们可以选择在等于最大值时不更新,那么最后记录的就是第一个最大值(如果我们从哈希表开头开始遍历)?但哈希表遍历顺序不确定。因此,如果我们要求返回第一个在数组中出现次数最多的元素(即原数组中第一次出现达到最大次数的元素),那么这种方法就不行。因为哈希表遍历顺序不是数组的顺序。另一种方法:在遍历数组统计次数时,同时记录最大值和对应的元素。但是,在统计次数时,我们不知道当前元素是否最终会是最大值。所以我们需要先统计完次数,然后再遍历数组(按照数组顺序)来找到第一个出现次数等于最大值的元素。步骤(版本1.1:返回数组中第一个出现次数最多的元素):1.用哈希表统计每个元素的出现次数2.遍历哈希表,得到最大出现次数max_count3.再次遍历原数组,对于每个元素,在哈希表中查找其出现次数,如果等于max_count,则返回该元素(这样返回的是数组中第一个出现次数等于max_count的元素)。步骤(版本1.2:返回任意一个出现次数最多的元素):1.用哈希表统计每个元素的出现次数2.同时,在统计过程中,记录当前的最大次数和对应的元素(即每次更新哈希表后,检查当前元素的出现次数,如果大于当前记录的最大次数,则更新最大值和该元素)。但是,这样在统计过程中,我们只能记录最后一个出现次数最多的元素(因为当出现次数等于最大值时,我们不会更新,所以记录的是最后一个达到最大值的元素?或者当出现次数大于当前最大值时更新,所以记录的是最后一个出现次数超过当前最大值的元素)。这样得到的是最后一个出现次数最多的元素(在遍历过程中)。我们选择版本1.1:返回第一个出现次数最多的元素(按原数组顺序)。另外,我们也可以考虑版本2:返回所有出现次数最多的元素,然后排序(按元素值从小到大)。根据用户问题,他可能只需要一个,但为了全面,我们可以提供两种方法。我们先实现版本1.1:返回第一个出现次数最多的元素。代码步骤:1.使用unordered_map<int, int> counts;记录每个字出现的次数2.第一次遍历数组,更新counts。3.遍历counts,找到最大出现次数max_count。4.第二次遍历数组,对于每个元素,如果该元素在counts中的值等于max_count,则返回该元素(因为按数组顺序,第一个遇到的就是第一个出现次数最多的元素)。注意:为什么第二次遍历数组而不是遍历哈希表?因为要按数组的顺序,所以遍历数组可以保证我们找到的是第一个达到最大次数的元素。但是,有可能同一个元素在数组中出现多次,我们只需要第一个出现次数最多的元素(即第一个出现次数等于max_count的元素),所以遍历数组时,每个元素都会被检查,但当我们第一次遇到一个元素,它的出现次数等于max_count,我们就返回它。例如:数组[1,2,2,1],max_count=2。遍历数组:第一个元素1,出现次数2(等于max_count),返回1。而2也是2次,但1先被遇到。如果我们想返回最后一个出现次数最多的元素,那么我们就反向遍历,或者顺序遍历到最后一个。但用户没有要求顺序,所以返回第一个遇到的即可。下面我们写代码(只返回一个元素):*/#include <iostream>#include <unordered_map> #include<vector> usingnamespace std; intfindMostFrequentElement(constvector<int>& arr) {if(arr.empty()){//数组为空,返回一个默认值,或者抛出异常,这里我们返回0return0;}unordered_map<int,int>counts;intmax_count=0;//第一次遍历:统计次数for (int num: arr) {counts[num]++;//这里可以更新max_count,但也可以不更新,等后面再找//为了减少一次遍历,我们可以在这里更新max_count//但是注意:这里更新的是当前的最大值,但后面可能还有更大的,所以需要每次都比较//所以这里不更新,等统计完再找最大值}//找出最大出现次数for (const auto& pair: counts) {if(pair.second >max_count) {max_count =pair.second;}}//第二次遍历数组,找到第一个出现次数等于max_count的元素for (int num: arr) {if(counts[num] ==max_count) {returnnum;}}//理论上不会执行到这里,因为一定有至少一个元素returnarr[0]; }/*但是,上述代码需要两次遍历数组和一次遍历哈希表。我们可以优化,在统计次数的同时记录最大值,然后第二次遍历数组找第一个最大值元素。但最大值需要先知道,所以无法避免第二次遍历(除非我们在统计时记录达到最大值的元素,但这样只能记录最后一个,不能记录第一个)。另一种方法:在统计次数的同时,我们记录当前的最大次数和对应的元素,但是这样我们无法知道第一个达到最大次数的元素(因为当某个元素出现次数增加时,它可能超过当前最大值,但此时它并不是第一个出现的元素)。所以为了记录第一个,我们还是需要第二次遍历。因此,上述代码是可行的。如果要求返回所有出现次数最多的元素(并按元素值从小到大排序),我们可以这样:1.统计次数,并记录最大次数max_count2.遍历哈希表,将所有出现次数等于max_count的元素放入一个vector中。3.对这个vector排序(从小到大)。4.返回这个vector(或者输出)。代码: vector<int>findMostFrequentElements(constvector<int>& arr) {unordered_map<int, int> counts;int max_count =0;for (int num: arr) {counts[num]++;}for(constauto&pair :counts){if (pair.second> max_count){max_count= pair.second;}}vector<int>result;for(constauto&pair :counts){if (pair.second== max_count){result.push_back(pair.first);}}sort(result.begin(),result.end());return result; }注意:这里我们遍历哈希表来收集结果,然后排序。因为哈希表无序,所以我们需要排序。另外,这样返回的是所有出现次数最多的元素(可能有多个)。用户问题中只要求一个,所以我们先提供第一种方法的代码。但是,用户问题描述是“找出数组中出现次数最多的”,没有明确要求一个还是多个。我们可以根据实际情况选择。如果用户希望得到所有,那么第二种方法更好。考虑到引用[4]中要求返回所有并排序,而其他引用只返回一个,所以我们可以先按只返回一个实现,然后提示用户如果需要所有可以扩展。现在,我们按照用户最初的问题(引用[1]-[5]都是返回一个元素,除了引用[4]返回多个),我们提供返回一个元素的版本(按数组顺序第一个)。测试用例:输入:[1,2,3,2,1] ->出现次数1:2,2:2,3:1 ->最大值2,第一个出现次数2的元素是1(因为1在第一个位置就出现了,但注意,第一次遇到1时,我们并不知道它的次数,所以需要第二次遍历时,第一个遇到的出现次数2的元素是1(在位置0))。输入:[1,2,2,1]->返回1(第一个元素1的出现次数2,等于最大值2,所以返回1)。但是,如果我们希望返回2(因为2先达到最大值?)实际上,12都是出现2次,我们按数组顺序,第一个遇到的出现次数等于2的元素是1(在位置0),而2在位置1,所以1先遇到。另一种想法:在统计次数时,我们也可以记录每个元素第一次出现的位置。然后,对于所有出现次数最多的元素,我们选择位置最小的那个。但这样需要额外的空间记录位置。我们这里采用简单的方法:第二次遍历数组,按顺序找到第一个出现次数等于最大值的元素。现在,我们写一个主函来测试。注意:数组可能为空,需要处理。完整代码示例(返回一个元素):*/ #include<iostream> #include<unordered_map>#include <vector>using namespacestd;int findMostFrequentElement(const vector<int>&arr){if (arr.empty()) {//可以抛出异常,这里返回0(假设数组元素为正整,0表示无效)return0;}unordered_map<int,int>counts;//统计次数for (int num: arr) {counts[num]++;}//找最大次数int max_count =0;for (const auto& pair: counts) {if(pair.second >max_count) {max_count =pair.second;}}//再次遍历数组,找第一个出现次数等于max_count的元素for(intnum :arr){if (counts[num]== max_count){return num;}}//理论上不会到这里return arr[0];} intmain(){vector<int> arr;int n;cout <<"请输入数组元素个:";cin >>n;cout<< "请输入数组元素:";for (int i=0;i <n;i++){int x;cin >>x;arr.push_back(x);}intresult =findMostFrequentElement(arr);cout <<"出现次数最多的元素是: "<< result<< endl;return0;} /*如果用户需要返回所有出现次数最多的元素(并按从小到大排序),我们可以提供另一个函: vector<int>findAllMostFrequentElements(const vector<int>&arr){if (arr.empty()) {return{};}unordered_map<int,int>counts;intmax_count=0;for(intnum :arr){counts[num]++;}for (const auto& pair: counts) {if(pair.second >max_count) {max_count =pair.second;}}vector<int> result;for (const auto& pair: counts) {if(pair.second ==max_count) {result.push_back(pair.first);}}sort(result.begin(), result.end());returnresult;}然后在主函中调用并输出。注意:如果数组为空,我们返回空向量。用户可以根据需求选择使用哪个函。现在,我们根据用户问题,我们提供了返回一个元素(第一个出现次数最多的元素)的C++实现。但是,我们注意到引用[1]和[5]中的方法都是使用双重循环,效率较低。我们这里使用哈希表,效率更高($O(n)$)。另外,引用[2]和[3]也是双重循环。因此,我们推荐使用哈希表的方法。相关问题:1.如果数组中有多个元素出现次数相同且都是最大,如何返回所有这样的元素?2.如何优化上述算法以降低空间复杂度?3.如果数组元素范围有限(例如0到100),如何用计数组代替哈希表?4.如何在不排序的情况下,按元素在数组中出现的顺序返回所有出现次数最多的元素?我们将在回答中自然生成相关问题。 */</think>### 在C++中查找数组出现次数最多的元素 在C++中查找数组中出现次数最多的元素,可以通过**哈希表**(`unordered_map`)高效实现。以下是分步解释和代码实现: #### 方法原理 1. **统计频率**:使用哈希表记录每个元素的出现次数 2. **查找最大值**:遍历哈希表找到最高频率 3. **定位元素**:返回第一个达到最高频率的元素(按原数组顺序) 时间复杂度:$O(n)$ 空间复杂度:$O(n)$(哈希表开销) #### 代码实现 ```cpp #include <iostream> #include <vector> #include <unordered_map> using namespace std; int findMostFrequent(vector<int>& arr) { if (arr.empty()) return -1; // 空数组处理 unordered_map<int, int> freqMap; int maxCount = 0; // 统计元素频率 for (int num : arr) { freqMap[num]++; if (freqMap[num] > maxCount) { maxCount = freqMap[num]; } } // 按原数组顺序查找第一个最大频率元素 for (int num : arr) { if (freqMap[num] == maxCount) { return num; } } return arr[0]; // 理论不会执行到此 } int main() { vector<int> arr = {2, 3, 1, 2, 5, 2, 3, 3}; int result = findMostFrequent(arr); cout << "出现次数最多的元素: " << result << endl; // 输出: 2 (注意:23均出现3次,但2数组中先出现) return 0; } ``` #### 关键点说明 1. **哈希表优势**:比暴力双重循环($O(n^2)$)更高效[^1][^3][^5] 2. **顺序处理**:返回第一个遇到的最大频率元素(非随机) 3. **多个最大值**:当多个元素频率相同时,返回数组顺序中最先出现的 4. **空数组处理**:避免未定义行为 #### 扩展:返回所有最大值元素 若需返回所有出现次数最多的元素(按值排序): ```cpp vector<int> findAllMostFrequent(vector<int>& arr) { unordered_map<int, int> freqMap; int maxCount = 0; for (int num : arr) { freqMap[num]++; maxCount = max(maxCount, freqMap[num]); } vector<int> result; for (auto& [num, count] : freqMap) { if (count == maxCount) { result.push_back(num); } } sort(result.begin(), result.end()); // 按值排序 return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值