数组基础知识

学习资料来源:

【算法通关手册】:https://algo.itcharge.cn/

【datawhale组队学习】https://github.com/datawhalechina/team-learning

大多是对这次组队学习的学习资料的学习笔记,非原创~

1. 数组简介

1.1 数组定义

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

下面看一个整数数组的存储方式:
在这里插入图片描述

  1. 数组的每一个数据元素都占有一定的存储单元;
  2. 每个存储单元都有自己的内存地址;
  3. 元素之间紧密排列。

数组定义的另外两种解释:使用了顺序存储结构的线性表

  1. 线性表:
    • 一条线;
    • 数据元素类型相同;
    • 数据元素最多前后两个方向。
  2. 连续的内存空间:
    • 顺序存储结构(另一种是链式);
    • 占用连续的内存空间;
    • 相邻数据元素,存储位置也相邻。

1.2 如何随机访问数据元素

此乃数组的一个最大特点:根据下标直接定位!

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

1.3 多维数组

  • 本质:数组的数组,即以数组作为数据元素的数组

1.4 编程实现python

  • 通常我们把列表作为python中的数组使用
  • python中列表存储的数组类型可以不一致,数组长度也可以不一致

例如:

arr = ['python', 'java', ['asp', 'php'], 'c']

2. 数组的基本操作

2.1 访问元素

访问数组中的第i个元素

合法区间:0 <= i <= len(nums) - 1

复杂度为 O ( 1 ) O(1) O(1)

示例:

# 从数组nums中读取下标为i的数据元素值

def value(nums, i):
    if 0 <= i <= len(nums) - 1:
        print(nums[i])

arr = [0, 5, 2, 3, 7, 1, 6]
value(arr, 3)
3

2.2 查找元素

val

  • 线性查找:逐一对比
    • 找不到可以返回一个特殊值(例如-1
    • O ( n ) O(n) O(n)

示例:

# 从数组nums中查找元素值为val的数据元素第一次出现的位置

def find(nums, val):
    for i in range(len(nums)):
        if nums[i] == val:
            return i
    return -1
    
arr = [0, 5, 2, 3, 7, 1, 6]
find(arr, 5)
1

2.3 插入元素

  1. 在数组尾部插入值为val的元素 O ( 1 ) O(1) O(1)
  2. 在数组的第i个位置上插入值为val的元素。
  • 插入尾部append方法:
arr = [0, 5, 2, 3, 7, 1, 6]
val = 4
arr.append(val)
arr
[0, 5, 2, 3, 7, 1, 6, 4]
  • 在第i个位置上插入val
    1. 先检查下标i是否合法;
    2. 把第i个位置到第len(nums)-1位置上的元素依次向后移动;
    3. 再在第i个元素位置插入val值;
    4. 更新数组的元素计数值。【因为移动元素的操作次数跟元素个数有关,最坏和平均复杂度都是 O ( n ) O(n) O(n)
  • insert方法:在这里插入图片描述
arr = [0, 5, 2, 3, 7, 1, 6]
i, val = 2, 4
arr.insert(i, val)
arr
[0, 5, 4, 2, 3, 7, 1, 6]

2.4 改变元素

  • 将数组中第 i 个元素值改为 val:

    1. 需要先检查i的范围是否在合法的范围区间,即0 <= i <= len(nums) - 1
    2. 后将第 i个元素值赋值为 val

    访问操作不依赖于数组中元素个数,因此时间复杂度为 O ( 1 ) O(1) O(1)在这里插入图片描述

def change(nums, i, val):
    if 0 <= i <=len(nums) - 1:
        nums[i] = val
        
arr = [0, 5, 2, 3, 7, 1, 6]
i, val = 2, 4
change(arr, i, val)
arr
[0, 5, 4, 3, 7, 1, 6]

2.5 删除元素

  1. 删除尾部元素;
  2. 删除数组第i个位置上的元素;
  3. 基于条件删除元素.
  • 删除尾部元素:将元素计数值减一即可。使得原尾部非法。 O ( 1 ) O(1) O(1)

pop方法:

arr = [0, 5, 2, 3, 7, 1, 6]
arr.pop()
arr
[0, 5, 2, 3, 7, 1]
  • 删除数组第i个位置上的元素:

    1. 检查i是否合法;
    2. 将第i + 1个位置到第 len(nums) - 1位置上的元素依次向左移动;
    3. 删除后修改数组的元素计数值。

    删除中间位置元素的操作涉及移动元素,而移动元素的操作次数跟元素个数有关,因此删除中间元素的最坏和平均时间复杂度都是 O ( n ) O(n) O(n).

pop方法:

arr = [0, 5, 2, 3, 7, 1, 6]
i = 3
arr.pop(i)
arr
[0, 5, 2, 7, 1, 6]
  1. 基于条件删除元素:
    • 删除多个元素操作中涉及到的多次移动元素操作;
    • 可以通过算法改进,将多趟移动元素操作转变为一趟移动元素,从而将时间复杂度降低为 O ( n ) O(n) O(n)

remove方法

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

3. 数组总结

  • 数组是最基础、最简单的数据结构。数组是相同类型的数据元素构成的有序集合。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
  • 数组的最大特点的支持随机访问。
    1. 其访问元素、改变元素的时间复杂度为 O ( 1 ) O(1) O(1);
    2. 在尾部插入、删除元素的时间复杂度也是 O ( 1 ) O(1) O(1);
    3. 普通情况下插入、删除元素的时间复杂度为 O ( n ) O(n) O(n)

4. leetcode题目

  • 0066.加⼀
  • 0724.寻找数组的中⼼下标
  • 0189.旋转数组
  • 0048.旋转图像
  • 0054.螺旋矩阵
  • 0498.对⻆线遍历

0066.加⼀

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:

输入:digits = [0]
输出:[1]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/plus-one
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

digits = [1,2,3]
'''
    解题思路:
    1. 转化成字符串
    2. 再转化成数字加一
    3. 再变回字符串
    4. 再拆成整形列表
    
    主要是运用了字符串可迭代的性质,但是这样可能有些麻烦,而且貌似没有用到这节课的东西...后续再研究一下别人的做法
'''
digits = [1, 2, 3]
digits_str = [str(i) for i in digits]
num_str = ''
for i in digits_str:
    num_str = num_str + i 
new_num = int(num_str) + 1
new_digits = [int(i) for i in str(new_num)]
new_digits
[1, 2, 4]

0724.寻找数组的中⼼下标

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

示例 2:

输入:nums = [1, 2, 3]
输出:-1
解释:
数组中不存在满足此条件的中心下标。

示例 3:

输入:nums = [2, 1, -1]
输出:0
解释:
中心下标是 0 。
左侧数之和 sum = 0 ,(下标 0 左侧不存在元素),
右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-pivot-index
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

def pivotIndex(nums):
        '''
            解题思路:
            直接遍历!
            虽然好像还是太慢了。。
        '''
        for i in range(len(nums)):

            if sum(nums[:i]) == sum(nums[i + 1:]): 
                return i

        return -1

0189.旋转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105

进阶:

尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def rotate(numsk) -> None:
        """
        Do not return anything, modify nums in-place instead.
        思路:
            1. 新的列表做切片
            2. 再进行赋值
            运行速度还可以,但是由于新建了一个临时存放列表导致内存占用大。
        """
        if k > len(nums):
            k = k % len(nums)
        nums_temp = nums[len(nums) - k:] + nums[:len(nums) - k]
        for i in range(len(nums)):
            nums[i] = nums_temp[i]

0048.旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

示例 3:

输入:matrix = [[1]]
输出:[[1]]

示例 4:

输入:matrix = [[1,2],[3,4]]
输出:[[3,1],[4,2]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution:
    def rotate(matrix) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)
        # 对角线翻转
        for i in range(n - 1):
            for j in range(i + 1, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        # 水平翻转
        for i in range(n):
            for j in range(n >> 1):
                matrix[i][j], matrix[i][n - j - 1] = matrix[i][n - j - 1], matrix[i][j]

0054.螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

'''
参考题解:
作者:HUST_PDE_YCX
链接:https://leetcode-cn.com/problems/spiral-matrix/solution/yong-shi-40ms-xiao-hao-137mb-mei-bu-xiang-xi-zhu-s/
'''

class Solution:
    def spiralOrder( matrix):
        if not matrix:return []

        x=y=0                                     # 矩阵元素位置初始化
        res = []                                  # 初始化,存储遍历后的矩阵元素
        dx = [ 0, 1, 0,-1]                        # 方向:右,下,左,上
        dy = [ 1, 0,-1, 0]                        # 注:与通常平面坐标系 记号 不同
        di = 0                                    # 初始化方向变量
        visited = set()                           # 初始化集合,存储已走过的坐标
        m,n = len(matrix),len(matrix[0])          # 矩阵的行列 
                
        for i in range(m*n):                                     # 
            res.append(matrix[x][y])                             # 存储遍历矩阵过的元素
            visited.add((x,y))                                   # 存储遍历过的坐标
            tx,ty = x+dx[di],y+dy[di]                            # 先记录下一步坐标,用于判断下一步怎么走
            if 0<=tx<m and 0<=ty<n and (tx,ty) not in visited:   # 判断坐标是否需变向,且没有遍历过
                x,y = tx,ty                                       
            else:                                                
                di = (di+1)%4                                    # 改变方向,右下左上为一圈,防止方向坐标越界
                x,y = x + dx[di],y+dy[di]                        # 下一步坐标
        return res

0498.对⻆线遍历

'''
参考题解
作者:heuristic-chaplygincbp
链接:https://leetcode-cn.com/problems/diagonal-traverse/solution/dui-jiao-xian-bian-li-by-heuristic-chapl-49rd/
'''
class Solution:
    def findDiagonalOrder(mat):
        dir = [(-1, 1), (1, -1)]
        r, c = len(mat), len(mat[0])
        res = []
        i = 0
        j = 0
        temp = 0
        while temp < r + c - 1:
            while 1:
                res.append(mat[i][j])
                i += dir[temp % 2][0] # 偶数次向上遍历, 奇数次向下遍历
                j += dir[temp % 2][1]
                # 四个方向
                if i < 0 and j < c:
                    i = 0
                    break
                elif j < 0 and i < r:
                    j = 0
                    break
                elif j == c:
                    j = c - 1
                    i += 2 # 行超出边界,向下前进两个位置
                    break
                elif i == r:
                    i = r - 1
                    j += 2 # 列超出边界,向右前进两个位置
                    break
                temp += 1
            return res
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值