一、概念:
數組是一種線性表數據結構,用一組連續的內存空間,來存儲一組具有相同類型的數據。
1、了解線性表(每個數據最多只有一個前驅和后繼節點,eg 數組、鏈表、隊列、棧等)和非線性表(eg 樹、堆、圖)
2、連續的內存空間和相同數據類型的數據,這兩點使得數組可以實現隨機訪問。
二、操作:
隨機訪問:
根據下標隨機訪問的時間復雜度為O(1)。
根據數組的示意圖,可知根據下標即可知道該元素的地址,具體計算公式為 num[index]_addr = base_addr + data_type_size * index 。
插入:
在確保數組有空間的情況下進行插入:
1、如果確保數組數據的順序,在某個位置K插入某個數,需要把插入位置K以及以后的數據移動,平均復雜度為O(n)。
2、如果對原數組數據順序沒有要求,直接在最后位置插入一個數,然后和第K個數進行調換即可,時間復雜度為O(1)。
刪除:
刪除數據,數據需要向前運動,平均時間復雜度為O(n)
1、刪除最后一個元素,時間復雜度為O(1)
2、刪除第一個元素,時間復雜度為O(n)
3、如果有多次刪除操作,可以考慮合并提高效率
三、注意點:
1、數據的越界問題,數組下標從0開始,下標取值范圍[0,1,2,...len-1]
2、多維數組
3、學習高級語言的容器,對數組的封裝以及動態的擴容,java的arraylist、C++的vector
四、常見面試題:
例題1: 1207. 獨一無二的出現次數https://leetcode-cn.com/problems/unique-number-of-occurrences/
給你一個整數數組 arr,請你幫忙統計數組中每個數的出現次數。
如果每個數的出現次數都是獨一無二的,就返回 true;否則返回 false。
示例 1:
輸入:arr = [1,2,2,1,1,3]
輸出:true
解釋:在該數組中,1 出現了 3 次,2 出現了 2 次,3 只出現了 1 次。沒有兩個數的出現次數相同。
示例 2:
輸入:arr = [1,2]
輸出:false
示例 3:
輸入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
輸出:true
思路1:判斷哈希的長度和set的長度
思路2:哈希計數 + 數組排序
思路3:哈希計數 + set 重復判斷
時間復雜度:O(n)
空間復雜度:O(n)
代碼實現:
# 思路1:判斷哈希的長度和set中的長度
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
dic = Counter(arr)
return len(dic) == len(set(dic.values()))
# 思路2:哈希計數+數組排序
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
dic = Counter(arr)
nums = list(dic.values())
nums.sort()
for i in range(1, len(nums)):
if nums[i] == nums[i-1]:
return False
return True
# 思路3:哈希計數+ set重復判斷
class Solution:
def uniqueOccurrences(self, arr: List[int]) -> bool:
dic = Counter(arr)
arr = list(dic.values())
visited = set()
for i in range(len(arr)):
if arr[i] not in visited:
visited.add(arr[i])
else:
return False
return True
例題2: 349. 兩個數組的交集 https://leetcode-cn.com/problems/intersection-of-two-arrays/
給定兩個數組,編寫一個函數來計算它們的交集。
示例 1:
輸入:nums1 = [1,2,2,1], nums2 = [2,2]
輸出:[2]
示例 2:
輸入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出:[9,4]
思路一: 哈希計數+set去重
思路二: 兩個set去重
思路三: 邏輯判斷
思路四: 哈希計數 用map[j] = 0 保證去重
代碼實現:
方法一:哈希計數+set去重
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
dic = Counter(nums1)
nums2 = list(set(nums2))
res = []
for i in range(len(nums2)):
if nums2[i] in dic.keys():
res.append(nums2[i])
return res
方法二:兩個set去重
寫法1:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return list(set(nums1) & set(nums2))
寫法2:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1 = list(set(nums1))
nums2 = list(set(nums2))
return [i for i in nums1 if i in nums2]
方法三:邏輯判斷
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
res = []
for i in nums1:
if i not in res and i in nums2:
res.append(i)
return res
方法四:哈希計數 用map[j] = 0 保證去重
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
res = []
map = {}
for i in nums1:
map[i] = map[i]+1 if i in map else 1
for j in nums2:
if j in map and map[j] > 0:
res.append(j)
map[j] = 0
return res
例題3:1122. 數組的相對排序https://leetcode-cn.com/problems/relative-sort-array/
給你兩個數組,arr1 和 arr2,
arr2 中的元素各不相同
arr2 中的每個元素都出現在 arr1 中
對 arr1 中的元素進行排序,使 arr1 中項的相對順序和 arr2 中的相對順序相同。未在 arr2 中出現過的元素需要按照升序放在 arr1 的末尾。
示例:
輸入:arr1 = [2,3,1,3,2,4,6,7,9,2,19], arr2 = [2,1,4,3,9,6]
輸出:[2,2,2,1,4,3,3,9,6,7,19]
思路1:新建數組計數排序, T:O(m+n), S:O(1)
思路2:用map計數排序, T:O(logm + n), S:O(max(m,n))
代碼實現:
# 思路一
class Solution:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
arr = [0] * 1001
res = []
for i in range(len(arr1)):
arr[arr1[i]] += 1
for i in range(len(arr2)):
while arr[arr2[i]] > 0:
arr[arr2[i]] -= 1
res.append(arr2[i])
for i in range(len(arr)):
while arr[i] > 0:
res.append(i)
arr[i] -= 1
return res
# 思路二
class Solution:
def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]:
dif= [i for i in arr1 if i not in arr2]
dif.sort()
res = []
arr1_cnt = collections.Counter(arr1)
for i in arr2:
res += [i] * arr1_cnt[i]
res += dif
return res
例題4:1. 兩數之和:https://leetcode-cn.com/problems/two-sum/
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那 兩個 整數,并返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路1:暴力求解法,兩層遍歷,循環,如果nums[i] + nums[j] == target, return [i, j], T:O(n^2), S:O(1)
思路2:哈希表查詢,T:O(n), S:O(n)
代碼實現:
# 思路一:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
# 思路二:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i in range(len(nums)):
if target - nums[i] in dic.keys():
return [dic[target - nums[i]], i]
else:
dic[nums[i]] = i
例題5:922. 按奇偶排序數組 II https://leetcode-cn.com/problems/sort-array-by-parity-ii/
給定一個非負整數數組 A, A 中一半整數是奇數,一半整數是偶數。
對數組進行排序,以便當 A[i] 為奇數時,i 也是奇數;當 A[i] 為偶數時, i 也是偶數。
你可以返回任何滿足上述條件的數組作為答案。
示例:
輸入:[4,2,5,7]
輸出:[4,5,2,7]
解釋:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也會被接受。
思路:
雙指針法,even記錄偶數的下標, odd記錄奇數的下標;遍歷數組,位運算 & 1 == 1為奇數, & 1 == 0 為偶數;放入到結果數據集中。
T:O(n), S:O(n)額外開辟一個長度為n的數組記錄返回集。
代碼實現:
class Solution:
def sortArrayByParityII(self, A: List[int]) -> List[int]:
res = [0] * len(A)
even, odd= 0, 1
for i in range(len(A)):
if A[i] & 1 == 1:
res[odd] = A[i]
odd += 2
else:
res[even] = A[i]
even += 2
return res
例題6:15. 三數之和 https://leetcode-cn.com/problems/3sum/
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重復的三元組。
注意:答案中不可以包含重復的三元組。
示例:
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:
思路:排序 + 雙指針,+ 去重; 先對數組進行排序,排序后用雙指針左右夾逼的方法進行遍歷。遍歷時注意兩次判重,第一次判重是判斷第一個元素,重復時continue跳過。第二次判重是在左指針和右指針,重復時左右指針分布+1,和-1跳過。
T:O(nlogn), S:O(1)
代碼實現:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if len(nums) < 3:
return []
nums.sort()
if nums[0] > 0:
return []
res = []
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
left, right = i+1, len(nums) - 1
while left < right:
count = nums[i] + nums[left] + nums[right]
if count < 0:
left += 1
elif count > 0:
right -= 1
else:
res.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]:
left += 1
while left < right and nums[right] == nums[right-1]:
right -= 1
left += 1
right -= 1
return res
例題7:66. 加一 https://leetcode-cn.com/problems/plus-one/
思路:
判斷各位是否為9,如果不為9的話,+1 返回結果;如果不為9的話,變成0. 如果循環結束仍未返回的話,則新建一個數組,長度+1, 首位為1,其余為0,返回。
T:O(n), S:O(n)
代碼實現:
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
for i in range(len(digits) - 1, -1, -1):
if digits[i] == 9:
digits[i] = 0
else:
digits[i] += 1
return digits
digits = [0] * (len(digits) + 1)
digits[0] = 1
return digits
例題8: 283. 移動零 https://leetcode-cn.com/problems/move-zeroes/
給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。
示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]
思路:
單指針,標記非零元素的下標, [0,1,0,3,12] 非零元素1, 3, 12的下標依次就是 0, 1, 2。
T:O(n)
S:O(1)
代碼實現:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
j = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[i], nums[j] = nums[j], nums[i]
j += 1
例題9: 349. 兩個數組的交集 https://leetcode-cn.com/problems/intersection-of-two-arrays/
給定兩個數組,編寫一個函數來計算它們的交集。
示例 1:
輸入:nums1 = [1,2,2,1], nums2 = [2,2]
輸出:[2]
示例 2:
輸入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出:[9,4]
思路:
思路:用set分別對nums1和nums2去重,然后nums1轉成list,并遍歷,如果遍歷的元素在nums2的set中,則加入返回的結果集中。
T:O(n) , S:O(n)
代碼實現:
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
res = []
nums1 = list(set(nums1))
nums2 = set(nums2)
for num in nums1:
if num in nums2:
res.append(num)
return res
例題10: 127. 單詞接龍 https://leetcode-cn.com/problems/word-ladder/
給定兩個單詞(beginWord 和 endWord)和一個字典,找到從 beginWord 到 endWord 的最短轉換序列的長度。轉換需遵循如下規則:
每次轉換只能改變一個字母。
轉換過程中的中間單詞必須是字典中的單詞。
說明:
如果不存在這樣的轉換序列,返回 0。
所有單詞具有相同的長度。
所有單詞只由小寫字母組成。
字典中不存在重復的單詞。
你可以假設 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:
輸入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
輸出: 5
解釋: 一個最短轉換序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
返回它的長度 5。
示例 2:
輸入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
輸出: 0
解釋: endWord "cog" 不在字典中,所以無法進行轉換。
思路:
思路:雙端隊列,初始裝入開始遍歷的單詞,和長度1。 循環時,從雙端隊列的左端彈出元素,如果彈出的單詞和目標單詞相同則,返回當前的長度。如果不同,則遍歷當前單詞,對每個字符一次進行變換,每變換一次驗證一下是否在字典集中,是否訪問過。如果之前未訪問過,又在字典集中,則裝入雙端隊列中,長度+1.
如果為未找到,則返回0.
T:O(n*c^2),n為單詞的個數,c為單詞的平均長度
S:O(n),n為單詞的個數
代碼實現:
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
words = set(wordList)
visited = set()
deque = collections.deque([(beginWord, 1)])
alpha = string.ascii_lowercase
while deque:
word, length = deque.popleft()
if word == endWord:
return length
for i in range(len(word)):
for ch in alpha:
new_word = word[:i] + ch + word[i+1:]
if new_word not in visited and new_word in words:
visited.add(new_word)
deque.append((new_word, length + 1))
return 0
例題11: 347. 前 K 個高頻元素 https://leetcode-cn.com/problems/top-k-frequent-elements/
給定一個非空的整數數組,返回其中出現頻率前 k 高的元素。
示例 1:
輸入: nums = [1,1,1,2,2,3], k = 2
輸出: [1,2]
示例 2:
輸入: nums = [1], k = 1
輸出: [1]
思路:
思路:哈希計數, 計數結果按values的大小進行反向排序,取出前k個高頻的keys值
T:O(k), S:O(n),n為不同元素的個數
代碼實現:
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
dic = Counter(nums)
li = sorted(dic.items(), key = lambda kv : kv[1], reverse = True)
re = []
for i in range(k):
re.append(li.pop(0)[0])
return re
例題12: 劍指 Offer 59 - I. 滑動窗口的最大值 https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/
給定一個數組 nums 和滑動窗口的大小 k,請找出所有滑動窗口里的最大值。
示例:
輸入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
輸出: [3,3,5,5,6,7]
解釋:
滑動窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
思路:
思路:用雙端隊列,隊列里存的是數組元素的下標,永遠保持隊列里的最左端是最大的元素
T:O(n) , S:O(k)
代碼實現:
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
res, deque = [], []
for i in range(len(nums)):
while deque and nums[i] > nums[deque[-1]]:
deque.pop()
deque.append(i)
while i - deque[0] > k - 1:
deque.pop(0)
if i >= k - 1:
res.append(nums[deque[0]])
return res
例題13: 455. 分發餅干 https://leetcode-cn.com/problems/assign-cookies/
假設你是一位很棒的家長,想要給你的孩子們一些小餅干。但是,每個孩子最多只能給一塊餅干。
對每個孩子 i,都有一個胃口值 g[i],這是能讓孩子們滿足胃口的餅干的最小尺寸;并且每塊餅干 j,都有一個尺寸 s[j] 。如果 s[j] >= g[i],我們可以將這個餅干 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是盡可能滿足越多數量的孩子,并輸出這個最大數值。
示例 1:
輸入: g = [1,2,3], s = [1,1]
輸出: 1
解釋:
你有三個孩子和兩塊小餅干,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅干,由于他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。
示例 2:
輸入: g = [1,2], s = [1,2,3]
輸出: 2
解釋:
你有兩個孩子和三塊小餅干,2個孩子的胃口值分別是1,2。
你擁有的餅干數量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2.
思路:
思路:貪心算法,最大的餅干,優先滿足胃口最大的小孩子,對兩個數組同時進行倒序遍歷
T:O(n), S:O(1)
代碼實現:
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
j = len(s) - 1
count = 0
for i in range(len(g) -1, -1, -1):
if j >=0 and s[j] >= g[i]:
j -= 1
count += 1
return count
撰寫記錄
2020.12.05-08:00:01-第一次撰寫
2020.12.14-07:28:10-第二次撰寫
2020.12.15-07:27:15-第三次撰寫
2020.12.23-06:02:08-第四次撰寫
2020.12.24-07:28:09-第五次撰寫
2020.12.26-10:35:10-第六次撰寫
2021.01.03-08:03:00-第七次撰寫
2021.01.06-07:05:00-第八次撰寫
2021.02.01-19:40:00-第九次撰寫