剑指offer-39-数组中出现次数超过一半的数字
微信搜索【程序员画工师】关注更多Java编程技术、数据结构与算法、面试题相关内容。
题目
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路1-HashMap
借助于HashMap实现,遍历该数组,用哈希表记录每个元素出现的次数,每次更新次数时判断是否找到目标数字,找到则输出。时间复杂度O(n)、空间复杂度O(n)。
上代码
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length <= 0){
return 0;
}
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int length = array.length;
for(int i=0; i<length; i++){
map.put(array[i], map.getOrDefault(array[i],0)+1);
if(map.get(array[i])*2>length){
return array[i];
}
}
return 0;
}
}
思路2-先排序
如果这个数组是一个排序的数组的话,由于目标在数组中出现的次数超过一半,不难想到数组中间的那个数字就是目标数字。
可得排序的思路:先排序,取中间的数,使用Arrays.sort()方法,底层是快排,时间复杂度为O(nlogn)。
上代码
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length <= 0){
return 0;
}
Arrays.sort(array);
int half = array.length >> 1;
if(!checkHalf(array,array[half])){
return 0;
}
return array[half];
}
public boolean checkHalf(int [] array,int num){
int count = 0;
for(int i = 0;i < array.length;i++){
if(array[i] == num){
count ++;
}
}
if( count * 2 <= array.length){
return false;
}
return true;
}
}
思路3-Partiton
基于Partiton函数:同样是找中位数(长度为n的数组中第n/2大的数字),受快速排序算法的启发,先在数组中随机选一个数字,然后调整数组顺序,使得比选中数字小的数字都排在它的左边,比选中数字大的都排在它的右边。
如果选中的数字的下标是n/2,那么这个数字就是中位数;如果选中的数字的下标大于n/2,则中位数在它的左边,可以接着在它左边部分的数组中查找;如果选中的数字的下标小于n/2,则中位数在它的右边,可以接着在它右边部分的数组中查找。这是一个典型的递归过程。时间复杂度为O(n)。
上代码
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length <= 0){
return 0;
}
int left=0,right=array.length-1;
int index = partition(array,left,right);
while(index!=array.length/2){
if(index<array.length/2)
index = partition(array,index+1,right);
else
index = partition(array,left,index-1);
}
int result = array[index];
if(!checkHalf(array,result)){
return 0;
}
return result;
}
public boolean checkHalf(int [] array,int num){
int count = 0;
for(int i = 0;i < array.length;i++){
if(array[i] == num){
count ++;
}
}
if( count * 2 <= array.length){
return false;
}
return true;
}
public static int partition(int[] nums,int left,int right){
int pivot = nums[left];
while(left<right){
while (left<right && nums[right]>=pivot)
right--;
if(left<right)
nums[left] = nums[right];
while (left<right && nums[left]<pivot)
left++;
if(left<right)
nums[right] = nums[left];
}
nums[left] = pivot;
return left;
}
}
思路4
根据数组的特点可得,数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数的和还要多。
所以有:初始假设第一个数出现次数最大,然后遍历每个数,如果遇到相同,则次数加1,否则次数减1,如果次数为0,则重置当前数为出现次数最多的数,那么要找的数字肯定是最后一次把次数设为1时的数字。
时间复杂度:O(n)
上代码
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length <= 0){
return 0;
}
int result = array[0];
int count = 1;
for(int i = 0;i < array.length;i++){
if(count == 0){
result = array[i];
}else if(result == array[i]){
count ++;
}else{
count --;
}
}
if(!checkHalf(array,result)){
return 0;
}
return result;
}
public boolean checkHalf(int [] array,int num){
int count = 0;
for(int i = 0;i < array.length;i++){
if(array[i] == num){
count ++;
}
}
if( count * 2 <= array.length){
return false;
}
return true;
}
}
注意
•判断数组是否有效:数组长度为0的特殊情况需要处理。
•如果数组中出现频率最高的数字都没有达到这个标准,就需要定义checkHalf函数来进行判断。
References
[1] 《剑指offer(第二版)》 何海涛著
程序员画工师公众号,获取更多详细Java、Python、C、前端、小程序、产品相关学习资料,欢迎交流