算法與數據結構 之 數組專題

數組

一、概念:

數組是一種線性表數據結構,用一組連續的內存空間,來存儲一組具有相同類型的數據。
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-第九次撰寫

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容