Leetcode学习之数组)
数组
定义
数组是具有一定顺序关系的若干对象组成的集合,组成数组的对象称为数组元素
列如:
-
向量对应一维数组
A = [ a 0 a 1 . . . a n ] A = \begin{bmatrix} a_{0} & a_{1} & ... & a_{n} \end{bmatrix} A=[a0a1...an] -
矩阵对应二位数组
A m × n = [ a 00 a 01 . . . a 0 n − 1 a 10 a 11 . . . a 1 n − 1 . . . . . . . . . . . . a m − 10 a m − 11 . . . a m − 1 n − 1 ] A_{m\times n}=\begin{bmatrix} a_{00} & a_{01} & ... & a_{0n-1} \\ a_{10} & a_{11} & ... & a_{1n-1}\\ ... & ... & ... & ...\\ a_{m-10} & a_{m-11} & ... & a_{m-1n-1} \end{bmatrix} Am×n=⎣⎢⎢⎡a00a10...am−10a01a11...am−11............a0n−1a1n−1...am−1n−1⎦⎥⎥⎤
数组的储存
n维数组的定义
下标由n个数组成的数组称为n维数组
// 一维数组(线)
int [] a = new int[10];
// 二维数组(面)
int[ , ] = new int[2,3];
// 三维数组(体),类比书 页 行 列
int[ , , ] = new int[2,3,4]
数组存储的特点
- 数组元素在内存中按顺序连续存储
- C、C++、C#按行存储
- 数组名表示该数组的首地址,是常量
常用数组的存储
一维数组a[n]
int [] a = new int[10];
占用字节数为
l
o
c
(
a
[
i
]
)
=
l
o
c
(
a
[
0
]
)
+
i
×
c
loc(a[i])=loc(a[0])+ i\times c
loc(a[i])=loc(a[0])+i×c
二维数组a[m,n]
列如:
int [ , ] a = new int[2,3];
占用字节数为
l
o
c
(
a
[
i
,
j
]
)
=
l
o
c
(
a
[
0
,
0
]
)
+
i
×
n
×
c
+
j
×
c
=
l
o
c
(
a
[
0
,
0
]
)
+
j
×
c
×
(
n
+
1
)
loc(a[i,j])=loc(a[0,0]) + i \times n \times c + j \times c =loc(a[0,0]) + j \times c \times (n+1)
loc(a[i,j])=loc(a[0,0])+i×n×c+j×c=loc(a[0,0])+j×c×(n+1)
三维数组a[m,n,l]
第一维下标变化最慢,第三维(最后一位)下标变化最快
列:
int[ , , ] a = new int[2,3,4]
占用字节数为
l
o
c
(
a
[
i
,
j
,
k
]
)
=
l
o
c
(
a
[
0
,
0
,
0
]
)
+
(
i
×
n
×
l
+
j
×
l
+
k
)
×
c
loc(a[i,j,k]) = loc(a[0,0,0])+(i \times n \times l + j \times l + k) \times c
loc(a[i,j,k])=loc(a[0,0,0])+(i×n×l+j×l+k)×c
练习
两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
输入:nums = [3,2,4], target = 6
输出:[1,2]
输入:nums = [3,3], target = 6
输出:[0,1]
解答
# 法1
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
for j in range(i + 1,len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
# 法2
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
numDict = dict()
for i in range(len(nums)):
if target-nums[i] in numDict:
return numDict[target-nums[i]], i
numDict[nums[i]] = i
return [0]
最接近的三数之和
给你一个长度为 n 的整数数组nums
和 一个目标值 target。请你从nums
中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
解法1:
# 三层循环,不推荐复杂度为O(n^3)
class Solution:
def threeSumClosest(self, nums, target):
error = abs(nums[0] + nums[1] + nums[2] - target)
sum = 0
for i in range(len(nums) - 2):
for j in range(i + 1, len(nums) - 1):
for k in range(j + 1, len(nums)):
sum = nums[i] + nums[j] + nums[k]
if abs(nums[i] + nums[j] + nums[k] - target) < error:
sum = nums[i] + nums[j] + nums[k]
error = abs(sum - target)
return sum
解法二:
# 利用双指针的方法
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
best = nums[0] + nums[1] + nums[2]
def update(sum):
nonlocal best
if abs(sum - target) < abs(best - target):
best = sum
nums.sort()
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
left = i +1
right = len(nums)-1
while left < right:
sum = nums[i] + nums[left] + nums[right]
leftval = nums[left]
rightval = nums[right]
if sum == target:
return sum
update(sum)
if sum > target:
while left < right and rightval == nums[right]:
right-=1
elif sum < target:
while left < right and leftval == nums[left]:
left +=1
return best
解题思路:
本题目因为要计算三个数,如果靠暴力枚举的话时间复杂度会到O(n^3)
,需要降低时间复杂度
首先进行数组排序,时间复杂度 O(nlogn)O(nlogn)
在数组nums
中,进行遍历,每遍历一个值利用其下标i
,形成一个固定值 nums[i]
再使用前指针指向start = i + 1
处,后指针指向end = nums.length - 1
处,也就是结尾处
根据sum = nums[i] + nums[start] + nums[end]
的结果,判断 sum 与目标 target 的距离,如果更近则更新结果ans
同时判断 sum
与 target 的大小关系,因为数组有序,如果 sum > target 则 end–,如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果
整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为O(n^2)O(n 2 )
总时间复杂度:O(nlogn) + O(n^2) = O(n^2)
作业题:删除有序数组中的重复项,移除元素,三数之和
移除元素
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。s
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
a =0
b =0
while a < len(nums):
if nums[a] !=val:
nums[b] = nums[a]
b += 1
a+=1
return b
三数之和
给你一个包含 n
个整数的数组 nums
,判断 nums
中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
# 存储结果列表
res_list = []
# 对nums列表进行排序,无返回值,排序直接改变nums顺序
nums.sort()
for i in range(len(nums)):
# 如果排序后第一个数都大于0,则跳出循环,不可能有为0的三数之和
if nums[i] > 0:
break
# 排序后相邻两数如果相等,则跳出当前循环继续下一次循环,相同的数只需要计算一次
if i > 0 and nums[i] == nums[i-1]:
continue
# 记录i的下一个位置
j = i + 1
# 最后一个元素的位置
k = len(nums) - 1
while j < k:
# 判断三数之和是否为0
if nums[j] + nums[k] == -nums[i]:
# 把结果加入数组中
res_list.append([nums[i], nums[j], nums[k]])
# 判断j相邻元素是否相等,有的话跳过这个
while j < k and nums[j] == nums[j+1]: j += 1
# 判断后面k的相邻元素是否相等,是的话跳过
while j < k and nums[k] == nums[k-1]: k -= 1
# 没有相等则j+1,k-1,缩小范围
j += 1
k -= 1
# 小于-nums[i]的话还能往后取
elif nums[j] + nums[k] < -nums[i]:
j += 1
else:
k -= 1
return res_list
if __name__ == '__main__':
s = Solution()
result_list = s.threeSum([-1, 0, 1, 2, -1, -4])
print(result_list)
删除有序数组中的重复项
给你一个有序数组 nums
,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1)
额外空间的条件下完成。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
left = 0
right = 0
while right < len(nums):
if nums[right] == nums[left]:
right = right + 1
else:
left = left + 1
nums[left] = nums[right]
return left + 1
因为空间复杂度为O(1)
,所以只能在原数组上进行修改。
首先暴力不考虑,我们需要做的是将数组分成左右两部分:
-
左侧是符合题意的结果;
-
右侧是等待选择的数组。
那么接下来我们要做的,就是在右侧选择与左侧最后一个不相等的加入到左侧最后。