关于数组的算法
数组是什么?
在空间中,一个连续内存地址,并且元素类型相同的数据结构
数组的操作与时间复杂度
- 查询:O(1)
因为数组的内存空间是连续的,并且,数组的下标是0开始计算累加的,所以每当我们去通过下标查询数组元素的时候,被查询的位置是可以被计算出来的。比如当前有个数组,里面有四个元素,分别存储了1,2,3,4。这时候,第一个元素的内存地址是100,每个元素占4字节内存空间,所以数组中的第三个元素内存地址是100+4*2=108,这时候已经算出内存地址了,就可以直接获取到对应的元素了 - 插入:O(N)
插入之所以是O(N)的世界复杂的,是因为每次插入,除去在数组有空位,并且末尾插入的时候,其他时候均需要把数组中的元素向后面迁移,为新的数据腾出空间 - 修改:O(N)
修改的元素我们是不确定的,除非我们通过下标指定某个元素被修改了。比如我们需要修改元素值为4的元素,这时候我们需要去遍历数组中的每一个元素,判断值是否为4,然后进行修改 - 删除:O(N)
删除数组和修改同理,指定下标删除是O(1),但是要对某个值进行删除,则需要遍历数组
数组练习题
- 485最大连续1的个数
# 最开始想到的方法
class Solution(object):
def findMaxConsecutiveOnes(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
n = 0
x = 0
for i in nums:
if i == 1:
n += 1
else:
if n > x:
x = n
n = 0
if n > x:
return n
else:
return x
# 优化了一下的方法
class Solution(object):
def findMaxConsecutiveOnes(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
maxCount = count = 0
for i, num in enumerate(nums):
if num == 1:
count += 1
else:
maxCount = max(maxCount, count)
count = 0
maxCount = max(maxCount, count)
return maxCount
# 两个方法其实是一样的,只不过优化后的,使用了内置函数max对两个数字进行大小比较
- 283移动零
在力扣上的解题方案是使用双指针对元素进行判断和换位
def moveZeroes(nums: List[int]) -> None:
n = len(nums)
left = right = 0
while right < n:
if nums[right] != 0:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right += 1
其实也可以通过空间去换,但是这里的nums指向地址其实已经发生改变了,如果是在函数外面的nums传入进来,并且打印的也是外面的nums,那么我们这个解法是错误的。毕竟外面的nums是完全没改变的。
def moveZeroes(self, nums):
arr_1 = []
arr_2 = []
for i, v in enumerate(nums):
if v == 0:
arr_1.append(v)
else:
arr_2.append(v)
nums = arr_2 + arr_1
力扣里面的题目说明是这个的:

- 27移动元素
我这里搞了三个解法,但是时间复杂度,呃呃呃呃,有些不理想
方法一:
会有个额外的数组被创建,用作循环。然后通过从末尾删除,就不会影响nums的下标值了。当然,题目中说的不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。我选择忽略,欸嘿嘿。顺带说一下,切片是复制整个列表,到一个新的空间中,所以时间复杂度也应该是O(N)的
这个方案的时间复杂度是O(2N),空间复杂度是O(2N)
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
arr = nums[::-1]
last = len(nums) - 1
for i, v in enumerate(arr):
if v == val:
n = last - i
nums.pop(n)
return len(nums)
方法二
这个方法比较粗暴,只管查nums中是否存在val,如果有,则用remove直接删除。顺带解释一下python的作用:该方法没有返回值但是会移除列表中的某个值的第一个匹配项。这里说明remove是遍历查询,而in也是遍历查询
所以时间复杂度:O(N^2),空间复杂度为O(N)
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
while val in nums:
nums.remove(val)
return len(nums)
方法三
方法三的话是通过index返回下标去做对应的删除动作的,而index时间复杂度是O(N),并且,在index找不到值的时候是会报错的,所以需要try一下
所以这里的总体时间复杂度是O(N^2),空间复杂度是O(N)
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
try:
while True:
n = nums.index(val)
nums.pop(n)
except:
return nums
官方题解
这个是通过双指针做的,时间复杂度为O(N),空间复杂度为O(N)。因为题目中有说明


所以这样写也可以算是正确的,返回的left值最终指向的就是满足条件的值
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
for v in nums:
if v != val:
nums[left] = v
left += 1
return left
或者这样写更好理解吧
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
for i, v in enumerate(nums):
if v != val:
nums[left], nums[i] = v, nums[left]
left += 1
return left

浙公网安备 33010602011771号