算法之找出数组中出现次数大于n/m的元素

博客介绍了如何利用‘俄罗斯方块’的游戏规则来解决算法问题,即在数组中找出出现次数大于n/m的元素。文章指出,这种情况下元素个数最多为(m-1)个,并通过举例和代码展示了解题过程,特别讨论了当n/m不为整数时的情况。

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

最经典的题目莫过于是: 在一个数组中找出出现次数超过n/2的元素?更进一步的,找出数组中出现次数大于n/3的所有元素?

注:这里有一个很重要的事实,那就是出现次数大于n/m的元素的个数至多为(m-1)个,比如出现次数大于n/3的至多只有两个。

关于这一类题目的解题思路,可以先讲一个游戏 称作 “俄罗斯方块”。这里的规则是每一行的元素要完全不一样,一样的元素则总是在同一列,如果最下面的行已经被填满,那么消除最下面的行。

例如在数组 A = {7,3,3,7,4,3,4,7,3,4,3,4,7,3,4} Len = 15,现在找出现次数大于 Len/3的,也就是出现超过5次的。那么现在我们的方块的宽度就应该是3,即每一步的状态一次为

                                                                         &

<think>根据用户需求,我们需要在C语言中实现一个函数,找出数组中出现次数超过一半的元素。根据引用[1]中的思路,如果存在这样的元素,排序后它一定会出现在数组中间位置(即numsSize/2处)。因此,我们可以先对数组进行排序,然后返回中间位置的元素。但是,这种方法的前提是数组中一定存在一个出现次数超过一半的元素。如果不存在这样的元素,那么中间位置的元素可能不是正确答案。然而,题目要求是找出出现次数超过一半的元素,如果不存在,应该如何处理?根据问题描述,用户要求的是“出现次数大于numsSize/2的元素”,如果存在则返回该元素,如果不存在,按照常理应该返回一个错误值(例如-1)或者进行额外检查。引用[1]中的代码直接返回了中间位置的元素,没有检查该元素是否真的出现超过一半次数。因此,我们需要改进:在返回之前,验证中间位置的元素是否确实出现次数超过一半。步骤:1.对数组进行排序。2.取出中间位置的元素(假设为candidate)。3.遍历数组,统计candidate出现的次数。4.如果次数大于numsSize/2,则返回candidate;否则返回-1(表示不存在)。但是,用户提供的引用[1]中的代码并没有做检查,而引用[2]提到了错误处理:当数组中有数字大于数组长度时返回-1。但这里我们关注的是出现次数,所以检查出现次数是更直接的做法。另外,我们也可以使用摩尔投票法(MooreVotingAlgorithm)来在O(n)时间复杂度和O(1)空间复杂度内解决这个问题。摩尔投票法的基本思想是:每次从序列中选择两个不同的数字删除掉(或称为“抵消”),最后剩下的一个数字(或相同的数字)就是出现次数超过一半的元素。摩尔投票法步骤:1.初始化候选元素candidate和计数器count=0。2.遍历数组中的每个元素:-如果count为0,则将当前元素设为候选元素candidate,并设count=1。-否则,如果当前元素等于candidate,则count++;否则count--。3.遍历结束后,candidate就是可能的出现次数超过一半的元素。然后需要再次遍历数组,统计candidate出现的次数,如果大于numsSize/2,则返回candidate;否则返回-1。摩尔投票法效率更高,因为只需要两次遍历,且不需要排序。考虑到用户要求用C语言实现,我们将提供两种方法,并重点推荐摩尔投票法。方法一:排序法(需要O(nlogn)时间,但代码简单)方法二:摩尔投票法(O(n)时间,O(1)空间)由于用户问题中引用的代码使用了排序法,我们先实现排序法,并添加验证步骤。然后实现摩尔投票法。下面分别给出两种方法的代码:方法一(排序法):```c#include<stdio.h>#include<stdlib.h>intcmp_int(constvoid*s1,constvoid*s2){return*(int*)s1-*(int*)s2;}intmajorityElement(int*nums,intnumsSize){qsort(nums,numsSize,sizeof(int),cmp_int);intcandidate=nums[numsSize/2];//验证candidate是否出现超过一半intcount=0;for(inti=0;i<numsSize;i++){if(nums[i]==candidate){count++;}}if(count>numsSize/2){returncandidate;}else{return-1;//不存在超过一半的元素}}```方法二(摩尔投票法):```cintmajorityElement(int*nums,intnumsSize){intcandidate=0;intcount=0;//第一遍遍历,找到候选元素for(inti=0;i<numsSize;i++){if(count==0){candidate=nums[i];count=1;}else{if(nums[i]==candidate){count++;}else{count--;}}}//第二遍遍历,验证候选元素count=0;for(inti=0;i<numsSize;i++){if(nums[i]==candidate){count++;}}if(count>numsSize/2){returncandidate;}else{return-1;}}```两种方法都可以,但摩尔投票法更高效。用户可以根据需求选择。注意:在摩尔投票法中,我们假设数组非空。如果数组为空,应该返回-1。代码中已经处理了空数组情况(在验证阶段count为0,返回-1)。相关问题:1.为什么摩尔投票法可以找到出现次数超过一半的元素?2.如果数组中有两个出现次数超过n/3的元素,如何找出?能否用摩尔投票法?3.排序法和摩尔投票法在时间和空间复杂度上有什么不同?</think>### 如何用C语言实现找出数组中出现次数大于numsSize/2的元素找出数组中出现次数超过一半的元素,最常用的方法是**摩尔投票算法**(Moore Voting Algorithm),该算法时间复杂度为$O(n)$,空间复杂度为$O(1)$。以下是详细实现步骤和代码: #### 摩尔投票算法原理 1. **核心思想**:不同元素相互抵消,最后剩下的元素可能是目标 2. **前提条件**:数组中必须存在出现次数>$\frac{n}{2}$的元素 3. **算法步骤**: - 初始化候选元素`candidate`和计数器`count=0` - 遍历数组: - 当`count=0`时,选择当前元素作为候选 - 遇到相同元素则`count++`,不同元素则`count--` - 最终候选元素需要二次验证 #### C语言实现代码 ```c #include <stdio.h> int majorityElement(int* nums, int numsSize) { int candidate = 0; // 候选元素 int count = 0; // 计数器 // 第一遍遍历:找出候选元素 for (int i = 0; i < numsSize; i++) { if (count == 0) { candidate = nums[i]; // 选择新候选 count = 1; } else { // 相同元素计数+1,不同元素抵消 count += (nums[i] == candidate) ? 1 : -1; } } // 第二遍遍历:验证候选元素 count = 0; for (int i = 0; i < numsSize; i++) { if (nums[i] == candidate) count++; } return (count > numsSize / 2) ? candidate : -1; } // 测试代码 int main() { int arr1[] = {2,2,1,1,1,2,2}; // 存在目标元素2 int arr2[] = {3,2,3}; // 存在目标元素3 int arr3[] = {1,2,3}; // 无目标元素 printf("示例1结果: %d\n", majorityElement(arr1, 7)); // 输出2 printf("示例2结果: %d\n", majorityElement(arr2, 3)); // 输出3 printf("示例3结果: %d\n", majorityElement(arr3, 3)); // 输出-1 return 0; } ``` #### 算法特点 1. **时间复杂度**:$O(n)$(两次遍历) 2. **空间复杂度**:$O(1)$(仅用两个变量) 3. **优势**: - 无需排序,效率高 - 适用于大规模数据 - 可扩展解决出现次数>$\frac{n}{3}$的问题[^3] #### 备选方案:排序法 如果允许使用库函数,可通过快速排序实现(需包含`stdlib.h`): ```c #include <stdlib.h> int cmp(const void* a, const void* b) { return *(int*)a - *(int*)b; } int majorityElement_sort(int* nums, int numsSize) { qsort(nums, numsSize, sizeof(int), cmp); int candidate = nums[numsSize / 2]; // 验证是否超过一半 int count = 0; for (int i = 0; i < numsSize; i++) { if (nums[i] == candidate) count++; } return (count > numsSize / 2) ? candidate : -1; } ``` > ⚠️ 注意:排序法时间复杂度为$O(n\log n)$,空间复杂度$O(1)$,性能不如摩尔投票法[^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值