学习资料来源:
【算法通关手册】:https://algo.itcharge.cn/
【datawhale组队学习】https://github.com/datawhalechina/team-learning
大多是对这次组队学习的学习资料的学习笔记,非原创~
1. 数组简介
1.1 数组定义
- 数组(Array):一种线性表数据结构。它使用一组连续的内存空间,来存储一组具有相同类型的数据。是相同类型的数据元素构成的有序集合。
下面看一个整数数组的存储方式:
- 数组的每一个数据元素都占有一定的存储单元;
- 每个存储单元都有自己的内存地址;
- 元素之间紧密排列。
数组定义的另外两种解释:使用了顺序存储结构的线性表
- 线性表:
- 一条线;
- 数据元素类型相同;
- 数据元素最多前后两个方向。
- 连续的内存空间:
- 顺序存储结构(另一种是链式);
- 占用连续的内存空间;
- 相邻数据元素,存储位置也相邻。
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 插入元素
- 在数组尾部插入值为
val
的元素 O ( 1 ) O(1) O(1); - 在数组的第
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
- 先检查下标
i
是否合法; - 把第
i
个位置到第len(nums)-1
位置上的元素依次向后移动; - 再在第
i
个元素位置插入val
值; - 更新数组的元素计数值。【因为移动元素的操作次数跟元素个数有关,最坏和平均复杂度都是 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
:- 需要先检查
i
的范围是否在合法的范围区间,即0 <= i <= len(nums) - 1
- 后将第
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 删除元素
- 删除尾部元素;
- 删除数组第
i
个位置上的元素; - 基于条件删除元素.
- 删除尾部元素:将元素计数值减一即可。使得原尾部非法。 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
个位置上的元素:- 检查
i
是否合法; - 将第
i + 1
个位置到第len(nums) - 1
位置上的元素依次向左移动; - 删除后修改数组的元素计数值。
删除中间位置元素的操作涉及移动元素,而移动元素的操作次数跟元素个数有关,因此删除中间元素的最坏和平均时间复杂度都是 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]
- 基于条件删除元素:
- 删除多个元素操作中涉及到的多次移动元素操作;
- 可以通过算法改进,将多趟移动元素操作转变为一趟移动元素,从而将时间复杂度降低为 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. 数组总结
- 数组是最基础、最简单的数据结构。数组是相同类型的数据元素构成的有序集合。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
- 数组的最大特点的支持随机访问。
- 其访问元素、改变元素的时间复杂度为 O ( 1 ) O(1) O(1);
- 在尾部插入、删除元素的时间复杂度也是 O ( 1 ) O(1) O(1);
- 普通情况下插入、删除元素的时间复杂度为 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