1.只出現一次的數字(136,簡單)
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
說明:
你的算法應該具有線性時間複雜度。 你可以不使用額外空間來實現嗎?
示例1:
輸入:[2,2,1]
輸出:1
示例2:
輸入:[4,1,2,1,2]
輸出:4
解法一:
我首先想到的比較蠢的方法,直接存到字典裏,把數組裏的數當作key,如果字典裏沒有這個鍵值就設爲1,有的話就加一,最後取出值爲1的key值。
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dict = {}
for item in nums:
if item in dict:
dict[item] += 1
else:
dict[item] = 1
for key in dict:
if dict[key] == 1:
return key
解法二:
首先要了解一個事情,異或操作,兩個相同的數異或結果爲0,一個數跟0異或是這個數本身,異或是對於二進制來說的,一個數異或自己本身結果爲零(全部二進制位相同) 一個數異或零結果爲這個數本身(0和0異或爲0,1和0異或爲1,所以結果還是原來的數) 而一個數異或另一個數兩次就是相當於一個數異或零 所以結果還是這個數本身。所以當數組中只有一個數出現了一次,其他數都出現兩次時,初始化一個0對數組裏每一個數進行異或,結果就是這個數本身。
python的異或符號爲^
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
num = 0
for i in nums:
num ^= i
return num
2.求衆數(169,簡單)
給定一個大小爲 n 的數組,找到其中的衆數。衆數是指在數組中出現次數大於 ⌊ n/2 ⌋ 的元素。
你可以假設數組是非空的,並且給定的數組總是存在衆數。
示例1:
輸入: [3,2,3]
輸出: 3
示例2:
輸入: [2,2,1,1,1,2,2]
輸出: 2
解法一:
和上一題思路一樣,定義一個字典,key值爲數組的數字,value爲它出現的次數,最後從字典裏找出value值大於n/2的值。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dict = {}
for i in nums:
if i in dict:
dict[i] += 1
else:
dict[i] = 1
num = len(nums)/2
for i in dict:
if dict[i]>num:
return i
改進:
可以用max函數,關於max函數可以參考這篇文章
dict = {}
for i in nums:
if i in dict:
dict[i] += 1
else:
dict[i] = 1
return max(dict.items(), key=lambda x: x[1])[0]
dict.items()返回的是可以遍歷的(鍵,值)元組數組
print(dict.items())
# dict_items([(1, 3), (2, 4)])
print(max(dict.items(), key=lambda x: x[1]))
# (2,6)
print(max(dict.items(), key=lambda x: x[1])[0])
# 2
解法二:
比較雞賊的方法,由於衆數的數量是大於n/2的,所以我們把nums用sorted函數排序,得到的list的第n/2個就是所求答案。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return sorted(nums)[len(nums)/2]
3.搜索二維矩陣Ⅱ(240,中等)
編寫一個高效的算法來搜索 m x n 矩陣 matrix 中的一個目標值 target。該矩陣具有以下特性:
- 每行的元素從左到右升序排列。
- 每列的元素從上到下升序排列。
示例:
現有矩陣matrix如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
給定target = 5,返回true。
給定target = 20, 返回false。
解法一:
暴力解法,遍歷數組
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if len(matrix) == 0 or len(matrix[0]) == 0:
return False
for i in range(len(matrix)):
for j in range(len(matrix[i])):
if matrix[i][j] == target:
return True
return False
寫的更簡潔的方法:
class Solution:
def searchMatrix(self, matrix, target):
if not matrix:
return False
for x in matrix:
for c in x:
if target== c:
return True
return False
解法二:
對於每一行用二分法:
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
for i in range(len(matrix)):
first = 0
last = len(matrix[i]) - 1
while first <= last:
mid = (last + first) // 2
if target < matrix[i][mid]:
last = mid - 1
elif target > matrix[i][mid]:
first = mid +1
else:
return True
return False
這種方法沒有利用到每一列也是排序好的信息,不是最佳。
解法三:
我們可以觀察到,對於矩陣的右上角,所有在它下面的數都比他大,所有在他左邊的數都比他小,所以我們可以從右上角的數開始與target對比,target比它大行數就加一,比它小列數就減一。
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if not matrix:
return False
row = 0
col = len(matrix[0]) - 1
rows = len(matrix)-1
while row <= rows and col >= 0:
if target == matrix[row][col]:
return True
elif target > matrix[row][col]:
row += 1
else:
col -= 1
return False
4.合併兩個有序數組(88,簡單)
給定兩個有序整數數組 nums1 和 nums2,將 nums2 合併到 nums1 中,使得 num1 成爲一個有序數組。
說明:
- 初始化 nums1 和 nums2 的元素數量分別爲 m 和 n。
- 你可以假設 nums1 有足夠的空間(空間大小大於或等於 m + n)來保存 nums2 中的元素。
示例:
輸入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
輸出:[1,2,2,3,5,6]
解法一:
class Solution(object):
def merge(self, nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: None Do not return anything, modify nums1 in-place instead.
"""
while m > 0 and n > 0:
if nums1[m-1] > nums2[n-1]:
nums1[m+n-1] = nums1[m-1]
m -= 1
else:
nums1[m+n-1] = nums2[n-1]
n -= 1
if n > 0:
nums1[:n] = nums2[:n]
return None
5.驗證迴文串(125,簡單)
給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。
迴文串就是正讀反讀相同。
說明: 本題中,我們將空字符串定義爲有效的迴文串。
示例1:
輸入: "A man, a plan, a canal: Panama"
輸出: true
示例2:
輸入: "race a car"
輸出: false
這道題主要是對字符串的操作不太熟悉,思路很清晰,把字符串轉換成只包含字母和數字的列表,之後比較原列表和倒序後的列表是否相同就可以了。
解法一:
class Solution(object):
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
list = []
for i in s:
if i.isalpha() or i.isdigit():
list.append(i.lower())
return True if list == list[::-1] else False
後來發現,其實沒必要轉成列表,字符串也可以實現同樣的操作,原理一樣。
解法二:
class Solution(object):
def isPalindrome(self, s):
"""
:type s: str
:rtype: bool
"""
s = s.lower()
newStr = ""
for i in s:
if i.isalnum():
newStr += i
return newStr==newStr[::-1]
最後實測,用第一種列表的方法速度要快了十倍左右。
解法三:
最簡潔的解法,用到了一個高級的函數filter。
filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。
該接收兩個參數,第一個爲函數,第二個爲序列,序列的每個元素作爲參數傳遞給函數進行判,然後返回 True 或 False,最後將返回 True 的元素放到新列表中。
filter(function, iterable)
# funcion --判斷函數
# iterable --可迭代對象
# 注意: Pyhton2.7 返回列表,Python3.x 返回迭代器對象
python2解法:
class Solution(object):
def isPalindrome(self, s):
"""
:type s: str
:rtype: boolp
"""
s = filter(str.isalnum, str(s.lower()))
return s == s[::-1]
python3解法:
class Solution:
def isPalindrome(self, s: str) -> bool:
s = list(filter(str.isalnum, s.lower()))
return s == s[::-1]
可以看到python3的filter返回對象必須list之後才能繼續操作,否則會報錯:
Line 4: TypeError: 'filter' object is not subscriptable
6.兩數之和Ⅱ-輸入有序數組(167,簡單)
給定一個已按照升序排列的有序數組,找到兩個數使得它們相加之和等於目標數。
函數應該返回這兩個下標值 index1 和 index2,其中 index1必須小於index2。
說明:
- 返回的下標值(index1 和 index2)不是從零開始的。
- 你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。
示例:
輸入: numbers = [2, 7, 11, 15], target = 9
輸出: [1,2]
解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。
解法:
這題主要還是抓住題目中數組是升序排列的條件,可以設置兩個指針,分別從數組的最左和最右開始判斷,假如比目標值小,左邊的指針就加一,比目標值大右邊的指針就減一。
class Solution(object):
def twoSum(self, numbers, target):
"""
:type numbers: List[int]
:type target: int
:rtype: List[int]
"""
left = 0
right = len(numbers)-1
while left<right:
if numbers[left]+numbers[right] == target:
return [left+1, right+1]
elif numbers[left]+numbers[right] < target:
left+=1
else:
right-=1
7.數組中的第K個最大元素(215,中等)
在未排序的數組中找到第k個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。
示例1:
輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5
示例2:
輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4
說明:
你可以假設 k 總是有效的,且 1 ≤ k ≤ 數組的長度。
解法一:
第k大的元素,只要把數組排序取第K個或第-k個元素就可以了。
list.sort(cmp=None, key=None, reverse=False)
cmp -- 可選參數, 如果指定了該參數會使用該參數的方法進行排序。
key -- 主要是用來進行比較的元素,只有一個參數,具體的函數的參數就是取自於可迭代對象中,指定可迭代對象中的一個元素來進行排序。
reverse -- 排序規則,reverse = True 降序, reverse = False 升序(默認)。
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
nums.sort()
return nums[-k]
解法二:
用了快排的思想,快排的思想就是找一個基準點,把比基準點大的放到左邊,比基準點小的放到右邊,這樣基準點的位置就是正確的,即左邊都比他大,右邊都比他小,在這道題中,不需要把所有的數組全部排好,假如排好基準點恰好在第K個位置上,那麼他就是第k大的元素。
快排的實現思想值得思考。首先設置一個index,然後循環list,比較list當前值與基準值(這裏取得最後一個元素)的大小,當比基準值大時,就把list[index]與list[i]互換,之後index加一,最後再把list[index]與list[high]互換,就完成了一次操作,之後再進行判斷,來更改high或low的位置繼續進行這個操作。
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
low, high = 0, len(nums)-1
while low <= high:
pivot = self.findpivot(nums, high, low)
if pivot == k-1:
return nums[pivot]
elif pivot > k-1:
high = pivot-1
else:
low = pivot+1
def findpivot(self, nums, high, low):
index = low
pivot = nums[high]
for i in range(low, high):
if nums[i] >= pivot:
nums[index], nums[i] = nums[i], nums[index]
index += 1
nums[index], nums[high] = nums[high], nums[index]
return index
解法三:
維護一個最小堆,堆可以看作一個完全二叉樹,最小堆的堆頂一定是最小的那個數,最大堆堆頂一定是最大的。
python中內置的堆模塊爲heapq
heaqp模塊提供了堆隊列算法的實現,也稱爲優先級隊列算法。
要創建堆,請使用初始化爲[]的列表,或者可以通過函數heapify()將填充列表轉換爲堆。
提供以下功能:
Usage:
heap = [] # creates an empty heap
heappush(heap, item) # pushes a new item on the heap
item = heappop(heap) # pops the smallest item from the heap
item = heap[0] # smallest item on the heap without popping it
heapify(x) # transforms list into a heap, in-place, in linear time
item = heapreplace(heap, item) # pops and returns smallest item, and adds
# new item; the heap size is unchanged
用堆來實現就比較簡單了,循環列表加入堆,當堆的size大於k了就彈出堆頂,最後的堆頂就是第k大的值。
class Solution(object):
def findKthLargest(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
import heapq
heap = []
heapq.heapify(heap)
for num in nums:
heapq.heappush(heap, num)
if len(heap)>k:
heapq.heappop(heap)
return heap[0]
8.前K個高頻元素(347,中等)
給定一個非空的整數數組,返回其中出現頻率前 k 高的元素。
示例1:
輸入: nums = [1,1,1,2,2,3], k = 2
輸出: [1,2]
示例2:
輸入: nums = [1], k = 1
輸出: [1]
說明:
- 你可以假設給定的 k 總是合理的,且 1 ≤ k ≤ 數組中不相同的元素的個數。
- 你的算法的時間複雜度必須優於 O(n log n) , n 是數組的大小。
解法一:
用一個字典來統計頻次,最後根據字典的value值進行排序,然後循環k次放到列表裏返回。
根據字典的value或者key排序要可以用lamada表達式,關於lamada表達式:
Python提供了很多函數式編程的特性,如:map、reduce、filter、sorted等這些函數都支持函數作爲參數,lambda函數就可以應用在函數式編程中。如下:
# 需求:將列表中的元素按照絕對值大小進行升序排列
list1 = [3,5,-4,-1,0,-2,-6]
sorted(list1, key=lambda x: abs(x))
當然,也可以如下:
list1 = [3,5,-4,-1,0,-2,-6]
def get_abs(x):
return abs(x)
sorted(list1,key=get_abs)
只不過這種方式的代碼看起來不夠Pythonic
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
dict = {}
re = []
for num in nums:
if num in dict:
dict[num] += 1
else:
dict[num] = 1
s = sorted(dict.items(), key=lambda x:x[1], reverse=True)
print(s)
for i in range(k):
re.append(s[i][0])
return re
這種解法的時間複雜度是:
O(nlogn)
不符合題目要求(還是不太懂怎麼計算複雜度)
解法二:
求出出現頻率最高的k個數,當我們通過dict統計出頻率之後,其實和上一題一樣都是一個排序之後輸出前k大個數,所以依然可以使用堆來實現。
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
dict = {}
list = []
re = []
for num in nums:
if num in dict:
dict[num] += 1
else:
dict[num] = 1
heapq.heapify(list)
for key, value in dict.items():
if len(list) < k:
heapq.heappush(list, (value, key))
elif value > list[0][0]:
heapq.heapreplace(list, (value, key))
while list:
re.append(heapq.heappop(list)[1])
return re
複雜度分析:
- 時間複雜度:O(nlogk),其中 n 表示數組的長度。首先,遍歷一遍數組統計元素的頻率,這一系列操作的時間複雜度是 O(n) 的;接着,遍歷用於存儲元素頻率的 map,如果元素的頻率大於最小堆中頂部的元素,則將頂部的元素刪除並將該元素加入堆中,這一系列操作的時間複雜度是 O(nlogk) 的;最後,彈出堆中的元素所需的時間複雜度是 O(klogk) 的。因此,總的時間複雜度是 O(nlogk) 的。
- 空間複雜度:O(n),最壞情況下(每個元素都不同),map 需要存儲 n 個鍵值對,優先隊列需要存儲 k 個元素,因此,空間複雜度是 O(n) 的。
解法三:
最後,爲了進一步優化時間複雜度,可以採用桶排序(bucket sort),即用空間複雜度換取時間複雜度。
第一步和解法二相同,也是統計出數組中元素的頻次。接着,將數組中的元素按照出現頻次進行分組,即出現頻次爲 i 的元素存放在第 i 個桶。最後,從桶中逆序取出前 k 個元素。
桶排序詳見這裏
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
dict = {}
re = []
for num in nums:
if num in dict:
dict[num] += 1
else:
dict[num] = 1
bucket = [[] for _ in range(len(nums) + 1)]
# 這裏的輸出是 [[], [], [], [], [], [], [], [], [], []]
for key, value in dict.items():
bucket[value].append(key)
for i in range(len(nums), -1, -1): # range(start,stop,step) 不包含stop,所以到-1,就是到0
if bucket[i]:
re.extend(bucket[i])
if len(re) >= k:
break
return re[:k]
extend和append的區別就是,append會把整體當作一個元素加到列表裏,extend會把整體拆成元素加進去,如:
music_media = ['compact disc', '8-track tape', 'long playing record']
new_media = ['DVD Audio disc', 'Super Audio CD']
music_media.append(new_media)
print music_media
>>>['compact disc', '8-track tape', 'long playing record', ['DVD Audio disc', 'Super Audio CD']]
複雜度分析
- 時間複雜度:O(n),其中n表示數組的長度。
- 空間複雜度:O(n)