一、数组基础
1.数组简介
1.1数组定义
数组(Array):一种线性表数据结构。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
1.整数数组的储存方式:
1.2如何随机访问数组元素
可以进行随机访问。即数组可以根据下标,直接定位到某一个元素存放的位置。
数组寻址公式如下:下标 i 对应的数据元素地址 = 数据首地址 + i × 单个数据元素所占内存大小。
1.3多维数组
二维数组其本质上是以数组作为数据元素的数组,二维数组的第一维度表示行,第二维度表示列。
二维数组形式如下图:
1.4不同编程语言中二维数组的创建
1.c/c++:
C / C++ 语言中的数组最接近数组结构定义中的数组,使用的是一块存储相同类型数据的、连续的内存空间。不管是基本类型数据,还是结构体、对象,在数组中都是连续存储的。
int arr[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};
2.java:
Java 中的数组跟数据结构定义中的数组不太一样。Java 中的数组也是存储相同类型数据的,但所使用的内存空间却不一定是连续(多维数组中)。且如果是多维数组,其嵌套数组的长度也可以不同。
int[][] arr = new int[3][]{ {1,2,3}, {4,5}, {6,7,8,9}};
3.python:
python使用了类似 Java 中的 ArrayList 容器类数据结构,叫做列表。通常我们把列表来作为 Python 中的数组使用。Python 中列表存储的数据类型可以不一致,数组长度也可以不一致。
arr = ['python', 'java', ['asp', 'php'], 'c']
2.数组的基本操作
我将通过LeetCode中的题目来带大家认识python中的数组操作。
1.访问元素
2.查找元素
3.插入元素
4.改变元素
5.删除元素
在LeetCode有一道非常经典的题,两数之和,这道题考查了我们对数组元素的访问。
个人题解:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i, x in enumerate(nums): # x=nums[i],nums中的元素可以通过nums[i]的方式直接访问
#enumerate(nums)
可以返回一个迭代器,其中包含每个元素的索引和对应的值。i
表示索引,x
表示对应的值。
for j in range(i + 1, len(nums)): # 枚举 i 右边的 j
if x + nums[j] == target: # 满足要求
return [i, j] #返回结果
这道题涉及访问、插入元素以及数组和数字之间的转换,我们通过取出数组中的每个元素,并在运算操作后再将数据插入数组中。
个人题解:
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
num=0
for i in digits:#将数组中的元素取出
num*=10
num+=i
num+=1
digits_0=[]
while num!=0:
digits_0.insert(0,num%10)#将处理后的数据插入数组
num=num//10
return digits_0
这道题涉及到查找元素以及对数组的理解
个人题解:
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
n=len(nums)
total=sum(nums)
#计算整个数组的总和 total
,作为右侧元素的和。
start=0
#初始化 start
为 0,用于记录左侧元素的和
for i in range(n):
total-=nums[i]
#遍历数组 nums
,对于每个元素,首先将其从 total
中减去,得到右侧元素的和(不包括当前元素)
if start==total:
return i
#检查 start
是否等于 total
,如果相等,则说明当前索引 i
是中心索引,返回 i
start+=nums[i]
return -1
#如果没有找到中心索引,则返回 -1
该题涉及到改变元素、以及对元素的倒数访问
个人题解:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
k=k%len(nums)
nums[:]=nums[-k:]+nums[:-k]
在此题中注释表示函数不要返回任何值,而是直接在原始的 nums
列表上进行修改。
我们来区分一下nums=nums[-k:]+nums[:-k]与nums[:]=nums[-k:]+nums[:-k]区别
前者表示创建一个新的列表,并将其赋值给 nums后者
使用切片赋值的方式来修改列表。通过将新列表的元素切片赋值给 nums
,可以直接修改列表的内容,而不创建新的列表对象,而此题要求我们直接在原始的列表上修改,故采用后者来对nums的值进行修改。
3.数组的基础知识总结
数组是最基础、最简单的数据结构。数组是实现线性表的顺序结构存储的基础。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
数组的最大特点的支持随机访问。访问数组元素、改变数组元素的时间复杂度为 O(1),在数组尾部插入、删除元素的时间复杂度也是 O(1),普通情况下插入、删除元素的时间复杂度为 O(n)。
4.数组练习题目
部分题目已在前面的教程中展出
1.
解决该题目需要用到矩阵的转置以及行顺序调换
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):
for j in range(i+1,n):#通过双重循环进行矩阵的转置
temp=matrix[i][j]
matrix[i][j]=matrix[j][i]
matrix[j][i]=temp
for i in range(n):
for j in range(n//2):#通过双重循环完成每行元素顺序的反转
temp=matrix[i][n-1-j]
matrix[i][n-1-j]=matrix[i][j]
matrix[i][j]=temp
2.
此题考查了对二维数组结构的理解,我们采用a,b,m,n分别表示图表的上边界、下边界、左边界、右边界,每当添加完一行或一列数据后把对应上边界和左边界的记录值减一,下边界和右边界的记录值加一,从而完成图表的更新,如此循环,最终得出结果。
个人题解如下:
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
a=0
b=len(matrix)-1
m=0
n=len(matrix[0])-1
count=(b+1)*(n+1)#count表示二维数组的元素数
result=[]
if count==0:
return result#若为空数组则直接返回
else:
while True:
for i in range(m,n+1):
result.append(matrix[a][i])#从左到右添加行数据
a+=1#上边界更新
if a>b:
break
for i in range(a,b+1):
result.append(matrix[i][n])#从上到下添加列数据
n-=1#右边界更新
if m>n:
break
for i in range(n,m-1,-1):
result.append(matrix[b][i])#从右向左添加行数据
b-=1#下边界更新
if a>b:
break
for i in range(b,a-1,-1):
result.append(matrix[i][m])#从下到上添加列数据
m+=1#上边界更新
if m>n:
break
return result#返回添加后的数组
3.
本题通过巧妙的方式考察了对二维数组下标的访问,以及对循环的理解和掌握,通过使用两个嵌套循环来遍历矩阵的对角线;使用条件判断进行不同情况的处理,根据循环次数的奇偶性,选择不同的遍历方向;列表操作的运用,使用列表推导式生成并添加遍历的元素到结果列表。
个人题解:
class Solution:
def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:
m=len(mat)
n=len(mat[0])
result=[]
for i in range(m+n-1):#通过使用两个嵌套循环来遍历矩阵的对角线
if i % 2 == 0:#根据循环次数的奇偶性,选择不同的遍历方向
result+=[mat[count][i-count]for count in range(min(i,m-1),max(-1,i-n),-1)]
else:#使用列表推导式生成并添加遍历的元素到结果列表
result+=[mat[count][i-count]for count in range(max(0,i-n+1),min(i+1,m))]
return result
4.
这道题较为基础,需要遍历给定的二进制数组 nums
,以便逐个检查其中的元素,以及根据当前的元素值(0 或 1)来判断连续 1 的个数应该如何更新,在遇到 0 时如何重新计数,还需要不断更新一个最大值变量,以便记录已经遍历过的连续 1 的最大个数。
个人题解:
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
count=[]
index=0
for i in nums:#遍历给定的二进制数组 nums
if i==1:#根据当前的元素值(0 或 1)来判断连续 1 的个数
index+=1#记录已经遍历过的连续 1 的最大个数
else:
count.append(index)
index=0
count.append(index)
return max(count)#取数组中的最大值
5.
个人题解:
这段代码时间复杂度为O(n),空间复杂度为O(1),核心思想是通过先遍历一次数组得到每个元素前面所有元素的乘积,再遍历一次数组得到每个元素后面所有元素的乘积,最后将前后两个乘积合并。
class Solution:
def productExceptSelf(self, nums: List[int]) -> List[int]:
result=[1]*len(nums)#result
列表是一个空列表,用于存储每个元素左侧所有元素的乘积
num=1
for i in range(len(nums)):
result[i]=num#遍历输入的 nums
列表,使用变量 num
记录当前位置左侧所有元素的乘积,并将其赋值给 result[i]
num*=nums[i]#更新 num
为当前位置元素的乘积,以便计算下一个位置左侧所有元素的乘积
num=1
for i in range(len(nums)-1,-1,-1):#进行一次反向的循环,从倒数第二个元素开始到第一个元素(索引为 len(nums)-1
到 -1)
result[i]*=num#计算每个元素右侧所有元素的乘积,并将其与 result[i]
相乘,得到每个元素除自身以外的所有元素的乘积。
num*=nums[i]
return result
6.
个人题解:
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
m=len(matrix)
n=len(matrix[0])#获取矩阵的行数和列数
a=set()
b=set()#创建两个空的集合 a
和 b
for i in range(m):#通过两层嵌套循环遍历矩阵的每个元素。
for j in range(n):
if matrix[i][j]==0:#如果当前元素的值为 0
a.add(i)#将该元素所在的行号和列号分别添加到集合 a
和 b
中
b.add(j)
for i in range(m):#再进行一次两层嵌套循环遍历矩阵的每个元素。
for j in range(n):
if (i in a)or(j in b):#对于每个元素,如果该元素所在的行号或列号出现在集合 a
或 b
中
matrix[i][j]=0#就将该元素的值修改为 0
7.
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
a=0
b=n-1
c=0
d=n-1
#使用类似的边界控制和循环结构,通过四个变量 a
、b
、c
、d
来表示当前矩阵的上下左右边界
result = [[0 for j in range(n)] for i in range(n)]
#创建一个大小为 n x n
的二维数组 result
,用于存放最终生成的蛇形矩阵
num=1
#循环开始之前,初始化变量 num
为 1,用于记录当前要填入的数字。接下来,通过一个无限循环来不断填充矩阵中的元素
while True:
for i in range(c,d+1):
#循环中,首先从左到右填充最上面的行,然后将上边界下移一行(a+=1
)
result[a][i]=num
num+=1
a+=1
if a>b:
break
for i in range(a,b+1):
#从上到下填充最右边的列,然后将右边界左移一列(d-=1
)
result[i][d] =num
num+=1
d-=1
if c>d:
break
for i in range(d,c-1,-1):
#从右到左填充最下面的行,将下边界上移一行(b-=1
)
result[b][i]=num
num+=1
b-=1
if a>b:
break
for i in range(b,a-1,-1):
#从下到上填充最左边的列,将左边界右移一列(c+=1
)
result[i][c]=num
num+=1
c+=1
if c>d:
break
#当所有位置都被填充完后,返回生成的蛇形矩阵 result
return result
8.
个人题解:
此题较难,思考半天也法处理细胞之间的相互影响,借鉴了下大佬的思路才运行成功
class Solution:
def gameOfLife(self, board: List[List[int]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
m=len(board)
n=len(board[0])
for i in range(m):
for j in range(n):
count=0#这里统计每个格子周围八个格子的活细胞数,每个格子计数重置为零
for a in range(-1,2):
for b in range(-1,2):
#if判断取出越界的情况
if (a==0 and b==0)or i+a<0 or i+a>=m or j+b<0 or j+b>=n:
#如果周围格子是活细胞(1)或活细胞变死细胞(2)的,都算一个活细胞
continue
if board[i+a][j+b]==1 or board[i+a][j+b]==2:
count+=1
if board[i][j] == 1 and (count < 2 or count > 3):
board[i][j] = 2
#格子本身是活细胞,周围满足变成死细胞的条件,标记为2
if board[i][j] == 0 and count == 3:
#格子本身是死细胞,周围满足复活条件,标记为3
board[i][j] = 3
for i in range(m):
for j in range(n):
# 死细胞为0,活细胞变成死细胞为2,都为偶数,模2为0,刚好是死细胞
# 活细胞为1,死细胞变成活细胞为3,都为奇数,模2为1,刚好是活细胞
board[i][j] %= 2
本文参考链接:
https://datawhalechina.github.io/leetcode-notes/#/ch01/01.02/01.02.01-Array-Basic
https://leetcode.cn/leetbook/
https://leetcode.cn/problems/game-of-life/solutions/2353592/javapython3cmo-ni-te-shu-zhi-biao-ji-by-9b72c/