題目來源:
https://leetcode.com/problems/majority-element/description/
題目分析:
給定一個n維的數組,尋找出現次數最多的那個元素。那個出現最多的元素至少會在數組中出現一半,要我們求出這個元素。
下面有好幾種方法:
1.暴力搜索的方法
對數組中的元素進行遍歷,然後分別求出每個元素出現的次數,如果該次數大於列表的一半,則輸出這個數字。其具體代碼如下:
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
major_num=len(nums)//2 %python3裏的“/ "表示 浮點數除法,返回浮點結果;" // "表示整數除法
for num in nums:
s=sum(1 for elem in nums if elem==num ) %這裏表示的是每次if判斷正確就sum+1
if (s>major_num):
return num
結果是時間複雜度超過了。
2.HashMap方法
Hash算法實際上就是利用Key計算位置的算法,它利用了字典鍵-值對的特性。在這裏我們採用了python內置模塊collections中的Counter類方法。Collections包含了除了dict,set,list,tuple以外的一些特殊的容器類型。具體可參見文檔:http://docs.python.org/2/library/collections.html。下面主要介紹一下我們使用的Counters類。
Counter類的目的是用來跟蹤值出現的次數。它是一個無序的容器類型,以字典的鍵值對形式存儲,其中元素作爲key,其計數作爲value。計數值可以是任意的Interger(包括0和負數)。Counter類和其他語言的bags或multisets很相似。因此本題的代碼可以寫成:
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
counts=collections.Counter(nums)
return max(counts.keys(),key=counts.get)
爲了更好的理解代碼,我們可以把上述程序改寫爲:
import collections
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
counts=collections.Counter(nums) %從一個可迭代對象(list,tuple,dict,字符串)創建Counter類
return max(counts.keys(),key=lambda x:counts.get(x))
Counter類的存儲是一個字典,而get方法是字典中用來得到值的。lambda方法定義了一個匿名類,counts.get(x)得到了counts中所有的值,keys代表了counts中所有的鍵,max函數如果有關鍵字key,用法就是按照key來選最大值,而key是get函數,得到的是值,因此最後一句話就返回了值最大的key,也就是出現次數最多的數。
3.排序法
因爲出現次數最多的數字佔據了整個數組的一半之多,那麼將數組進行排序後,其中間的那個數一定就是我們要找的數字。其代碼爲:
class Solution:
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums.sort()
return (nums[len(nums)//2])
4.最大投票算法(需要學習的算法)Moore's voting algorithm
該算法主要用來解決在元素列表中找到出現元素次數最多的元素。具體可以看http://www.cs.utexas.edu/~moore/best-ideas/mjrty/index.html。算法的思想是:每次都找出一對不同的元素,從數組中刪掉,直到數組爲空或只有一種元素。
不難證明,如果存在元素e出現頻率超過半數,那麼數組中最後剩下的就只有e。當然,最後剩下的元素也可能並沒有出現半數以上。比如說數組是[1, 2, 3],最後剩下的3顯然只出現了1次,並不到半數。排除這種false positive情況的方法也很簡單,只要保存下原始數組,最後掃描一遍驗證一下就可以了。
因此該程序爲:
class Solution {
public int majorityElement(int[] nums) {
int len = nums.length, candidate=nums[0], count=1;
for(int i=1; i<len; i++) {
if(nums[i]==candidate)
count++;
else {
count--;
if(count==0) {
candidate=nums[i];
count=1;
}
}
}
return candidate;
}
}
如果要採用這個算法,題目中出現次數大於列表的一半是必要條件,因爲我們每次在移除時,最壞的情況是每次移除一個出現次數最多的數和一個非目標數,要確保剩下的數爲出現次數最多的數,則需要它大於列表的一半。
關於這個算法的改進與變形,可以參看:https://blog.csdn.net/huanghanqian/article/details/74188349