題目
劍指 Offer 39. 數組中出現次數超過一半的數字
思路1(排序)
- 因為題目說一定會存在超過數組長度一半的一個數字,所以我們將數組排序后,位於
length/2位置的一定是眾數
代碼
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
復雜度分析
- 時間復雜度:\(O(NlogN)\)
- 空間復雜度:\(O(1)\)
思路2(哈希表)
- 遍歷一遍數組,將數組的值作為鍵,出現的次數作為值,存放到哈希表中
- 遍歷哈希表,找到出現次數最多的那一個鍵值對就是我們要的眾數
代碼
class Solution {
public int majorityElement(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int n : nums) {
map.put(n, map.getOrDefault(n, 0) + 1);
}
int max = 0;
int res = 0;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (max < entry.getValue()) {
max = entry.getValue();
res = entry.getKey();
}
}
return res;
}
}
復雜度分析
- 時間復雜度:\(O(N)\)
- 空間復雜度:\(O(N)\)
思路3(分治)
- 將數組從中間開始不斷分成兩份,直到只剩下一個元素時候開始返回
- 如果left和right出現的頻率一樣,直接返回
- 計算left和right在lo~hi范圍內出現的頻率,將高頻率的返回
- 為什么可以用這個方法?比如有
[1, 2, 3, 2, 2, 2, 5, 4, 2],共有9個元素,共會被分成5份(每份一個或者兩個元素),然后從每份中再獲取一個值,共獲取5個值,又因為眾數的個數要大於數組的長度,所以,眾數一定會至少被選中一個,只要被選中一個,那么我們的countInRange函數會根據范圍幫我們計算出最多出現的元素個數即眾數
代碼
class Solution {
public int majorityElement(int[] nums) {
return majorityElementRec(nums, 0, nums.length-1);
}
public int majorityElementRec(int[] nums, int lo, int hi) {
// 如果只有一個元素,那么直接返回
if (lo == hi) {
return nums[lo];
}
// 獲取中間值
int mid = lo + (hi - lo) / 2;
// 遞歸左邊和右邊,直到剩下一個元素
int left = majorityElementRec(nums, lo, mid);
int right = majorityElementRec(nums, mid+1, hi);
// 相等只需要返回一個
if (left == right) {
return left;
}
// 計算left和right在lo~hi范圍中的出現的次數
int leftCount = countInRange(nums, left, lo, hi);
int rightCount = countInRange(nums, right, lo, hi);
// 返回出現次數多的數
return leftCount > rightCount ? left : right;
}
// 統計目標數字在指定范圍出現的次數
public int countInRange(int[] nums, int target, int lo, int hi) {
int count = 0;
for (int i = lo; i <= hi; i++) {
if (nums[i] == target) {
count++;
}
}
return count;
}
}
復雜度分析
- 時間復雜度:\(O(NlogN)\)
- 空間復雜度:\(O(logN)\),遞歸過程中使用了額外的棧空間
思路4(摩爾投票法)
- 眾數記為+1,把其他數記為-1,將它們全部加起來,和最終會大於0
- 假設眾數記為
res,票數記為votes,假設有這么一個數組:[1, 2, 3, 2, 2, 2, 5, 4, 2],使用摩爾投票法的過程如下:
i=0時:因為當前票數為0,所以將1賦值給res,同時票數也加一。此時res=1 votes=1
i=1時:2不等於1,所以票數要減1,此時res=1 votes=0
i=2時:因為票數為0,所以眾數res要指向當前的3,然后票數加一,此時res=3 votes=1
i=3時:2不等於3,所以票數要減1,此時res=3 votes=0
i=4時:因為票數為0,所以眾數res要指向當前的2,然后票數加一,此時res=2 votes=1
i=5時:由於2=2,所以票數加一,此時res=2 votes=2
i=6時:5不等於2,所以票數要減1,此時res=2 votes=1
i=7時:4不等於2,所以票數要減1,此時res=2 votes=0
i=8時:因為票數為0,所以眾數res要指向當前的2,然后票數加一,此時res=2 votes=1
- 最后就直接輸出res即為眾數
代碼
class Solution {
public int majorityElement(int[] nums) {
// 代表結果的眾數
int res = nums[0];
// 統計票數
int votes = 0;
for (int i = 0; i < nums.length; i++) {
// 剛開始是0票,所以把數組的第一個元素作為眾數
// 如果以后的循環votes票數被抵消掉了為0,那么下一個元素就作為眾數
if (votes == 0) {
res = nums[i];
}
// 和當前眾數相同的,那么票數就加1
if (res == nums[i]) {
votes++;
} else {
// 如果和當前票數不同,票數就被抵消掉了一個
votes--;
}
}
return res;
}
}
復雜度分析
- 時間復雜度:\(O(N)\)
- 空間復雜度:\(O(1)\)