title: 每日一練(20):陣列中出現次數超過一半的數字
categories:[劍指offer]
tags:[每日一練]
date: 2022/02/16
每日一練(20):陣列中出現次數超過一半的數字
陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。
你可以假設陣列是非空的,並且給定的陣列總是存在多數元素。
示例 1:
輸入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
輸出: 2
限制:
1 <= 陣列長度 <= 50000
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/probl...
方法一:雜湊表
思路
我們知道出現次數最多的元素大於n/2
次,所以可以用雜湊表來快速統計每個元素出現的次數。
演算法
我們使用雜湊對映(HashMap)來儲存每個元素以及出現的次數。對於雜湊對映中的每個鍵值對,鍵表示一個元素,值表示該元素出現的次數。
我們用一個迴圈遍歷陣列 nums 並將陣列中的每個元素加入雜湊對映中。在這之後,我們遍歷雜湊對映中的所有鍵值對,返回值最大的鍵。我們同樣也可以在遍歷陣列 nums 時候使用打擂臺的方法,維護最大的值,這樣省去了最後對雜湊對映的遍歷。
複雜度分析
- 時間複雜度:O(n),其中 n 是陣列 nums 的長度。我們遍歷陣列 nums 一次,對於 nums 中的每一個元素,將其插入雜湊表都只需要常數時間。如果在遍歷時沒有維護最大值,在遍歷結束後還需要對雜湊表進行遍歷,因為雜湊表中佔用的空間為 O(n)(可參考下文的空間複雜度分析),那麼遍歷的時間不會超過 O(n)。因此總時間複雜度為 O(n)。
- 空間複雜度:O(n)。雜湊表最多包含
n - n/2
個鍵值對,所以佔用的空間為 O(n)。這是因為任意一個長度為 n 的陣列最多隻能包含 n 個不同的值,但題中保證 nums 一定有一個眾數,會佔用(最少)n/2 + 1
個數字。因此最多有n - (n/2 +1)
個不同的其他數字,所以最多有n - n/2
個不同的元素。
int majorityElement(vector<int>& nums) {
unordered_map<int, int> counts;
int majority = 0, cnt = 0;
for (int num : nums) {
++counts[num];
if (counts[num] > cnt) {
majority = num;
cnt = counts[num];
}
}
return majority;
}
方法二:排序
思路
如果將陣列 nums 中的所有元素按照單調遞增或單調遞減的順序排序,那麼下標為 n/2
的元素(下標從 0 開始)一定是眾數。
演算法
對於這種演算法,我們先將 nums 陣列排序,然後返回上文所說的下標對應的元素。
複雜度分析
- 時間複雜度:O(n log n)。將陣列排序的時間複雜度為 O(n log n)。
- 空間複雜度:O(log n)。如果使用語言自帶的排序演算法,需要使用 O(log n) 的棧空間。如果自己編寫堆排序,則只需要使用 O(1) 的額外空間。
int majorityElement(vector<int>& nums) {
sort(nums.begin(), nums.end());
return nums[nums.size() / 2];
}
方法三:隨機化
思路
因為超過 n/2
的陣列下標被眾數佔據了,這樣我們隨機挑選一個下標對應的元素並驗證,有很大的概率能找到眾數。
演算法
由於一個給定的下標對應的數字很有可能是眾數,我們隨機挑選一個下標,檢查它是否是眾數,如果是就返回,否則繼續隨機挑選。
int majorityElement(vector<int>& nums) {
while (true) {
int candidate = nums[rand() % nums.size()];
int count = 0;
for (int num : nums) {
if (num == candidate) {
++count;
}
if (count > nums.size() /2)
}
}
}