1. 程式人生 > >leetcode | 初級演算法-陣列

leetcode | 初級演算法-陣列

01 起

最近“不務正業地”刷了一波leetcode上的演算法題,初級演算法已經刷完50%,戰況如下,

刷題固然爽快,但及時總結才是進步之道,下面就陣列部分的題目進行回顧和總結。

注意,刷題使用的語言是Python3,"陣列"這個資料結構在Python中對應的就是"列表"list。

初級演算法中陣列類題目共11道,如下,我們一道一道來總結

02 解題

下面我們逐個擊破陣列部分的11道題。

2.1 從排序陣列中刪除重複項

題目:給定一個排序陣列,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後陣列的新長度。

思路:審題在於”排序陣列“和”原地演算法“,對於排序陣列,可以用set()實現去重並返回原順序,另外,可以直接遍歷對比num[i],num[i+1],若相同則去掉nums[i+1]

class Solution:
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums)<2:
            return len(nums)
        i=0
        while i<len(nums)-1:
            if nums[i]==nums[i+1]:
                nums.remove(nums[i+1])
            else:
                i+=1
        return len(nums)
2.2 買賣股票的最佳時機 II

題目:給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)

思路:因為可以儘可能多地交易(買賣),又不能同時參與多筆交易,因此為了獲得最大利潤,可以採取如下措施:只要前一天價格低於當天,就在前一天買入股票並在當天賣出,即,遍歷每天的股價,若第i天股價低於第i+1天,則在第i天買入,第i+1天賣出

class Solution:
    def maxProfit(self, prices):
        """
        :type prices: List[int]
        :rtype: int
        """
        profit=0
        for i in range(len(prices)-1):
            if prices[i]<prices[i+1]:
                profit+=prices[i+1]-prices[i]
        return profit
2.3 旋轉陣列

題目:給定一個數組,將陣列中的元素向右移動 k 個位置,其中 k 是非負數

思路:k可能大於陣列長度,於是先i=k%l,再將陣列後i個元素平移到陣列左邊

class Solution:
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: void Do not return anything, modify nums in-place instead.

        """
        # 演算法一:未考慮k>len(nums)的情況,執行時長64ms
        """
        l=len(nums)
        nums[:]=nums[l-k:l]+nums[:l-k]
        
        """
        演算法三:此演算法最高效,考慮k>len(nums)的情況,通過取餘數來化解,執行時長55ms
        """
        l=len(nums)
        i=k%l
        nums[:]=nums[-i:]+nums[:-i]
2.4 存在重複

題目:給定一個整數陣列,判斷是否存在重複元素。如果任何值在陣列中出現至少兩次,函式返回 true。如果陣列中每個元素都不相同,則返回 false

思路:非常簡單,python的set()函式可以對列表去重,我們只需要對比set前後列表的長度是否相同即可

class Solution:
    def containsDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        return len(nums)!=len(set(nums))
2.5 只出現一次的數字

題目:給定一個非空整數陣列,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。

思路:三種思路,一是笨辦法,一一對比;二是異或操作,相同兩數異或為0,某數異或0為其本身;三是靈活運用set()

class Solution:
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        
        """
        演算法一:如果nums過長,時間複雜度較高,不過此演算法可以找出多個“只出現一次的元素”
        """
        for i in range(len(nums)):
            if nums[i] not in nums[:i]+nums[i+1:]:
                result=nums[i]
        return result
        
        """
        演算法二核心思想:異或,異或操作是在二進位制上的異或操作,相同兩數異或=0,數異或0=數本身。
        於是定義一個整數,初始化為0,並用該數依次與列表中元素進行異或操作,最後的結果肯定就是隻出現過一次的那個數(異或次序並不影響最終結果,因此不論列表元素如何排列,相同兩數異或總是為0)
        此演算法只對 只有一個“只出現一次的元素” 的數列適用
        """
        num=0
        for i in nums:
            num ^= i
        return num

        """
        演算法三核心思想:set(list)將篩除重複元素
        此演算法只對 只有一個“只出現一次的元素” 的數列適用
        """
        num=sum(list(set(nums)))*2-sum(nums)
        return num
2.6 兩個陣列的交集 II

題目:給定兩個陣列,編寫一個函式來計算它們的交集

思路:兩種方法,一是每比較一次,就刪除陣列中被比較的元素;二是直接利用Counter()計數,然後&運算

class Solution:
    def intersect(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        
        """
        思路一:每比較一次,就刪除陣列中被比較的元素
        實現方法:字典計數,比較後字典對應key的計數-1
        """
        result=[]
        count={}
        for num in nums1:
            count[num]=count.get(num,0)+1
        
        for num in nums2:
            if num in count.keys() and count[num]: #if 0等價於false
                result.append(num)
                count[num]-=1
        return result
        
        """
        思路二:直接利用Counter()計數,然後&運算(&運算返回a、b的counter結果中(key,value)對的交集)
        """
        from collections import Counter
        return (list((Counter(nums1)&Counter(nums2)).elements()))
2.7 加一

題目:給定一個由整陣列成的非空陣列所表示的非負整數,在該數的基礎上加一。最高位數字存放在陣列的首位, 陣列中每個元素只儲存一個數字。你可以假設除了整數 0 之外,這個整數不會以零開頭。

思路:將列表轉為數字,再加一。列表轉數字,兩個方法, 方法一:string和int之間的轉換,將列表中個數字加到空字串,然後將最終的字串轉為數字再+1,再將此結果轉為字串,最後將字串各元素append到列表中輸出 方法二:遍歷list各元素,按nums[i]10*(len(nums)-i+1)求和得到列表表示的數字 這裡採用方法一

class Solution:
    def plusOne(self, digits):
        """
        :type digits: List[int]
        :rtype: List[int]
        """
        resStr="";res=0;resList=[]
        for i in digits:
            resStr+=str(i)
        res=str(int(resStr)+1)
        for i in res:
            resList.append(int(i))
        return resList
2.8 移動零

題目:給定一個數組 nums,編寫一個函式將所有 0 移動到陣列的末尾,同時保持非零元素的相對順序。

思路:兩種方法。思路一:遍歷nums各元素i,若i為0,將i放在最後一位,i之後的元素前移一位,此方法理解上比較直觀,但時間複雜度較高;思路二:遍歷nums各元素i,若i為0,直接刪除該元素,並在末尾補0,這種方法簡單直接高效。

class Solution:
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        
        """
        思路一:遍歷nums各元素i,若i為0,將i放在最後一位,i之後的元素前移一位
        560ms
        """
        n=len(nums)
        i=0;z=0
        while i<n-z:
            if nums[i]==0:
                z+=1
                for j in range(i,n-1):
                    nums[j]=nums[j+1]
                nums[n-1]=0
                if nums[i]!=0:
                    i+=1
            else:
                i+=1
        
        """
        思路二:遍歷nums各元素i,若i為0,直接刪除該元素,並在末尾補0
        76ms
        """
        z=0  #計數0的個數,防止連續0中後面的0被跳過
        for i in range(len(nums)):
            if nums[i-z]==0:
                del nums[i-z]
                nums.append(0)
                z+=1
2.9 兩數之和

題目:給定一個整數陣列和一個目標值,找出陣列中和為目標值的兩個數。你可以假設每個輸入只對應一種答案,且同樣的元素不能被重複利用

思路:兩種方法。思路一:subNums儲存i索引之後的list,校驗target-nums[i]是否在subNums中,若存在則返回i和subNums中的索引值,此方法當 陣列很長時,耗時較長;思路二:是思路一的改進版,呼叫迭代器enumerate()進行迭代,加快運算速度;dict儲存nums各元素和對應的索引值

class Solution:
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        
        """
        思路一:subNums儲存i索引之後的list,對比
        缺點:當nums較長時,運算時間較長
        """
        subNums=[]
        result=[]
        for i in range(len(nums)):
            subNums=nums[i+1:] #計算量減半,每次只取nums[i]之後的元素,因為之前的元素已經計算過了
            if target-nums[i] in subNums:
                result.append(i)
                result.append(i+1+subNums.index(target-nums[i]))
        return result
        
        """
        思路二:enumerate(),呼叫迭代器進行迭代,加快運算速度;dict儲存nums各元素和對應的索引值
        """
        dictNum={}
        for i, num in enumerate(nums):
            if target-num in dictNum.keys():
                return i,dictNum[target-num]
            dictNum[num]=i #dict儲存nums各元素和對應的索引值
2.10 有效的數獨

題目:判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。數獨空白格用 ‘.’ 表示。

  • 數字 1-9 在每一行只能出現一次。
  • 數字 1-9 在每一列只能出現一次。
  • 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。

思路:按題意,數獨最大是9X9的,用三個列表儲存各行、各列、各方塊的元素情況,初始化為空,判斷某數字是否存在於某行+某列+某方塊中,若存在,返回False,否則將此元素新增到行列方塊中 難點:第i行、第j列的元素在第幾個方塊中?>>3*(i//3)+j//3

class Solution:
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        """
        rows=[[] for i in range(9)]
        cols=[[] for i in range(9)]
        boxs=[[] for i in range(9)] #用三個列表儲存各行、各列、各方塊的元素情況,初始化為空
        
        for i,row in enumerate(board): #enumerate迭代器,演算法效率更高
            for j,num in enumerate(row): #注意區分row rows
                if num!="." and num in rows[i]+cols[j]+boxs[3*(i//3)+j//3]:
                    return False
                rows[i].append(num)
                cols[j].append(num)
                boxs[3*(i//3)+j//3].append(num)
        return True
2.11 旋轉影象

題目:給定一個 n × n 的二維矩陣表示一個影象,將影象順時針旋轉 90 度。必須在原地旋轉影象,這意味著你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉影象。

思路:根據規律,將矩陣轉置,然後反轉每行實現

class Solution:
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: void Do not return anything, modify matrix in-place instead.
        """
        n=len(matrix)
        for i in range(n): #手動轉置矩陣
            for j in range(i+1,n): #注意j的遍歷範圍
                tmp=matrix[i][j] #用一個變數來儲存中間態,這個idea很普遍!
                matrix[i][j]=matrix[j][i]
                matrix[j][i]=tmp
        for i in range(n):
            matrix[i]=matrix[i][::-1]

03 總結

本文解鎖了leetcode初級演算法題庫中陣列板塊的全部題目,並對每道題的解題思路和程式碼進行說明,供你參考,也便於我日後複習總結。

下期我們將繼續解鎖其他板塊的題目,一起進步吧!