20211116_datawhale31期_leetcode刷题_1数组

15 篇文章 0 订阅
11 篇文章 0 订阅

一 数组

在这里插入图片描述

来源

Datewhle31期__LeetCode 刷题 :

1.1 基础理论

数组(Array):一种线性表数据结构。它使用一组连续的内存空间,来存储一组具有相同类型的数据。

  • 数组 就是相同类型的数据元素构成的有序集合.

  • 数组常与链表比较:

    可见数组的特点时可根据下标及寻址公式, 进行随机访问且效率高

寻址公式如下:下标 i 对应的数据元素地址 = 数据首地址 + i * 单个数据元素所占内存大小


  • 一维及多维数组图例:

在这里插入图片描述

  • 以二维数组为例,数组的形式如下图所示。

在这里插入图片描述


  • 数组在C, java, python的实现

    1. C
      数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
      在这里插入图片描述
      不管是基本类型数据,还是结构体、对象,在数组中都是连续存储的
      如:
int arr[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};

在这里插入图片描述
C中在内存中是连续存储的, 如上图,一个二维数组就可以看成一个一维数组,只是里面存放的元素为一维数组。所以C中的数组是呈线性结构

  1. Java
    Java 中的数组也是存储相同类型数据的,但所使用的内存空间却不一定是连续(多维数组中)。且如果是多维数组,其嵌套数组的长度也可以不同。例如:
int[][] arr = new int[3][]{ {1,2,3}, {4,5}, {6,7,8,9}};

在这里插入图片描述

如上图, 在Java中,数组都是引用实体变量,呈树形结构,每一个叶子节点之间毫无关系,只有引用关系,每一个引用变量只引用一个实体
3. Python
原生 Python 中其实没有数组的概念,而是使用了类似 Java 中的 ArrayList 容器类数据结构,叫做列表。通常我们把列表来作为 Python 中的数组使用。Python 中列表存储的数据类型可以不一致,数组长度也可以不一致。例如:

  	arr = ['python', 'java', ['asp', 'php'], 'c']
  • 数组的操作—增查改删
  1. 访问及查找
    • 访问数组中第 i 个元素:只需要检查i的范围是否在合法的范围区间, 时间复杂度为O(1)
    • 查找数组中元素值为 val 的位置:在数组无序的情况下,只能通过将 val 与数组中的数据元素逐一对比的方式进行检索,也称为线性查找------------时间复杂度为O(n)
  2. 插入
    • 尾部插入:如果数组尾部容量不满,则直接把 val 放在数组尾部的空闲位置,并更新数组的元素计数值。如果数组容量满了,则插入失败
      不过,Python 中的 list 做了其他处理,当数组容量满了,则会开辟新的空间进行插入。在尾部插入元素的操作不依赖数组个数,其时间复杂度为O(1)
      python用append即可/:
arr = [0, 5, 2, 3, 7, 1, 6]
val = 4
arr.append(val)
print(arr)

在这里插入图片描述
2. i 个位置上插入: 先检查插入下标 i 是否合法,即 0 <= i <= len(nums)。确定合法位置后,通常情况下第 i 个位置上已经有数据了(除非i == len(nums) ),要把第 i 个位置到第len(nums) - 1 位置上的元素依次向后移动,然后再在第 i 个元素位置插入 val 值,并更新数组的元素计数值。因为移动元素的操作次数跟元素个数有关,最坏和平均时间复杂度都是O(n)

Python 中的 list 直接封装了中间插入操作,直接调用 insert 方法即可。

arr = [0, 5, 2, 3, 7, 1, 6]
i, val = 2, 4	
arr.insert(i, val)
print(arr)
  1. 改变:

    • 将数组中第 i 个元素值改为 val:改变元素操作跟访问元素操作类似。需要先检查 i 的范围是否在合法的范围区间,即 0 <= i <= len(nums) - 1。然后将第 i 个元素值赋值为 val。访问操作不依赖于数组中元素个数,因此时间复杂度为O(1)
      在这里插入图片描述
  2. 删除

    • 删除数组尾部-----------------将元素计数值减一即可, 时间复杂度为 O(1)。------python中使用pop
    • 删除数组第 i 个位置上的元素---------先检查下标 i 是否合法,即 o <= i <= len(nums) - 1。如果下标合法,则将第 i + 1 个位置到第 len(nums) - 1 位置上的元素依次向左移动。删除后修改数组的元素计数值。删除中间位置元素的操作同样涉及移动元素,而移动元素的操作次数跟元素个数有关,因此删除中间元素的最坏和平均时间复杂度都是O(n)-------------同样使用pop

在这里插入图片描述
3. 基于条件删除元素 :------------------通过循环检查元素,查找到元素后将其删除。删除多个元素操作中涉及到的多次移动元素操作,可以通过算法改进,将多趟移动元素操作转变为一趟移动元素,从而将时间复杂度降低为O(n) 。一般而言,这类删除操作都是线性时间操作,时间复杂度为O(n) 。---------------remove

arr = [0, 5, 2, 3, 7, 1, 6]
i = 3
arr.remove(5)
print(arr)

1.2 数组基础题目

1.2.1 189旋转数组

https://leetcode-cn.com/problems/rotate-array/

  • 法一--------自己第一想到----------O(n)
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k %= len(nums)
        for i in range(k): 
            nums.insert(0,nums[-1])
            nums.pop()
  • 法二-------三次颠倒-----------------也是参考答案

在这里插入图片描述

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k %= len(nums)
        nums[:] = nums[::-1]       
        nums[:k] = nums[:k][::-1]
        nums[k:] = nums[k:][::-1]
  • 法三-------拼接------------与二类似
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k %= len(nums)
        nums[:] = nums[-k:] + nums[:-k] 

在这里插入图片描述

1.2.2 66加一

https://leetcode-cn.com/problems/plus-one/

  • 法一--------------直接想到的----------转换成int+1 再转回列表
class Solution:
    def plusOne(self, digits: List[int]) -> List[int]:
        dig = int(''.join(str(i) for i in digits)) + 1
        return [int(i) for i in list(str(dig))]
  • 法二-------------参考答案-----------首位补0 + 尾巴加1 循环判断尾巴是否为9

想到尾巴加一思路-------但自己不知如何处理1999999这种情况---------前面补0太秒

思路: 这道题把整个数组看成了一个整数,然后个位数 +1。问题的实质是利用数组模拟加法运算。

如果个位数不为 9 的话,直接把个位数 +1 就好。如果个位数为 9 的话,还要考虑进位。

具体步骤:

  1. 数组前补 0 位。
  2. 将个位数字进行 +1 计算。
  3. 遍历数组
    1.如果该位数字 大于等于 10,则向下一位进 1,继续下一位判断进位。
    2.如果该位数字 小于 10,则跳出循环。
def plusOne(self, digits: List[int]) -> List[int]:
    digits = [0] + digits
    digits[len(digits)-1] += 1
    for i in range(len(digits)-1,0,-1):
        if digits[i] != 10:
            break
        else:
            digits[i] = 0
            digits[i-1] += 1
        
    if digits[0] == 0:
        return digits[1:] 
    else:
        return digits

在这里插入图片描述

1.2.3 724 . 寻找数组的中心下标

https://leetcode-cn.com/problems/find-pivot-index/

法1 ------------- 一开始想到的 利用for循环 条件为 sum(:i) == sum(i+1:)

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            if sum(nums[:i]) == sum(nums[i+1:]):
                return i
        return -1
  • 结果时间爆炸------------------- 6684ms!!!

由于没刷题经验…经实验 → for里面加sum() = 完犊子

  • 看参考答案总结利用 → 两边和(sum左或右) + 目前值(num[i]) = 列表总和(tatol) → 即可

  • 法二

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        sum_1 = 0
        total = sum(nums)     #注意:  求和位于for之外
        for i in range(len(nums)):
            if sum_1 * 2 + nums[i] == total:
                return i
            sum_1 += nums[i]
        return -1

在这里插入图片描述

1.2.4 0485. 最大连续 1 的个数

https://leetcode-cn.com/problems/max-consecutive-ones/

  • 一个暂存计数(sum_1), 另一用来返回最大计数(count)
class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        count = 0
        sum_1 = 0
        for i in nums:
            if i == 1:
                sum_1 += 1
                count = max(sum_1, count)
            else:
                sum_1 = 0   #碰到0重新计数
        return count

在这里插入图片描述

1.2.5 238 . 除自身以外数组的乘积

https://leetcode-cn.com/problems/product-of-array-except-self/

  • 思路: num[i]左边乘积 与右边乘积 相乘 → for循环出两边乘积

在这里插入图片描述

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        right = 1
        left = 1
        output = [1 for _ in range(len(nums))]
        for i in range(len(nums)):
            output[i] *= left
            left *= nums[i]

        for j in range(len(nums)-1, -1 , -1):
            output[j] *= right
            right *= nums[j]


        return output

在这里插入图片描述

1.2.6 面试题 01.07 . 旋转矩阵

https://leetcode-cn.com/problems/rotate-matrix-lcci/

  • 方法一 →→→→→首先想到用numpy → 先拆分再组合(实际不符题意—占用额外的空间且速度超级慢)
import numpy as np
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        
        m = np.array(matrix)     #转换成array数组
        n = m.shape[0]
        m = np.split(m,n,axis = 0)    #将原数组按行拆分
        m = np.stack(m[::-1],axis = 2)   # 将拆分结构翻转并组合
        matrix[:] = m.tolist()[0]

在这里插入图片描述


一行即解决---------但时间和内存和法一差不多

matrix[:] = np.rot90(np.array(matrix), -1).tolist()
  • 法三 →→→→→ 上下翻 , 再对角翻 →参考答案→ 较符合题意且符合算法逻辑
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)
        for i in range(n//2):
            for j in range(n):
                matrix[i][j],matrix[n-1-i][j] = matrix[n-1-i][j],matrix[i][j]
        for i in range(n):
            for j in range(i):
                matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j]

在这里插入图片描述

1.2.7 面试题 01.08 . 零矩阵

https://leetcode-cn.com/problems/zero-matrix-lcci/

  • 法一-------------- 先想到记录0的row和col 再分别将行列变为0
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        count_x = set()
        count_y = set()
        n_x = len(matrix)
        n_y = len(matrix[0])

        for i in range(n_x):
            for j in range(n_y):
                if matrix[i][j] == 0:
                    count_x.add(i)
                    count_y.add(j)
                    
        for i in count_x:
            matrix[i] = [0 for _ in range(n_y)]

        for i in range(n_x):
            for j in count_y:
                matrix[i][j] = 0
  • 法二 --------------------参考答案-----------利用原本空间即第一行第一列的bool来记录0-----------妙妙妙!
class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """

        rows = len(matrix)
        cols = len(matrix[0])

        row_0 = False
        col_0 = False    #第一行及第一列记录所对应 行列 是否有0元素  ---不另外占空间 

        for i in range(rows):
            if matrix[i][0] == 0:
                col_0 = True     #首列存在0元素,  首列 清零开关打开
                break

        for j in range(cols):
            if matrix[0][j] == 0:
                row_0 = True     #首行存在0元素,  首行 清零开关打开
                break


        for i in range(1, rows):
            for j in range(1, cols):   
                if matrix[i][j] == 0:
                    matrix[0][j] = matrix[i][0] = 0  #将元素为0对应的首行, 首列元素改为0

        for i in range(1, rows):
            for j in range(1, cols):
                if matrix[0][j] == 0 or matrix[i][0] == 0:  #如果首行首列为0, 对应行列全改为0
                    matrix[i][j] = 0

        
        if col_0:
            for i in range(rows):        #改首列全为0
                matrix[i][0] = 0 


        if row_0:
            for j in range(cols):     
                matrix[0][j] = 0    

在这里插入图片描述


1.2.8 498 . 对角线遍历

https://leetcode-cn.com/problems/diagonal-traverse/

  • 好难----------一直没找着规律— 下标相加之和递增 → 且分 奇偶: 下标和偶向上; 下标和奇数向下:

  • 找规律:

    • 当行号 + 列号为偶数时,遍历方向为从左下到右上。可以记为右上方向(-1, +1),即行号 -1,列号 +1。
    • 当行号 + 列号为奇数时,遍历方向为从右上到左下。可以记为左下方向(+1, -1),即行号 +1,列号 -1。

边界情况:

  • 向右上方向移动时:
    • 如果在最后一列,则向下方移动,即 x += 1。
    • 如果在第一行,则向右方移动,即 y += 1。
    • 其余情况想右上方向移动,即 x -= 1、y += 1。
  • 向左下方向移动时:
    • 如果在最后一行,则向右方移动,即 y += 1。
    • 如果在第一列,则向下方移动,即 x += 1。
    • 其余情况向左下方向移动,即 x += 1、y -= 1。
class Solution:
    def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:
        rows = len(mat)
        cols = len(mat[0])
        count = rows * cols
        x, y = 0, 0
        ans = []

        for i in range(count):
            ans.append(mat[x][y])

            if (x + y) % 2 == 0:
                # 最后一列
                if y == cols - 1:
                    x += 1
                # 第一行
                elif x == 0:
                    y += 1
                # 右上方向
                else:
                    x -= 1
                    y += 1
            else:
                # 最后一行
                if x == rows - 1:
                    y += 1
                # 第一列
                elif y == 0:
                    x += 1
                # 左下方向
                else:
                    x += 1
                    y -= 1
        return ans

在这里插入图片描述

1.2.9 48 . 旋转图像

https://leetcode-cn.com/problems/rotate-image/

  • 规律: matrixnew[col][n−row−1] = matrix[row][col]

  • 方法与旋转矩阵一样: 先上下翻, 再对角翻

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """

        n = len(matrix)
        for i in range(n//2):
            for j in range(n):
                matrix[i][j],matrix[n-1-i][j] = matrix[n-1-i][j],matrix[i][j]
        for i in range(n):
            for j in range(i):
                matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j]

1.2.10 118 . 杨辉三角

https://leetcode-cn.com/problems/pascals-triangle/

  • 简单—
class Solution:
    def generate(self, numRows: int) -> List[List[int]]:
        n = []
        for i in range(numRows):
            tmp = [1 for _ in range(i + 1)]  # 将每一行填充为全为一的list
            n.append(tmp)
            for j in range(1,i):
                n[i][j] = n[i-1][j] + n[i-1][j-1]  # 当前元素等于上两个之和
        return n

1.2.11 119 . 杨辉三角二

https://leetcode-cn.com/problems/pascals-triangle-ii/

  • 法一: numRows+1 , 返回以上代码 n[-1]即可
class Solution:
    def getRow(self, rowIndex: int) -> List[int]:
        n = []
        for i in range(rowIndex + 1):
            tmp = [1 for _ in range(i + 1)]  # 将每一行填充为全为一的list
            n.append(tmp)
            for j in range(1,i):
                n[i][j] = n[i-1][j] + n[i-1][j-1]  # 当前元素等于上两个之和
        return n[-1]

1.2.12 73 . 矩阵置零

https://leetcode-cn.com/problems/set-matrix-zeroes/

参考资料

  1. https://zhuanlan.zhihu.com/p/105962783-----什么是数组?
  2. https://www.runoob.com/cprogramming/c-arrays.html C数组
  3. https://blog.csdn.net/qq_42913794/article/details/89077825Java和C的数组区别
  4. https://zhuanlan.zhihu.com/p/57893374浅拷贝和深拷贝
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值