轉載本文章請標明作者和出處
本文出自《Darwin的程序空間》
本文題目和部分解題思路來源自《劍指offer》第二版
題目
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
解題分析
這個問題,解決不難,有很多種方案可以解決這個問題:
比如說我們可以使用哈希表(也就是HashMap),鍵爲出現的數字,值爲出現的次數,每遍歷一個元素就把它的次數加1,直到有一個數字出現的次數大於數據的size,這個數就是衆數,這種方式很好理解,時間複雜度爲O(n),但是需要的空間也是O(n);
再比如我們可以使用排序數組的方法進行,排序後的數組,數組的中間數即爲衆數(出現最多的那個數),數組排序的時間複雜度爲O(nlgn)
我們還可以使用partiton函數的做法,時間複雜度爲O(n),但是會修改原有的數據結構,需要的可以自行去百度一下這種算法,但是面試的時候需要詢問面試官是否可以修改原有的數據結構
筆者推薦的是一種叫做投票算法的實現;
就比如我們小學的時候選舉班長,選舉的時候如果有一個人獲取了全班一半以上人支持的話,他就當選了;
比如參加選舉的同學有四個[小A、小B、小C和小D]
如果最後是小A當選的話,那麼一定小B、小C和小D加起來的票數還沒有小A多,所以每次如果叫一個選擇小B或小C或小D的和選擇小A的出教室,最後教室裏面剩下的一定是選擇小A的,那麼如果是每次叫兩個選擇不一樣的人出教室,最後剩下的那個也一定是選擇小A的。道理很簡單,小B、小C和小D加起來都沒人家多,現在還有可能小B和小C和小D自行消耗,就更不可能有小A的多了。
這就是投票算法。
所以我們只需要記住當前遍歷數組的數字middle,和它出現的次數count,如果有和它不一樣的,就把count–,如果count小於1,那麼就換下一個數字,如果出現的和這個數字一樣的,就把count加1,最後剩下的一定是數組中出現次數超過一半的數字,也就是衆數。
但是要注意,這種做法,數組裏面一定要有衆數纔行,就算沒有衆數,這種算法也會給你一個答案的,如果還要判斷數組裏面是否存在衆數,那推薦你使用哈希表。
代碼(JAVA、python實現)
ps:這裏筆者使用的jdk爲1.8,python3.7版本
- java實現
public class Offer39_Mode {
public static void main(String[] args) {
System.out.println(mode(new int[]{1, 1, 1, 2, 2, 3, 4, 5, 2, 1, 1, 1, 1}));
}
public static int mode(int[] arr) {
if (Objects.isNull(arr) || arr.length == 0) {
return -1;
}
// 目前出現次數最多的數字
int middle = arr[0];
// 統計出現次數,用來抵消其他的數字
int count = 1;
for (int i = 1; i < arr.length; i++) {
int num = arr[i];
if (count == 0) {
middle = num;
count = 1;
} else if (num == middle) {
count++;
} else {
count--;
}
}
return middle;
}
}
- python實現
class Solution:
def majorityElement(self, nums: List[int]) -> int:
x = 1
y = nums[0]
for i in range(len(nums)):
if i == 0:
continue
if x == 0:
y = nums[i]
if nums[i] != y:
x -= 1
else:
x += 1
return y