算法(3):數組

??一鼓作氣,再而衰,三而竭,第三篇走起!


數組(Array)

??數組是大家再熟悉不過的東西了,但是這里還是簡單說一下,數組是連續存放一系列元素的數據結構,我們可以方便的通過索引訪問數組當中的任何一個元素。通常,數組在定義時是固定大小的,但是也有一種動態數組,它在初始化時不需要定義數組大小,也就意味著可以無限裝數據,我們稱之為動態數組,如C++當中的Vector(曾經是我最喜歡的一個數據結構,當然,在我遇到python當中的List之前)和Java當中的ArrayList 。

安利一波問題4,大家一定要看一看。


問題1:找到一個一維數組的“中樞”元素的索引。如果有很多中樞元素,則只需返回最左側的即可。中樞在此處意味著該索引左側元素和等于該索引右側元素和。
輸入: [1, 2, 8, 4, 5, 6]
輸出: 3(1+2+8 = 5+6)

代碼,那是異常的簡單啊。小編不禁想起了有一次面試,面試官讓我寫一個找數組中最大最小值的算法,啊,那時的時光是多么美好!如下代碼的時間復雜度O(n),空間復雜度O(1)

def pivotIndex(nums) -> int:
    ts, cs = sum(nums), 0
    for idx, num in enumerate(nums):
        ls, rs = cs, ts - cs - num
        if ls == rs:
            return idx
        cs += num
    return -1

if __name__ == '__main__':
    ans = pivotIndex([1, 2, 8, 4, 5, 6])
    print(ans)


問題2:加1操作。將輸入數組當成一個整數,然后執行加1操作
例子:
輸入: [1,2,3] 輸出: [1,2,4]
輸入: [9,9,9] 輸出: [1,0,0,0]

我的方法是直接在數組里面執行操作,當然你也可以換種方法,如把數組里元素按位讀出來變成int,然后加一,然后在按位存放到數組中。

def plusOne(digits: list) -> list:
    digits = [0] + digits
    for i in range(len(digits) - 1, -1, -1):
        if digits[i] == 9:
            digits[i] = 0
        else:
            digits[i] += 1
            break

    return (digits, digits[1:])[0 if digits[0] != 0 else 1]

if __name__ == '__main__':
    ans = plusOne([1, 2, 8, 4, 5, 6])
    print(ans)

問題3:對角線遍歷二維數組(Diagonal Traverse)。
一維數組一般都很簡單,但是到了二維數組,難度就會上升一個臺階,畢竟,問題的花樣變多了嘛~
輸入:[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]
輸出: [1,2,4,7,5,3,6,8,9]
如果看不懂的話,可以配上下面這張圖看,保證一眼就明白~

對角線遍歷矩陣圖

代碼具體思路分為兩步:
step1:根據對角線將矩陣分組(可以發現,同一對角線的矩陣row和col坐標相加為定值),如上圖,可以分為5組,得到[1] , [2,4],[3,5,7],[6,8],[9]。
step2:按照(row + col)的大小順序將數據依次添加到結果列表當中。根據代碼定義,在(row + col )為偶數時,需要翻轉一下該組元素。即[3,5,7](此時row + col = 2)需要變為[7,5,3]再添加到結果中。

import  collections
def findDiagonalOrder(matrix):
    """
    :type matrix: List[List[int]]
    :rtype: List[int]
    """
    result = []
    dd = collections.defaultdict(list)
    if not matrix: return result
    # Step 1: Numbers are grouped by the diagonals.
    # Numbers in same diagonal have same value of row+col
    for i in range(0, len(matrix)):
        for j in range(0, len(matrix[0])):
            dd[i + j].append(matrix[i][j])  # starting indices from 1, hence i+j+1.
    # Step 2: Place diagonals in the result list.
    # But remember to reverse numbers in odd diagonals.
    for k in sorted(dd.keys()):
        if k % 2 == 0: dd[k].reverse()
        result += dd[k]
    return result

if __name__ == '__main__':
    arr = [[ 1, 2, 3 ],[ 4, 5, 6 ],[ 7, 8, 9 ]]
    ans = findDiagonalOrder(arr)
    print(ans)

問題4:旋轉矩陣(Spiral Matrix),即返回一個矩陣順時針旋轉的結果。
輸入:
[ [ 1, 2, 3 ],
?[ 4, 5, 6 ],
?[ 7, 8, 9 ] ]
輸出:[1,2,3,6,9,8,7,4,5]

下面這個答案是我在網上找的,看到這個代碼之后的好久時間里,我的嘴巴只能發出“wo cao”這兩個音。。。看到這段代碼,我仿佛戀愛了。

def spiralOrder(matrix: list) -> list:
    return matrix and [*matrix.pop(0)] + spiralOrder([*zip(*matrix)][::-1])

if __name__ == '__main__':
    arr = [[ 1, 2, 3 ],[ 4, 5, 6 ],[ 7, 8, 9 ]]
    ans = spiralOrder(arr)
    print(ans)

問題5:數組求和。給定一個升序排列的數組,找出兩個數,其和等于輸入‘target’,結果返回這兩個數的索引。
輸入: numbers = [2,7,11,15], target = 9
輸出: [1,2]
我們如果使用暴力法的話,結果有C^2_n種,時間復雜度為O(n^2)。那么大家還記不記得 有一招從天而降的掌法... 上一章講解的雙指針技術呢?這里便用到了!兩個指針分別從左和右開始向中間走,兩個數的和大了,右指針左移,和偏小,左指針右移......

# 四個問題不吉利,小編拍胸脯保證,馬上(百年之內)把第五個問題補起來!
def twoSum(numbers: list, target: int) -> list:
    i, j = 0, len(numbers) - 1
    while i < j:
        if numbers[i] + numbers[j] == target:
            return [i, j]
        elif numbers[i] + numbers[j] > target:
            j -= 1
        else:
            i += 1
    return []

if __name__ == '__main__':
    numbers = [2, 7, 11, 15]
    target = 9
    ans = twoSum(numbers,target)
    print(ans)


問題6:移除特定值(寫五道已經達到我的標準了,奈何又看到一個較為經典的案例,那就......再來個雙指針教學吧~),將數組當中等于’target'的值移除掉,空間復雜度限定為O(1),最后結果返回新數組長度。
例子:
輸入:[1,2,7,4,4,5],target = 4
輸出:4 (新數組為[1,2,7,5],所以長度為4)
快指針遍歷數組,慢指針則作為新數組的尾部。只有當快指針所指的值和‘target’不同時,才會執行賦值以及p+=1操作,相同時直接跳過該數。

def removeElement(numbers: list, target: int) -> int:
    p = 0
    for i in range(len(numbers)):
        if numbers[i] != target:
            numbers[p] = numbers[i]
            p +=1
    return p

if __name__ == '__main__':
    numbers = [1,7,3,4,4,5]
    target = 4
    ans = removeElement(numbers,target)
    print(ans)
    print(numbers[:ans])


問題7:滿足要求的最小子數組(Minimum Size Subarray Sum)。不好意思我忍不住又添了一道......該題給定n只包含正整數的數組,以及一個整數s,要求是找到一個最小長度的連續子數組,該子數組的元素和≥s。 如果不存在,則返回0。
輸入: s = 7, nums = [2,3,1,2,4,3]
輸出: 2(子數組 [4,3] 是滿足題目約束的最小數組)
運行下面程序會輸出每步變量信息,大家可以自己讀讀試試,看能否理解~如果不理解可以留言探討,現在已經晚上11點了,小編扛不住,這道題就不寫分析了......

def minSubArrayLen( s: int, nums: list) -> int:
    total = left = 0
    result = len(nums) + 1
    for right, num in enumerate(nums):
        total += num
        while total >= s:
            result = min(result, right - left + 1)
            total -= nums[left]
            left += 1
            print('total:',total,'result:',result,'left:',left,'right:',right)
    return result if result <= len(nums) else 0

if __name__ == '__main__':
    numbers = [2,3,1,2,4,3]
    target = 7
    ans = minSubArrayLen(target,numbers)
    print(ans)


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。