九度OJ-题目1370:数组中出现次数超过一半的数字

题目链接地址:

九度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
****************************************************************/

你可能感兴趣的:(面试题,剑指offer)