目錄
169_求衆數(Majority-Element)
這道題有 5 種方法,8 種實現,詳細分析可以看花花醬的 YouTube 專欄。
描述
給定一個大小爲 n 的數組,找到其中的衆數。衆數是指在數組中出現次數大於 ⌊ n/2 ⌋
的元素。
你可以假設數組是非空的,並且給定的數組總是存在衆數。
示例 1:
輸入: [3,2,3]
輸出: 3
示例 2:
輸入: [2,2,1,1,1,2,2]
輸出: 2
解法一:暴力法
思路
遍歷數組中的每個元素,統計該元素出現的次數(嵌套遍歷),如果該元素出現的次數 \(> \left \lfloor n/2 \right \rfloor\),則該元素就是數組的衆數。
Java 實現
class Solution {
public int majorityElement(int[] nums) {
int majorityCount = nums.length / 2;
for (int num1 : nums) {
int count = 0;
for (int num2 : nums) {
if (num2 == num1) {
++count;
}
}
if (count > majorityCount) {
return num1;
}
}
throw new IllegalArgumentException("The array does not contain a majority element!");
}
}
Python 實現
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
majority_count = len(nums) // 2
for num1 in nums:
count = sum(1 for num2 in nums if num2 == num1)
if count > majority_count:
return num1
複雜度分析
- 時間複雜度:\(O(n^2)\),其中 \(n\) 表示數組的長度,由於嵌套了兩層
for
循環,因此總的時間複雜度是 \(O(n^2)\) 的 - 空間複雜度:\(O(1)\)
解法二:哈希表
思路
利用哈希表記錄數組中元素出現的次數,由於哈希表的插入操作的時間複雜度是 \(O(1)\) 的,所以遍歷整個數組統計出現次數的操作的時間複雜度是 \(O(n)\) 的。接着,再遍歷一遍哈希表,取出衆數。
Java 實現
class Solution {
public int majorityElement(int[] nums) {
Map<Integer, Integer> counts = new HashMap<>();
for (int num : nums) {
if (counts.containsKey(num)) {
counts.replace(num, counts.get(num) + 1);
} else {
counts.put(num, 1);
}
}
Map.Entry<Integer, Integer> majorityEntry = null;
for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {
if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {
majorityEntry = entry;
}
}
return majorityEntry.getKey();
}
}
Python 實現
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
counts = dict()
for num in nums:
counts[num] = counts.get(num, 0) + 1
return max(counts, key=counts.get)
複雜度分析
- 時間複雜度:\(O(n)\),其中 \(n\) 爲數組的長度。由於哈希表中元素的數目最多爲 \(n - \left( \left \lfloor n/2 \right \rfloor + 1 \right) + 1 = n - \left \lfloor n/2 \right \rfloor\),因此遍歷一次哈希表最多需要 \(n - \left \lfloor n/2 \right \rfloor\) 次操作,而遍歷一遍數組需要 \(n\) 次操作,所以總的時間複雜度是 \(O(n)\) 的
- 空間複雜度:\(O(n)\),因爲哈希表最多需要保存 \(n - \left \lfloor n/2 \right \rfloor\) 個元素
解法三:排序
將數組按照順序(遞增或者遞減)排列好後,索引爲 \(\left \lfloor n/2 \right \rfloor\) 的元素就是數組的衆數。
Java 實現
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}
Python 實現
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return sorted(nums)[len(nums) // 2]
複雜度分析
- 時間複雜度:\(O(n \log(n))\),其中 \(n\) 表示數組的長度,對數組進行排序的時間複雜度爲 \(O(n \log(n))\) 的
- 空間複雜度:\(O(n)\) 或者 \(O(1)\),取決於是否可以直接對原數組直接進行排序,如果不允許的話,需要額外的空間複製數組
解法四:隨機選擇【待完成】
思路
Java實現
Python 實現
複雜度分析
解法五:分而治之(Divide and conquer)【待完成】
思路
Java 實現
Python 實現
複雜度分析
解法六:多數投票算法(Boyer-Moore majority vote algorithm)
思路
多數投票算法一般用於尋找一個序列的多數元素(只需要線性時間和常數空間),是一種典型的流式算法(streaming algorithm)。但是,一般來說,該算法無法找到一個序列的衆數(mode),除非衆數出現的次數大於 \(\lfloor n/2 \rfloor\) 次。多數投票算法的思想是這樣:統計一個序列中的所有元素,將多數元素記爲 \(+1\),其餘的元素記爲 \(-1\),那麼最後的和一定是正的。具體地,該算法會維護兩個變量,一個用於記錄序列中的元素,記爲 m
,一個作爲計數器,記爲 count
。遍歷數組中的每個元素,如果當前的 count
爲 0,則將當前元素保存在 m
中,並設 count
爲1;如果 count
不爲0,則判斷當前元素與 m
是否相等,相等則 count
加一,不等則 count
減一。遍歷結束,變量 m
就是我們尋找的多數元素。
Java 實現
class Solution {
public int majorityElement(int[] nums) {
int me = nums[0], count = 1;
for (int i = 1; i < nums.length; ++i) {
if (count == 0) {
me = nums[i];
count = 1;
} else if (me == nums[i]) {
++count;
} else {
--count;
}
}
return me;
}
}
Python 實現
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
me, count = 0, 0
for num in nums:
if count == 0:
me, count = num, 1
elif me == num:
count += 1
else:
count -= 1
return me
複雜度分析
- 時間複雜度:\(O(n)\),其中 \(n\) 表示數組的長度
- 空間複雜度:\(O(1)\)