算法系列——陣列中出現次數超過一半的數字(劍指offer)
阿新 • • 發佈:2019-02-16
題目描述
陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為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;
}
}