面试题29:数组中出现次数超过一半的数字

面试题29:数组中出现次数超过一半的数字

题目描述:
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

题目分析:
找出在数组中出现次数超过一半的数。直观的解法是,数组排序,排序后数一下得出出现次数超过一半的数,时间复杂度是O(NlogN),那有没有更好的解法。

1. hash表的解法

思路1:
用哈希表存起来数组的元素,一次遍历,如果hash[num[i]]大于数组的一半时,返回num[i],空间复杂度是O(N),显然是用空间换取时间的解法。

2. 基于partition的O(N)的算法

思路2:
再来分析排序后的数组,出现次数超过一半的数一定在排序后数组的中间,相当于查找数组的中位数,那我们走到这里,正确的思路是:联想到我们有成熟的O(N)的算法求任意数组中第k大的数字(就是快速排序第一次partition的过程),于是我们有了如下解法:
快速排序的partition,选中key值,小于key的在左边,大于等于key的在key右边,partition结束后key落在正确的位置,

  1. 如果key的下标是中间的位置,则直接返回就是我们想要的结果;
  2. 如果key的下标小于中间的位置,则在key的右半部分查找;
  3. 如果key的下标大于中间的位置,则在key的左半部分查找;

代码如下:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if (len == 0)
            return 0;
        int left, right, mid, pos, val;
        left = 0;
        right = len - 1;
        mid = len / 2;
        pos = Partition(numbers, left, right);
        while (pos != mid) {
            if (pos > mid) {
                right  = pos - 1;
                pos = Partition(numbers, left, right);
            }else {
                left = pos + 1;
                pos = Partition(numbers, left, right);
            }
        }
        val = numbers[pos];
        if (CheckHalf(numbers, val))
            return val;
        else
            return 0;
    }
    int Partition(vector<int> &num, int left, int right) {
        int pivot = num[left];
        while (left < right) {
            while (left < right && num[right] >= pivot)
                -- right;
            num[left] = num[right];
            while (left < right && num[left] < pivot)
                ++ left;
            num[right] = num[left];
        }
        num[left] = pivot;
        return left;
    }
    /* 检查最后的val值是否在数组中出现次数超过一半 */
    bool CheckHalf(vector<int> num, int val) {
        int len = num.size();
        int count = 0;
        for (int i = 0; i < len; i ++)
            if (num[i] == val)
                ++ count;
        if (count > len / 2)
            return true;
        else
            return false;
    }
};

3. 基于数组特点的O(N)的算法

思路3:
考虑到数组的特点,有了如下解法:
我们在遍历数组时,保存两个值一个是val记录当前的值,一个出现次数count。

  1. 当我们遍历到下一个数字时,如果和val相等,则count+1;否则count-1;
  2. 当count等于0时,重新记录val的值;

代码如下:

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if (len == 0)
            return 0;
        int val, i, count;
        val = numbers[0];
        count = 1;
        for (i = 1; i < len; i ++) {
            if (count == 0)
                val = numbers[i];
            if (numbers[i] == val)
                ++ count;
            else 
                -- count;   
        }
        if (CheckHalf(numbers, val))
            return val;
        else
            return 0;
    }
    /* 检查最后的val值是否在数组中出现次数超过一半 */
    bool CheckHalf(vector<int> num, int val) {
        int len = num.size();
        int count = 0;
        for (int i = 0; i < len; i ++)
            if (num[i] == val)
                ++ count;
        if (count > len / 2)
            return true;
        else
            return false;
    }
};

推荐:http://taop.marchtea.com/04.03.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值