数组是指在一段连续的内存空间中的一段有限的、类型相同的数据的集合。
在学习一个数据结构时,我们常常关心他的访问,搜索,插入,删除等操作。
数组的访问:
数组可以通过下标随机访问数组中任何一个元素,因为数组元素的存储是连续的,所以可以通过数组内存空间的首地址加上元素的偏移量计算出某一个元素的内存地址,如下:
array[n]的地址 = array数组内存空间的首地址 + 每个元素大小*n
通过上述公式可知:数组中通过下标去访问数据时并不需要遍历整个数组,因此数组的访问时间复杂度是 O(1)
数组的搜索:
通过内容去查找数组中的元素,时间复杂度不是O(1),极端的情况下需要遍历整个数组的元素,时间复杂度是O(n)。
数组的插入与删除:
同样是因为数组元素的连续性要求,所以导致数组在插入和删除元素的时候效率比较低。
如果要在数组中间插入一个新元素,就必须要将要相邻的后面的元素全部往后移动一个位置,留出空位给这个新元素。如果新元素是插入在数组的最开头位置,那整个原始数组都需要向后移动一位,此时的时间复杂度为最坏情况即O(n),数组的删除与数组的插入是类似的。
下面是python中数组常用的操作:
# create an array
a = []
# add element time complexity:O(1)
a.append(1)
a.append(2)
a.append(3)
print(a) # [1,2,3]
# insert element time complexity:O(N)
a.insert(2,100)
print(a) # [1,2,100,3]
# access element time complexity:O(1)
temp = a[2]
print(temp) # 100
# updata element time complexity:O(1)
a[2] = 99
print(a) # [1,2,99,3]
# remove element time complexity:O(N)
a.remove(99)
print(a) # [1,2,3]
a.pop(1)
print(a) # [1,3]
a.pop()
print(a) # [1]
# get size
b = [10,20,30]
size = len(b)
print(size) # 3
# iterate array time complexity:O(N)
for i in b:
print(i)
# find an element time complexity:O(N)
index = b.index(20)
print(index) # 1
# sort an array time complexity:O(NlongN)
a = [3,1,2]
b = a.sort()
c = a.sort(reverse=True)
print(b,c) # [1,2,3],[3,2,1]
在熟悉了数组这一数据结构之后,来看看对应的简单题。力扣485题
对于这道题,我们首先要对数组nums进行遍历,查看数组为1的元素,定义一个变量count来记录连续为1的数量,以示例1为例,[1,1,0,1,1,1],我们在遍历前两个元素时,得到count为2,但是下一个元素为0,我们需要定义一个result变量来记录count的值,然后将count清零,再继续往下遍历。后面连续三个1,此时count=3,我们最后只需返回count与result中较大的值即可。示例代码如下:
class Solution(object):
def findMaxConsecutiveOnes(self, nums):
if nums is None or len(nums)==0: # 如果数组是空的直接返回0
return 0
count = 0
result = 0
for i in nums: # 遍历数组
if i == 1:
count += 1 # 如果为1,count+1
else:
result = max(result,count) # 如果为0,把count赋给result
count = 0 # 清空count继续往下遍历
return max(result,count)
# time complexity:O(N) # 有一个for循环,对数组进行遍历
# space complexity:O(1) # 没有新建的数据结构
参考大佬的进阶解法:用index记录最后一个0所在的索引,则res为i-index(或res),具体思路看大佬的讲解[1]
class Solution(object):
def findMaxConsecutiveOnes(self, nums):
index = -1 # index为最后一个0所在的索引,初始化为-1
res = 0 # res记录最大1的个数
for i, num in enumerate(nums):
if num == 0:
index = i # index为最后一个0所在的索引
else:
res = max(res, i - index) # 返回i-index与res中较大的值
return res
# time complexity:O(N)
# space complexity:O(1)
力扣283:
我们要在不改变顺序的前提下把0移到末尾,首先定义一个index从第一个元素的索引依次指向最后一个元素的索引,再对nums进行遍历,遇到非0的数,把他移到前面来,全部移动完后,只需要把数组长度内的剩下元素全部置为0就可以达到题目所要的效果。
class Solution:
def moveZeroes(self, nums: List[int]):
index = 0 # index初始化指向第一个索引
for i in range(0,len(nums)): # 遍历数组
if nums[i] != 0: # 如果这个元素不为0
nums[index] = nums[i] # 把他们移到index的位置(也就是移到前面)
index += 1 # index依次往后移动
# 循环结束,已经把所有非0元素按顺序移到前面,只需把后面长度内的元素的全部置0即可
for i in range(index,len(nums)):
nums[i] = 0
# time complexity:O(N) for循环,遍历N次,两个for循环为并列关系,2N系数通常不管,复杂度为O(N)
# space complexity:O(1) 没有产生新的数据结构
python暴力解法:python中count(0)函数可以获取数组中元素0的个数,循环0的个数的次数,每次删除其中的0并重新添加0(添加在后面)即可完成任务。但是这种算法的时间复杂度较高。需要注意的是,remove函数的用法。remove()方法是对列表元素进行删除操作的方法,括号中的参数是指定要删除的元素。该方法并不会删除列表中所有的指定要删除的元素,只会在该元素第一次出现时(从前往后),将该位置的元素删除,同时返回删除后的新列表),append()函数则是在后面添加。
class Solution:
def moveZeroes(self, nums: List[int]):
for i in range(nums.count(0)): # 遍历0的数量的次数,count函数复杂度N
nums.remove(0) # 复杂度N
nums.append(0)
# time complexity:O(N^2)
# space complexity:O(1)
力扣27
同相双指针解法:首先定义两个指针i和j,然后遍历数组,把不等于val的元素换到前面来(变相等于把val换到后面去)即可。
class Solution:
def removeElement(self, nums: List[int], val: int):
i, j = 0, 0 # 定义双指针
while i < len(nums): # 遍历数组
if nums[i] != val: # 如果不等于val,执行下面两部
nums[j] = nums[i] # 换到前面来
j += 1 # j是位置,从前往后
i += 1 # 如果等于val,就继续往下遍历
return j # 返回前j个,不需要考虑数组中超出新长度后面的元素
# time complexity:O(N)
# space complexity:O(1)
相向双指针解法:首先定义一个左指针一个右指针,然后遍历数组,当左指针指向val右指针不指向val时交换位置,同时左指针右移右指针左移确保遍历继续。(详见注释)
class Solution:
def removeElement(self, nums: List[int], val: int):
left = 0
right = len(nums) - 1
while(left <= right):
if nums[left] == val: # 如果左指针指向val
if nums[right] != val: # 如果右指针不指向val,交换位置,左指针右移右指针左移
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1 # 如果右指针指向val,直接左移不要这个元素
else: # 如果左指针不指向val,继续往后遍历
left += 1
return left
# time complexity:O(N)
# space complexity:O(1)