面試題39:數組中出現次數超過一半的數字
題目要求:
找出數組中出現次數超過數組長度一半的數字。如輸入{1,2,3,2,2,2,5,4,2},則輸出2。
解題思路:
因為該數字的出現次數超過了數組長度的一半,因此可以將問題轉化為求數組的中位數。如果按照這個思路,有以下兩種方式解決:排序后求中位數、用快排的分區函數求中位數(topK問題),這兩種思路都比較簡單,此處不再贅述。
書中還提到一種思路,相當巧妙,可以看作一種特殊的緩存機制。該思路需要一個整型的value變量和一個整型的count變量,記錄緩存值與該緩存值被命中的次數。緩存規則以及執行步驟如下:
步驟1: 緩存值value,命中次數count均初始化為0。
步驟2: 從頭到尾依次讀取數組中的元素,判斷該元素是否等于緩存值:
步驟2.1:如果該元素等于緩存值,則命中次數加一。
步驟2.2:如果該元素不等于緩存值,判斷命中次數是否大于1:
步驟2.2.1:如果命中次數大于1,將命中次數減去1。
步驟2.2.2:如果命中次數小于等于1,則令緩存值等于元素值,命中次數設為1
步驟3: 最終的緩存值value即為數組中出現次數超過一半的數字。
此方法時間復雜度o(n),空間復雜度o(1),實現簡單。
package chapter5;
/**
* Created with IntelliJ IDEA.
* Author: ryder
* Date : 2017/7/28
* Time : 17:51
* Description: 數組中出現次數超過一半的數字
**/
public class P205_MoreThanHalfNumber {
//轉化為topK問題(此處求第k小的值),使用快排的分區函數解決,求第targetIndex+1小的數字(下標為targetIndex)
//書中說這種方法的時間復雜度為o(n),但沒懂為什么。網上也有人說為o(nlogk)
public static int moreThanHalfNum1(int[] data){
if(data==null || data.length==0)
return 0;
int left = 0,right=data.length-1;
//獲取執行分區后下標為k的數據值(即第k+1小的數字)
int k = data.length>>>1;
int index = partition(data,left,right);
while(index!=k){
if(index>k)
index = partition(data,left,index-1);
else
index = partition(data,index+1,right);
}
return data[k];
}
//分區,[小,povot,大]
public static int partition(int[] data,int left,int right){
int pivot = data[left];
while(left<right){
while (left<right && data[right]>=pivot)
right--;
if(left<right)
data[left] = data[right];
while (left<right && data[left]<pivot)
left++;
if(left<right)
data[right] = data[left];
}
data[left] = pivot;
return left;
}
//根據數組特點進行記錄、查找,時間復雜度o(n)
public static int moreThanHalfNum2(int[] data){
if(data==null || data.length==0)
return 0;
int count = 1;
int value = data[0];
for(int i=1;i<data.length;i++){
if(data[i]==value)
count++;
else if(data[i]!=value && count==1)
value = data[i];
else
count--;
}
return value;
}
public static void main(String[] args){
int[] data = {1,2,3,2,2,2,5,4,2};
System.out.println(moreThanHalfNum2(data));
System.out.println(moreThanHalfNum1(data));
}
}
運行結果
2
2