原题
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
方法一
- 由于是一半以上,所以排序后数组的中位数必定是我们所要的值。
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
结果:运行时长 2ms 内存消耗43.2MB。
方法二
- 通过HashMap 来临时存储,并且每次更新出现的最多次数以及对应的数字
public int majorityElement(int[] nums) {
Map<Integer,Integer> map=new HashMap<Integer,Integer>();
int max=1,val,num=nums[0];
for(int i=0,len=nums.length;i<len;i++){
if(map.containsKey(nums[i])){
val=map.get(nums[i])+1;
map.put(nums[i],val);
if(max<val){
max=val;
num=nums[i];
}
}
else{
map.put(nums[i],1);
}
}
return num;
}
结果:运行时长 17ms 内存消耗45.2MB。
方法三
- 根据快排选取的数必定通过一次更新必定在最终位置上。所以每次取排序部分的第一个数作为比较对象,然后看它的最终位置上是否在mid处,不在的话缩小范围。
public int majorityElement(int[] nums) {
int len=nums.length,mid=len/2,pos=-1;
int from=0,to=len-1;
while(pos!=mid){
pos=quickSort(nums,from,to);
if(pos>mid)to=pos-1;
else if(pos<mid)from=pos+1;
}
return nums[mid];
}
public int quickSort(int []f,int from,int to){
if(from==to)return from;
int p=from,q=to,head=f[from];
while(p<q){
while(p<q&&f[q]>=head)q--;
f[p]=f[q];
while(p<q&&f[p]<=head)p++;
f[q]=f[p];
}
f[q]=head;
return q;
}
结果:运行时长 677ms 内存消耗43.4MB。
方法四
- 反思了一下,我觉得可能是原有数据有序,导致重复多次,所以我采用了随机选取基数
public int majorityElement(int[] nums) {
int len=nums.length,mid=len/2,pos=-1;
int from=0,to=len-1;
while(pos!=mid){
pos=quickSort(nums,from,to);
if(pos>mid)to=pos-1;
else if(pos<mid)from=pos+1;
}
return nums[mid];
}
public int quickSort(int []f,int from,int to){
if(from==to)return from;
int pos=(int)(Math.random()*(to-from));
int temp=f[from];
f[from]=f[pos+from];
f[pos+from]=temp;
int p=from,q=to,head=f[from];
while(p<q){
while(p<q&&f[q]>=head)q--;
f[p]=f[q];
while(p<q&&f[p]<=head)p++;
f[q]=f[p];
}
f[q]=head;
return q;
}
结果:运行时长 74ms 内存消耗43.3MB。
方法五
- 然后,我觉得还能优化,百度了一下,快排还有三者取中选择枢轴数的优化,我也试了一下
三者取中选择枢轴数的优化:就是头,尾,中间数三个排序后的中间那个数作为基数。
public int majorityElement(int[] nums) {
int len=nums.length,mid=len/2,pos=-1;
int from=0,to=len-1;
while(pos!=mid){
pos=quickSort(nums,from,to);
if(pos>mid)to=pos-1;
else if(pos<mid)from=pos+1;
}
return nums[mid];
}
public int quickSort(int []f,int from,int to){
if(from==to)return from;
int mid=(from+to)>>1;
if((f[from]<=f[mid]&&f[mid]<=f[to])||(f[to]<=f[mid]&&f[mid]<=f[from]))
swap(f,from,mid);
else if((f[mid]<=f[from]&&f[from]<=f[to])||(f[to]<=f[from]&&f[from]<=f[mid])){}
else
swap(f,to,from);
int p=from,q=to,head=f[from];
while(p<q){
while(p<q&&f[q]>=head)q--;
f[p]=f[q];
while(p<q&&f[p]<=head)p++;
f[q]=f[p];
}
f[q]=head;
return q;
}
public void swap(int f[],int a,int b){
int temp=f[a];
f[a]=f[b];
f[b]=temp;
}
结果:运行时长 10ms 内存消耗43MB。
心累,再怎么优化都没一开始想到的快。
方法六(官方的牛皮解法:投票法)
public int majorityElement(int[] nums) {
int x = 0, votes = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
return x;
}
结果:运行时长 2ms 内存消耗43MB。