算法系列——数组中出现次数超过一半的数字(剑指offer)

来源:互联网 发布:java乘法函数 编辑:程序博客网 时间:2024/06/06 02:04

题目描述

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

解题思路

排序法

将数组进行排序,数组中元素个数超过一半的元素必定在中间位置,直接返回中间位置的元素即可。
排序算法最好的平均时间复杂度为O(nlogn),空间复杂度为O(1)

哈希表

遍历数组,统计每个数出现的次数,然后再次遍历哈希表,找到那个次数超过一半的元素返回即可。
时间复杂度为O(n),空间复杂度为O(n),

根据数组特点

我们考虑在遍历数组的时候保存两个值:一个是数组中的一个数组,另一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数+1;如果下一个数字和我们之前保存的数字不同,则次数减一。如果次数为0,我们需要保存下一个数字,并把次数设为1.由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多。那么要找的数字肯定是最后一次把次数设为1时对应的数字。

基于partition算法

我们可以借鉴快速排序的partition算法思路。在随机快速排序算法中,我们先在数组中随机选择一个数字,然后调整数组中数字的排序,使得比选中的数字小的数字都排在它的左边,比选中数字大的都排在它的右边。如果这个选中的数字下标刚好是n/2(假设数组长度为n),那么这个数字就是数组的中位数。如果它的下标大于n/2,那么中位数应该位于它的左边,我们可以接着在它的左边部分数组中查找。如果它的下标小于n/2,那么中位数应该位于它的右边,我们可以接着在它的右边部分数组中进行查找。

程序实现

哈希表

 /**    *  哈希表 O(n) 空间 O(n)时间    **/    public int MoreThanHalfNum_Solution(int [] array) {        if(array==null||array.length==0)            return 0;        HashMap<Integer,Integer>  map=new HashMap<Integer,Integer>();        for(int i=0;i<array.length;i++){            if(map.containsKey(array[i]))                map.put(array[i],map.get(array[i])+1);             else                map.put(array[i],1);        }        for(Map.Entry entry:map.entrySet()){            if((Integer)entry.getValue()>array.length/2)                return (Integer)entry.getKey();        }        return 0;    }

基于数组特点

/**    *  数组特点 O(1)空间复杂度 O(n)时间复杂度    **/    public int MoreThanHalfNum_Solution(int [] array) {        if(array==null||array.length==0)            return 0;        int count=1;        int result=array[0];        for(int i=1;i<array.length;i++){            if(array[i]!=result)                count--;            else                count++;            if(count==0){                count=1;                result=array[i];            }        }        int time=0;        //验证返回是否有效        for(int i=0;i<array.length;i++)            if(array[i]==result)                time++;        return (time>array.length/2)?result:0;    }

基于快速排序的partition算法

public class Solution {    /**    *  数组特点 O(1)空间复杂度 O(n)时间复杂度    **/    public int MoreThanHalfNum_Solution(int [] array) {        if(array==null||array.length==0)            return 0;        int start=0;        int end=array.length-1;        int middle=(start+end)/2;        int index=partition(array,start,end);        while(index!=middle){            //index 大于middle,应该在start,index-1之间寻找            if(index>middle)                index=partition(array,start,index-1);             else              //index 小于middle,应该在index+1,end之间寻找                index=partition(array,index+1,end);        }        int time=0;        //验证返回是否有效        for(int i=0;i<array.length;i++)            if(array[i]==array[middle])                time++;        return (time>array.length/2)?array[middle]:0;    }    private int partition(int[] array,int start,int end){        //取得枢轴元素的索引        int pivot=getPivot(array,start,end);        //将枢轴元素放到最后一个位置        swap(array,pivot,end);        int small=start-1;        //扫描 [start..end-1]元素,使[start..small]< pivot        for(int j=start;j<end;j++){            if(array[j]<array[end]){                small++;                if(small!=j)                    swap(array,small,j);            }        }        //将枢轴元素放到应放的位置        swap(array,++small,end);        return small;           }    /**    *    * 取得array的中枢轴索引,返回[start,end)中的某个索引    */    private int getPivot(int[]array,int start,int end){        if(start==end)            return 0;        return new Random().nextInt(end-start)+1;    }    /**    *交换array 数组中的 i,j索引位置的元素    */    private void swap(int[]array,int i,int j){        int temp=array[i];        array[i]=array[j];        array[j]=temp;    }}
阅读全文
0 0