题目链接地址:
九度OJ-题目1370:数组中出现次数超过一半的数字
题目描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
输入:
每个测试案例包括2行:
第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数。
第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]。
输出:
对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1。
样例输入:
9
1 2 3 2 2 2 5 4 2
样例输出:
2
解题思路:
刚看到这道题时,我的想法是先排序,然后通过遍历排序序列来找出次数超过一半的数字。仔细一想其实不需要遍历有序序列,因为数字在数组中的出现次数超过了一半,所以有序序列的中间数就是数组中出现次数超过一半的数字。使用排序来寻找次数超过一半的数字的算法时间复杂度是O(logn * n), 而《编程之美》中的算法时间复杂度为O(n)。我采用的是《编程之美》中的算法,下面是算法思想:
假设长度为n的数组存在出现次数超过一半的元素candidate,则将两个不相等的元素剔除出数组后,在剩余的长度为n - 2的数组中,元素candidate出现的次数仍然超过剩余数组长度的一半。这就将原问题转化成了本质不变但规模更小的子问题。
举个栗子:对于测试用例:1 2 3 2 2 2 5 4 2
因为数组第1个元素’1’和第2个元素’2’不相等,所以将它们”踢出”数组,剩余数组就变成了:3 2 2 2 5 4 2
因为数组第1个元素’3’和第2个元素’2’不相等,所以将它们”踢出”数组,剩余数组就变成了:2 2 5 4 2
因为数组第1个元素’2’与第2个元素’2’相等,所以将它们保留在数组中,这时再将数组的第2个元素’2’与第3个元素’5’进行比较,发现二者不相等,所以将它们”踢出”数组,剩余数组就变成了:2 4 2
再将数组中的第1个元素’2’与第2个元素’4’进行比较,发现二者不相等,所以将它们”踢出”数组,剩余数组就剩下元素’2’了,这个’2’就是原数组中出现次数超过一半的候选元素了。
最后还需要遍历原数组以确定’2’在原数组中出现的次数是否真的超过一半。因为有时候所选出的候选元素在数组中出现的次数并没有超过一半。
例如,对于测试用例:1 2 1 2 3
‘3’是数组出现次数超过一半的候选元素,但是’3’在数组中出现的次数并没有超过一半。
AC代码如下:
#include<stdio.h> #define MAX 100000 int number[MAX]; /** * 输入数组 * @param n 数组的长度 * @return void */ void inputArray(int n) { int i; for(i = 0;i < n;i++) { scanf("%d",&number[i]); } } /** * 获取数组中出现次数超过一半的候选元素 * @param n 数组的长度 * @return candidate 返回数组中出现次数超过一半的候选元素 */ int getCandidateFromArray(int n) { int candidate = number[0]; // 用于保存候选元素 int count = 1; // 用于标记候选元素的数目 int i; for(i = 1;i < n;i++) { if(candidate == number[i]) { count++; // 如果数组中第i个元素等于候选元素,则记录候选元素的个数增加1个 } else { count--; // 如果数组中第i个元素不等于候选元素,则记录候选元素的个数减少1个 if(0 == count) { candidate = number[i]; // 如果候选元素的个数为0,则更新候选元素为number[i] count = 1; // 要记住给count赋值为 1 } } } return candidate; } /** * 判断候选元素在数组中出现的次数是否真的超过一半 * @param candidate 候选元素 * @param n 数组的长度 * @return bool 如果候选元素在数组中出现的次数超过一半则返回true,否则返回false */ bool checkCandidate(int candidate,int n) { int i; int count = 0; for(i = 0;i < n;i++) { if(candidate == number[i]) count++; } if(count > (n - count)) // 表示候选元素在数组中出现的次数超过了一半 return true; else return false; } int main() { int n; int candidate; bool isCandidateValid; // 判断候选元素是否合法 while(EOF != scanf("%d",&n)) { inputArray(n); candidate = getCandidateFromArray(n); isCandidateValid = checkCandidate(candidate,n); if(true == isCandidateValid) { printf("%d\n",candidate); } else { printf("-1\n"); } }// while return 0; } /************************************************************** Problem: 1370 User: blueshell Language: C++ Result: Accepted Time:40 ms Memory:1412 kb ****************************************************************/