剑指offer-例题数组中出现次数超过一半的数字

    科技2022-07-10  257

    题目描述

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

    (一)哈希表法:

            时间效率:O(n)

            空间效率:O(n)

    public class Solution { public int MoreThanHalfNum_Solution(int [] array) { if(array==null) return 0; if(array.length==0) return 0; int len=array.length/2; int[][] index=new int[10][1]; for(int i=0;i<array.length;i++) { index[array[i]][0]++; } for(int i=0;i<10;i++) { if(index[i][0]>len) return i; } return 0; } }

    (二)候选法:最佳解法

    如果数组中存在一个数字的出现次数超过数组长度的一半,那么它的出现次数肯定比其他数字出现次数之和还要大

    从头开始遍历数组,并记录数字和次数,当当前次数为0时,则令当前次数为1并记录数字,否则若数字相同则次数加1,数字不同则次数减1,要找的数字肯定是最后一次把次数设为1时对应的数字,但还需要进行验证,例如1 3 5 7 9最后一次将次数设为1时对应的数字是9,但其出现次数并没有超过数组长度的一半

               时间效率:O(n)

               空间效率:O(1)

    public class Solution { public int MoreThanHalfNum_Solution(int [] array) { if(array==null) return 0; if(array.length==0) return 0; int sum=1; int num=array[0]; for(int i=1;i<array.length;i++) { if(sum==0) { num=array[i]; sum=1; } if(array[i]==num) sum++; else sum--; } //验证 sum=0; for(int i=0;i<array.length;i++) { if(array[i]==num) sum++; } if(sum>array.length/2) return num; else return 0; } }

    (三)排序法

    基于快速排序

    只能在允许改变数组的情况使用

    如果数组中存在一个数字的出现次数超过数组长度的一半,那么它肯定位于排序后数组的中间

               时间复杂度:O(nlogn)

               空间复杂度:O(1)

    public class Solution { public int MoreThanHalfNum_Solution(int [] array) { if(array==null) return -1; if(array.length==0) return -1; int mid=(array.length-1)/2; int start=0; int end=array.length-1; int index=findIndex(array,start,end); while(index!=mid) { if(index>mid) index=findIndex(array,start,index-1); else index=findIndex(array,index+1,end); } int num=array[mid]; int sum=0; //验证 for(int i=0;i<array.length;i++) { if(array[i]==num) sum++; } if(sum>array.length/2) return num; else return 0; } public int findIndex(int[] array,int start,int end) { if(array==null||array.length==0||start>end) return -1; int num=array[start];//不要将start写成0 int i=start; int j=end; while(i<j) { while(array[j]>=num&&i<j)//不要漏掉等号 j--; while(array[i]<=num&&i<j) i++; if(i<j) { int temp=array[i]; array[i]=array[j]; array[j]=temp; } } array[start]=array[i]; array[i]=num; return i; } }

     

    扩展:

    有O(n)的时间复杂度的算法可以得到数组中任意第k大的数字

    public class Test { public static void main(String[] args) { int[] array= {1,3,6,8,2,4,90,34,56,100}; int k=1; System.out.println(get(k-1,array)); } //时间复杂度为O(nlogn) public static int get(int k,int[] array) { if(array==null||array.length==0||k<0) return -1; int start=0; int end=array.length-1; int index=findIndex(array,start,end); while(index!=k) { if(index>k) index=findIndex(array,start,index-1); else if(index<k) index=findIndex(array,index+1,end); } return array[k]; } //进行一次快速排序,返回首元素位于排序后的数组中的位置,时间复杂度为O(n) public static int findIndex(int[] array,int start,int end) { if(array==null||array.length==0) return -1; if(start>end) return -1; int num=array[start];//不要将start写成0 int i=start; int j=end; while(i<j) { while(i<j&&array[j]>=num) j--; while(i<j&&array[i]<=num) i++; if(i<j) { int temp=array[i]; array[i]=array[j]; array[j]=temp; } } array[start]=array[i];//不要将start写成0 array[i]=num; return i; } }

     

    Processed: 0.024, SQL: 8