目錄
243. 最短單詞距離
https://leetcode-cn.com/problems/shortest-word-distance/
給定一個單詞列表和兩個單詞 word1 和 word2,返回列表中這兩個單詞之間的最短距離。
示例:假設 words = ["practice", "makes", "perfect", "coding", "makes"],輸入: word1 = “coding”, word2 = “practice”,輸出: 3輸入: word1 = "makes", word2 = "coding",輸出: 1
注意:你可以假設 word1 不等於 word2, 並且 word1 和 word2 都在列表裏。
思路
一:兩重循環,查找是否有符合條件的,並更新最短距離,時間複雜度O(kn^2),n是words數組的長度,k是words中單詞的最大長度。
二:優化的二重循環,時間複雜度O(nk+n^2),rec_1和rec_2分別記錄了word1和word2在words中的下標,然後在倆數組中找到最小的差值即ok了。
class Solution(object):
def shortestDistance(self, words, word1, word2):
"""
:type words: List[str]
:type word1: str
:type word2: str
:rtype: int
"""
rec_1, rec_2 = [], []
for i in range(len(words)):
if words[i] == word1:
rec_1.append(i)
elif words[i] == word2:
rec_2.append(i)
res = abs(rec_1[0] - rec_2[0])
for i in rec_1:
for j in rec_2:
res = min(res, abs(i-j))
return res
三:用idx1和idx2分別記錄word1和word2的最新出現的位置,每次發現一個新的單詞,不必要再循環遍歷,因爲已經記錄過最新的位置,時間複雜度O(kn)。
class Solution(object):
def shortestDistance(self, words, word1, word2):
res, idx1, idx2 = len(words), -1, -1
for i in range(len(words)):
if words[i] == word1:
idx1 = i
elif words[i] == word2:
idx2 = i
if idx1 != -1 and idx2!= -1:
res = min(res, abs(idx1 - idx2))
return res
766. 託普利茨矩陣
https://leetcode-cn.com/problems/toeplitz-matrix/
如果一個矩陣的每一方向由左上到右下的對角線上具有相同元素,那麼這個矩陣是託普利茨矩陣。
給定一個 M x N 的矩陣,當且僅當它是託普利茨矩陣時返回 True。
示例 1:輸入: matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]],輸出: True,解釋:在上述矩陣中, 其對角線爲:"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。各條對角線上的所有元素均相同, 因此答案是True。
示例 2:輸入:matrix = [[1,2],[2,2]],輸出: False,解釋: 對角線"[1, 2]"上的元素不同。
說明: matrix 是一個包含整數的二維數組。matrix 的行數和列數均在 [1, 20]範圍內。matrix[i][j] 包含的整數在 [0, 99]範圍內。
思路
一:對角線
class Solution(object):
def isToeplitzMatrix(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
rec = [0] * (m + n - 1)
idx = 0
for i in range(m -1 , 0, -1):
rec[idx] = matrix[i][0]
idx += 1
for j in range(0, n):
rec[idx] = matrix[0][j]
idx += 1
for i in range(m):
for j in range(n):
if matrix[i][j] != rec[j - i + m - 1]:
return False
return True
二:檢查左上鄰居。
面試題 17.04. 消失的數字
https://leetcode-cn.com/problems/missing-number-lcci/
數組nums包含從0到n的所有整數,但其中缺了一個。請編寫代碼找出那個缺失的整數。你有辦法在O(n)時間內完成嗎?
注意:本題相對書上原題稍作改動
示例 1:輸入:[3,0,1],輸出:2
示例 2:輸入:[9,6,4,2,3,5,7,0,1],輸出:8
思路
一:創建一個n+1個元素的列表,若某值出現,則以該值爲下標的元素計1。
class Solution(object):
def missingNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
rec = [0] * (len(nums)+1)
for num in nums:
rec[num] = 1
for i in range(len(rec)):
if rec[i] == 0:
return i
二:copy一下leetcode上大神的解法,https://leetcode-cn.com/problems/missing-number-lcci/solution/onshi-jian-fu-za-du-o1kong-jian-fu-za-du-shi-xian-/,借用異或操作,res = res ^ x ^ x。對同一個值異或兩次,那麼結果等於它本身,所以我們對res從0-nums.length進行異或,同時對nums數組中的值進行異或,出現重複的會消失,所以最後res的值是隻出現一次的數字,也就是nums數組中缺失的那個數字。
class Solution(object):
def missingNumber(self, nums):
res = 0
for i in range(len(nums)):
res ^= i
res ^= nums[i]
res ^= len(nums)
return res
169. 多數元素
https://leetcode-cn.com/problems/majority-element/
給定一個大小爲 n 的數組,找到其中的多數元素。多數元素是指在數組中出現次數大於 ⌊ n/2 ⌋ 的元素。你可以假設數組是非空的,並且給定的數組總是存在多數元素。
示例 1:輸入: [3,2,3],輸出: 3
示例 2:輸入: [2,2,1,1,1,2,2],輸出: 2
思路
一:排序,因爲此處衆數的定義是出現次數大於 ⌊ n/2 ⌋ 的元素,且保證存在,則該數一定是下標⌊ n/2 ⌋的元素,例如長度爲3,則下標1上的元素一定是結果;長度爲4,則下標2上的元素一定是結果。
class Solution(object):
def majorityElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums = sorted(nums)
return nums[len(nums)//2]
二:消消樂。開闢一個列表,列表的第一個元素放候選衆數,第二個元素放計數,若遍歷的元素與候選衆數同,則計數加1,若不同,計數減1;若計數爲0,則將該數作爲新的候選衆數,並計1;最終的結果第一個元素是衆數,第二個元素也必然大於0。其實方法一和方法二能用都是因爲題目中衆數的定義,長度必須佔到 ⌊ n/2 ⌋ +1。後面看了一下題解,其實與官方題解的法6一樣。https://leetcode-cn.com/problems/majority-element/solution/qiu-zhong-shu-by-leetcode-2/,感覺官方寫的挺清晰的,copy一下。如果我們把衆數記爲 +1+1 ,把其他數記爲 −1−1 ,將它們全部加起來,顯然和大於 0 ,從結果本身我們可以看出衆數比其他數多。本質上,就是找 nums 的一個後綴 suf ,其中 suf[0] 就是後綴中的衆數。我們維護一個計數器,如果遇到一個我們目前的候選衆數,就將計數器加一,否則減一。只要計數器等於 0 ,我們就將 nums 中之前訪問的數字全部 忘記 ,並把下一個數字當做候選的衆數。直觀上這個算法不是特別明顯爲何是對的,我們先看下面這個例子(豎線用來劃分每次計數器歸零的情況)[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7]首先,下標爲 0 的 7 被當做衆數的第一個候選。在下標爲 5 處,計數器會變回0 。所以下標爲 6 的 5 是下一個衆數的候選者。由於這個例子中 7 是真正的衆數,所以通過忽略掉前面的數字,我們忽略掉了同樣多數目的衆數和非衆數。因此, 7 仍然是剩下數字中的衆數。[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 5, 5, 5, 5]現在,衆數是 5 (在計數器歸零的時候我們把候選從 7 變成了 5)。此時,我們的候選者並不是真正的衆數,但是我們在 遺忘 前面的數字的時候,要去掉相同數目的衆數和非衆數(如果遺忘更多的非衆數,會導致計數器變成負數)。因此,上面的過程說明了我們可以放心地遺忘前面的數字,並繼續求解剩下數字中的衆數。最後,總有一個後綴滿足計數器是大於 0 的,此時這個後綴的衆數就是整個數組的衆數。
class Solution(object):
def majorityElement(self, nums):
rec = [nums[0], 1]
for i in range(1,len(nums)):
if rec[1] == 0:
rec = [nums[i], 1]
elif rec[0] == nums[i]:
rec[1] += 1
else:
rec[1] -= 1
return rec[0]
1287. 有序數組中出現次數超過25%的元素
https://leetcode-cn.com/problems/element-appearing-more-than-25-in-sorted-array/
給你一個非遞減的 有序 整數數組,已知這個數組中恰好有一個整數,它的出現次數超過數組元素總數的 25%。請你找到並返回這個整數。
示例:輸入:arr = [1,2,2,6,6,6,6,7,10],輸出:6
提示:1 <= arr.length <= 10^4,0 <= arr[i] <= 10^5
思路
一:原數組有序,則可以對數組進行一次遍歷,並統計每個數出現的次數。只要發現某個數出現的次數超過數組 arr
長度的四分之一,那麼這個數即爲答案。
class Solution:
def findSpecialInteger(self, arr):
n = len(arr)
cur, cnt = arr[0], 0
for i in range(n):
if arr[i] == cur:
cnt += 1
if cnt * 4 > n:
return cur
else:
cur, cnt = arr[i], 1
return -1
二:二分法,copy了leetcode的官方題解,https://leetcode-cn.com/problems/element-appearing-more-than-25-in-sorted-array/solution/you-xu-shu-zu-zhong-chu-xian-ci-shu-chao-guo-25d-3/,
根據題目要求,滿足條件的整數 x 至少在數組 arr 中出現了 span = arr.length / 4 + 1 次,那麼我們可以斷定:數組 arr 中的元素 arr[0], arr[span], arr[span * 2], ... 一定包含 x。
我們可以使用反證法證明上述的結論。假設 arr[0], arr[span], arr[span * 2], ... 均不爲 x,由於數組 arr 已經有序,那麼 x 只會連續地出現在 arr[0], arr[span], arr[span * 2], ... 中某兩個相鄰元素的間隔中,因此其出現的次數最多爲 span - 1 次,這與它至少出現 span 次相矛盾。
有了上述的結論,我們就可以依次枚舉 arr[0], arr[span], arr[span * 2], ... 中的元素,並將每個元素在數組 arr 上進行二分查找,得到其在 arr 中出現的位置區間。如果該區間的長度至少爲 span,那麼我們就得到了答案。
class Solution(object):
def findSpecialInteger(self, arr):
# 因爲range中的步長取得n//4,該值不能爲0。
if len(arr) <= 2:
return arr[0]
if len(arr) == 3:
return arr[1]
n = len(arr)
for i in range(0, n, n // 4):
cnt = self._higher(arr, arr[i]) - self._lower(arr, arr[i]) + 1
if cnt > n // 4:
return arr[i]
def _lower(self, nums, target):
# [l, r], 找到第一個等於該數的數
l, r = 0, len(nums) - 1
loc = -1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] < target:
loc = mid
l = mid + 1
else:
r = mid - 1
return loc + 1
def _higher(self, nums, target):
# [l, r], 找到最後一個等於該數的數
l, r = 0, len(nums) - 1
loc = r + 1
while l <= r:
mid = l + (r - l) // 2
if nums[mid] <= target:
l = mid + 1
else:
loc = mid
r = mid - 1
return loc - 1
# def _lower(self, nums, target):
# l, r = 0, len(nums) - 1
# while l < r:
# mid = l + (r - l) // 2
# if nums[mid] < target:
# l = mid + 1
# else:
# r = mid
# if nums[l] == target:
# return l
# return -1
#
# def _higher(self, nums, target):
# l, r = 0, len(nums) - 1
# while l < r:
# mid = l + (r - l + 1) // 2
# if nums[mid] <= target:
# l = mid
# else:
# r = mid - 1
# if nums[r] != target:
# return -1
# return r
一個有趣的庫bisect,使用時保證原數組有序。
bisect_left:在有序數組中新的元素x應該插入的位置i,並保證a[:i]<x,a[i:]>=x,i是第一個大於等於x的元素的下標,該題中爲等於x的最小的下標(因爲必存在等於)。
bisect_right:在有序數組中新的元素x應該插入的位置i,並保證a[:i]<=x,a[i:]>x,i是第一個大於x元素的下標,在該題中i-1是等於x的最大的下標(因爲必存在等於)。
bisect_left(a, x[, lo[, hi]]) -> index
Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e < x, and all e in a[i:] have e >= x. So if x already appears in the list, i points just before the leftmost x already there.bisect_right(a, x[, lo[, hi]]) -> index
Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e <= x, and all e in a[i:] have e > x. So if x already appears in the list, i points just beyond the rightmost x already there
class Solution(object):
def findSpecialInteger(self, arr):
# 因爲range中的步長取得n//4,該值不能爲0。
if len(arr) <= 2:
return arr[0]
if len(arr) == 3:
return arr[1]
n = len(arr)
for i in range(0, n, n // 4):
# 與之前自定義的元素不同,這邊不用加1,具體見上面文字
cnt = bisect.bisect_right(arr, arr[i]) - bisect.bisect_left(arr, arr[i])
if cnt > n // 4:
return arr[i]
119. 楊輝三角 II
https://leetcode-cn.com/problems/pascals-triangle-ii/
給定一個非負索引 k,其中 k ≤ 33,返回楊輝三角的第 k 行。在楊輝三角中,每個數是它左上方和右上方的數的和。
示例:輸入: 3,輸出: [1,3,3,1]
進階:你可以優化你的算法到 O(k) 空間複雜度嗎?
思路
一:借用118. 楊輝三角的思路,把所有行都求出來,返回最後一行,O(n^2)的時間空間複雜度。
class Solution(object):
def getRow(self, rowIndex):
"""
:type rowIndex: int
:rtype: List[int]
"""
if rowIndex == 0:
return [1]
rec = [[1]]
for i in range(1, rowIndex + 1):
tmp = [1]
for j in range(1, len(rec[-1])):
tmp.append(rec[-1][j-1] + rec[-1][j])
tmp += [1]
rec.append(tmp)
return rec[-1]
二:我們發現,正在考慮的這一行只與上一行有關係,故只需要保留上一行,既可以只用兩行,O(n^2)的時間複雜度,O(n)的空間複雜度。
class Solution(object):
def getRow(self, rowIndex):
if rowIndex == 0:
return [1]
rec = [[0] * (rowIndex+1) for _ in range(2)]
for i in range(0, rowIndex + 1):
rec[i%2][0] = 1
for j in range(1, i):
rec[i%2][j] = rec[(i+1)%2][j - 1] + rec[(i+1) % 2][j]
rec[i%2][i] = 1
return rec[rowIndex % 2]
三:組合數,copy一下leetcode大佬的題解,https://leetcode-cn.com/problems/pascals-triangle-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--28/,
如果熟悉楊輝三角,應該記得楊輝三角其實可以看做由組合數構成。
組合數的相關公式,
class Solution(object):
def getRow(self, rowIndex):
rec = [[1] * (rowIndex + 1) for _ in range(2)]
for i in range(1, rowIndex + 1):
for j in range(1, i):
rec[i % 2][j] = rec[(i + 1) % 2][j - 1] + rec[(i + 1) % 2][j]
return rec[rowIndex % 2]
面試題 16.17. 連續數列
https://leetcode-cn.com/problems/contiguous-sequence-lcci/
給定一個整數數組(有正數有負數),找出總和最大的連續數列,並返回總和。
示例:輸入: [-2,1,-3,4,-1,2,1,-5,4],輸出: 6,解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
進階:如果你已經實現複雜度爲 O(n) 的解法,嘗試使用更爲精妙的分治法求解。
思路
一:用res記錄截止目前的最大值,local_sum表示前面的累加和,若其小於0,則表示在其基礎累加會起副作用,故丟棄所有值,重新累加。
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
res = nums[0]
local_sum = nums[0]
for i in range(1, len(nums)):
if local_sum < 0:
local_sum = nums[i]
else:
local_sum += nums[i]
res = max(res, local_sum)
return res
1170. 比較字符串最小字母出現頻次
https://leetcode-cn.com/problems/compare-strings-by-frequency-of-the-smallest-character/
我們來定義一個函數 f(s),其中傳入參數 s 是一個非空字符串;該函數的功能是統計 s 中(按字典序比較)最小字母的出現頻次。例如,若 s = "dcce",那麼 f(s) = 2,因爲最小的字母是 "c",它出現了 2 次。現在,給你兩個字符串數組待查表 queries 和詞彙表 words,請你返回一個整數數組 answer 作爲答案,其中每個 answer[i] 是滿足 f(queries[i]) < f(W) 的詞的數目,W 是詞彙表 words 中的詞。
示例 1:輸入:queries = ["cbd"], words = ["zaaaz"],輸出:[1],解釋:查詢 f("cbd") = 1,而 f("zaaaz") = 3 所以 f("cbd") < f("zaaaz")。
示例 2:輸入:queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"],輸出:[1,2],解釋:第一個查詢 f("bbb") < f("aaaa"),第二個查詢 f("aaa") 和 f("aaaa") 都 > f("cc")。
提示:1 <= queries.length <= 2000,1 <= words.length <= 2000,1 <= queries[i].length, words[i].length <= 10,queries[i][j], words[i][j] 都是小寫英文字母
思路
一:利用好條件很重要,1 <= queries[i].length, words[i].length <= 10,即所謂的f函數即helper的返回值一定是小於等於10的,故而我們可用計數排序,用rec[i]記錄返回值是i的有幾個元素,由於 f("aaa") 和 f("aaaa") 都 > f("cc"),在奇數排序的基礎上,我們重新定義rec,rec[i]記錄返回值大於等於i的有幾個元素,即是原先的rec的rec[i:]的累加和。且要是嚴格的小於不能等於,故res[i] = rec[v + 1]。
class Solution(object):
def numSmallerByFrequency(self, queries, words):
"""
:type queries: List[str]
:type words: List[str]
:rtype: List[int]
"""
rec = [0] * 12
for i in range(len(words)):
v = self._helper(words[i])
rec[v] += 1
for i in range(9, -1, -1):
rec[i] += rec[i+1]
res = [0] * len(queries)
for i in range(len(queries)):
v = self._helper(queries[i])
res[i] = rec[v + 1]
return res
def _helper(self, s):
rec = [0] * 26
for c in s:
rec[ord(c) - ord("a")] += 1
for i in range(26):
if rec[i] != 0:
return rec[i]
return -1
1089. 複寫零
https://leetcode-cn.com/problems/duplicate-zeros/
給你一個長度固定的整數數組 arr,請你將該數組中出現的每個零都複寫一遍,並將其餘的元素向右平移。注意:請不要在超過該數組長度的位置寫入元素。要求:請對輸入的數組 就地 進行上述修改,不要從函數返回任何東西。
示例 1:輸入:[1,0,2,3,0,4,5,0],輸出:null,解釋:調用函數後,輸入的數組將被修改爲:[1,0,0,2,3,0,0,4]
示例 2:輸入:[1,2,3],輸出:null,解釋:調用函數後,輸入的數組將被修改爲:[1,2,3]
提示:1 <= arr.length <= 10000,0 <= arr[i] <= 9
思路
一:藉助額外空間
class Solution(object):
def duplicateZeros(self, arr):
"""
:type arr: List[int]
:rtype: None Do not return anything, modify arr in-place instead.
"""
rec, j, cnt = arr[:], 0, 0
for i in range(len(arr)):
if cnt > 0:
arr[i] = 0
cnt -= 1
elif rec[j] == 0:
arr[i] = 0
j += 1
cnt += 1
else:
arr[i] = rec[j]
j += 1
return
二:不借助額外的空間,原址修改,做兩次遍歷,第一次遍歷確定新的列表中的最後一個元素是原列表哪個下標(last_i)對應的元素(若最後一個元素爲0,還要標記這個0是否要複寫)。第二次從右往左遍歷填入數據。
class Solution(object):
def duplicateZeros(self, arr):
last_i, i, two_zero = 0, 0, True
# 第一次遍歷確定新的列表中的最後一個元素是原列表哪個下標(last_i)對應的元素
while i < len(arr):
if arr[last_i] != 0:
i += 1
else:
i += 2
# 若最後一個元素爲0,還要標記這個0是否要複寫
if i > len(arr):
two_zero = False
last_i += 1
last_i, i = last_i - 1, len(arr) - 1
while last_i >= 0:
if arr[last_i] != 0:
arr[i] = arr[last_i]
else:
if not two_zero and i == len(arr) -1:
arr[i] = 0
else:
arr[i] = 0
i -= 1
arr[i] = 0
i -= 1
last_i -= 1
448. 找到所有數組中消失的數字
https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/
給定一個範圍在 1 ≤ a[i] ≤ n ( n = 數組大小 ) 的 整型數組,數組中的元素一些出現了兩次,另一些只出現一次。
找到所有在 [1, n] 範圍之間沒有出現在數組中的數字。您能在不使用額外空間且時間複雜度爲O(n)的情況下完成這個任務嗎? 你可以假定返回的數組不算在額外空間內。
示例:輸入:[4,3,2,7,8,2,3,1],輸出:[5,6]
思路
一:藉助額外空間,由於值域是有範圍的,可開闢一個固定空間rec,遍歷數組將nums中值作爲下標置1,遍歷rec,哪個下標爲0,就是缺失值。
class Solution(object):
def findDisappearedNumbers(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
rec = [0] * (len(nums) + 1)
for num in nums:
rec[num] += 1
res = []
for i in range(1, len(nums) + 1):
if rec[i] == 0:
res.append(i)
return res
二:同樣因爲題目值域的特殊性,且找的是缺失的數字。把 abs(nums[i])-1 索引位置的元素標記爲負數。即若該位置元素大於零,則將該位置乘上-1,nums[abs(nums[i])−1]×−1 。最後遍歷列表,若i下標的元素大於0(沒修改過),則i+1是缺失的。其實出現1次也好做,只需要將nums[abs(nums[i])−1]=nums[abs(nums[i])−1]×−1,改成nums[abs(nums[i])−1]=abs(nums[abs(nums[i])−1])×−1,找小於0的即可
class Solution(object):
def findDisappearedNumbers(self, nums):
for i in range(len(nums)):
new_idx = abs(nums[i]) - 1
if nums[new_idx] > 0:
nums[new_idx] = -1 * nums[new_idx]
res = []
for i in range(len(nums)):
if nums[i] > 0:
res.append(i + 1)
return res
442. 數組中重複的數據
https://leetcode-cn.com/problems/find-all-duplicates-in-an-array/
給定一個整數數組 a,其中1 ≤ a[i] ≤ n (n爲數組長度), 其中有些元素出現兩次而其他元素出現一次。找到所有出現兩次的元素。你可以不用到任何額外空間並在O(n)時間複雜度內解決這個問題嗎?
示例:輸入:[4,3,2,7,8,2,3,1],輸出:[2,3]
思路
一:仿照上一題的解法。
class Solution(object):
def findDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
res = []
for i in range(len(nums)):
new_idx = abs(nums[i]) - 1
if nums[new_idx] < 0:
# 小於0,表示之前遍歷過,現在又遍歷到,是解
res.append(new_idx + 1)
else:
# 大於0,表示之前沒有遍歷到,此時遍歷到了標負
nums[new_idx] = -1 * abs(nums[new_idx])
return res