剑指Offer对答如流系列 - 数组中出现次数超过一半的数字

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

题目描述

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

问题分析

大家最容易想到的思路是 数字次数超过一半,则说明排序之后数组中间的数字一定就是所求的数字。

既然是数组,要牵扯到排序,大家一般都会选用经典快速排序或者随机快速排序。随机快速排序由于每次划分的依据是从数组随机选出的,所以数据状况对它的影响减弱,时间复杂度为 O(N*logN),而快速排序受数据状况的影响,时间复杂度差的话为O(n^2)。本文使用的是快速排序,书写比较方便。

其实还有一个比较有意思,而且非常好的算法思想,被描述为阵地攻守的思想:

第一个数字作为第一个士兵,守阵地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即为敌人,同归于尽,count--;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
再加一次循环,记录这个士兵的个数看是否大于数组一般即可。

也就是数字次数超过一半,则说明:该数字出现的次数比其他数字之和还多

遍历数组过程中保存两个值:一个是数组中某一数字,另一个是次数。遍历到下一个数字时,若与保存数字相同,则次数加1,反之减1。若次数=0,则保存下一个数字,次数重新设置为1。由于要找的数字出现的次数比其他数字之和还多,那么要找的数字肯定是最后一次把次数设置为1的数字。

问题解答

思路一(不推荐)

    // 表示输入是否有效
    boolean isInputInvalid = true;
    // 快速排序
    public int MoreThanHalfNum(int [] array) {
        if(array==null ||array.length <= 0) {
            return 0;
        }
        int low=0;
        int high=array.length-1;
        int index=partition(array, low, high);

        while(index != array.length>>1){
            if(index < array.length>>1 ){
                low = index+1;
                index = partition(array,low,high);
            } else {
                high = index-1;
                index=partition(array,low,high);
            }
        }

        //判断次数是否超过一半
        int num=array[index];
        int times=0;
        for(int i=0;i < array.length;i++){
            if(array[i]==num){
                times++;
            }
        }
        if(times*2 > array.length){
            isInputInvalid=false;
            return num;
        }
        return 0;
    }

    private int partition(int[] array,int low ,int high){

        int pivotKey=array[low];
        while(low < high){
            while(low < high &&  array[high] >= pivotKey) {
                high--;
            }
            int temp=array[low];
            array[low]=array[high];
            array[high]=temp;
            while(low < high && array[low] <= pivotKey) {
                low++;
            }
            temp=array[low];
            array[low]=array[high];
            array[high]=temp;
        }
        return low;
    }

思路二(推荐)

    // 表示输入是否有效
    boolean isInputInvalid = true;

    public int MoreThanHalfNum(int [] array) {
        if(array==null || array.length <= 0) {
            return 0;
        }

        int num=array[0];
        int count = 1;
        for(int i=1;i<array.length;i++){
            if(count == 0) {
                num=array[i];
                count++;
            }
            else if(array[i] == num) {
                count++;
            }
            else {
                count--;
            }
        }

        if(count > 0){
            int times=0;
            for(int i=0;i<array.length;i++){
                if(array[i] == num){
                    times++;
                }
            }
            if(times*2 > array.length){
                isInputInvalid=false;
                return num;
            }
        }
        return 0;
    }

原文地址:https://www.cnblogs.com/JefferyChenXiao/p/12246441.html

时间: 2024-11-07 21:13:49

剑指Offer对答如流系列 - 数组中出现次数超过一半的数字的相关文章

剑指offer 面试29—数组中出现次数超过一半的数字

题目: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 解法一: 先将数组排序,然后出现次数超过一半的数字就是a[n/2+1],时间复杂度O(nlgn). 解法二:O(n) 基本思想: 消除原理:在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数.当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1.如果下一个数字

剑指offer python版 数组中出现次数超过一半的数字

def aa(nums): if not nums: return False hashes={} ret=[] for s in nums: hashes[s]=hashes[s]+1 if hashes.get(s) else 1 if hashes[s] >len(nums)/2: ret.append(s) return list(set(ret)) print(aa([1,2,3,2,2,2,2,2,3,2,3,3,4])) 原文地址:https://www.cnblogs.com/x

剑指Offer对答如流系列 - 数组中数字出现的次数

面试题56:数组中数字出现的次数 题目描述 问题(1)数组中只出现一次的两个数字 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 问题(2)数组中唯一只出现一次的数字 在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次.请找出那个只出现一次的数字. 问题分析 问题(1)分析 在这篇文章剑指Offer对答如流系列 - 二进制中 1 的个数中,我们详细探讨了位运算,其中有重要的一条:两个相同的数异

剑指Offer对答如流系列 - 数组中的逆序对

面试题51:数组中的逆序对 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 问题分析 大多数人的第一反应就是顺序扫描整个数组,对每个数字都和后面的数字比较大小,时间复杂度为O(n^2),效率太低. 利用归并排序的思想,先将数组分解成为n个长度为1的子数组,然后进行两两合并同时排好顺序.(在排序的时候计算逆序对) 归并排序是经典排序算法之一,其核心是将待排数组不断细分,然后排序最后再合并,这是经典的分治策略(分

剑指OFFER----面试题39. 数组中出现次数超过一半的数字

链接:https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/ 思路: cnt记录个数,val记录值:遍历数组,若等于val,则cnt++,否则cnt--,若cnt为0,则重置,最后val即为结果. 代码: class Solution { public: int majorityElement(vector<int>& nums) { int cnt =

剑指Offer对答如流系列 - 二进制中 1 的个数

面试题14:二进制中 1 的个数 题目描述 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数.例如把9表示成二进制是1001,有2位是1.因此如果输入9,该函数输出2. 问题分析与解决 这道面试题归属于 <剑指Offer>位运算章节.遇到二进制相关的问题,很容易想到位运算,虽然种类不多(与.或.异或.左移.右移),但是搞起来是千变万化的.待会再和你侃一些骚操作,我们先看这道题. (一)思路一 "与运算"有一个性质:通过与对应位上为1,其余位为0的数进行与运算,可以

剑指Offer对答如流系列 - 数据流中的中位数

面试题41:数据流中的中位数 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值. 所谓数据流,就是不会一次性读入所有数据,只能一个一个读取,每一步都要求能计算中位数. 问题分析 相信上一道题 最小的k个数 给了你容器的启示. 我们将读入的数据分为两部分,一部分数字小,另一部分大. 小的一部分采用大顶堆存放,大的一部分采用小顶堆存放.当总个数为偶数时,使

剑指Offer对答如流系列 - 圆圈中最后剩下的数字

面试题62:圆圈中最后剩下的数字 题目描述 0, 1, -, n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 例如,从数字0开始每次删除第3个数字,则删除的前四个数字是2 0 4 1 因此最后剩下的数字是3 问题分析 思路一: 既然涉及到数据的频繁删除,可以考虑使用链表来存放数据,每次对长度取余数可以实现循环操作. 思路二: 这种问题规律性非常强,其实已经有对这一规律背后的数学模型的探究,即约瑟夫环 举一个具体的场景: 据说著名犹太历

剑指Offer对答如流系列 - 链表中倒数第k个结点

面试题22:链表中倒数第k个结点 题目描述 输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是倒数第1个结点.例如一个链表有6个结点,从头结点开始它们的值依次是1.2.3.4.5.6.这个链表的倒数第3个结点是值为4的结点. 链表结点定义如下: public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } 问题分析 链表