使用语言python,解法的时间复杂度和空间复杂度都在平均水平之上,个别有90%左右的,不做具体说明。但是可以提升效率的点会进行说明,用粗体标出。考点和简要的中文也都在文章中标注出来了。
通过leetcode上数组的简单题目我们知道,基本的需要我们掌握的基础的知识有:python中可以对数组进行的操作和通过算法设计得到结果并且让程序更快。下面分别来通过题目和代码说明下我的理解:
这一题就很简单了,只要数组里面有两个一样的,返回True,没有一样的返回False。因此直接遍历,并且把遍历过的元素放在一个集合里面,后面的如果在这个集合里面说明有重复的。
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
s = set()
for num in nums:
if num in s:
return True
else:
s.add(num)
return False
这个其实很简单了…直接放代码啦~
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
if n == 0:
return
if m == 0:
nums1[0:] = nums2
return
nums1[m : len(nums1)] = nums2
nums1.sort()
提升效率: python数组可以一大坨直接赋值,效率比遍历快很多 nums1[m : len(nums1)] = nums2 (感觉这题的数据给的这么的奇怪就是为了这个知识点啊~~)切片整个赋值的用处!
简单的来说,就是需要找到一串和最大的子串,下面先让我们通过下图看看一些可能的情况:
// 里面的MaxNow是现在为止最大的和,MoveOn是可能最大的和,根据图来解释下到底是什么意思。
(1)首先我们按照一般的思维从左到右加和,假设前面全部是负的,那么我们一定不要,因此我们假设全部是正的。这是第一个阶段,这个时候的MaxNow和MoveOn是一样的。
(2)第二个阶段的时候,出现了负数,因此有两个选择,要不继续加和,要不放弃前面所有的,因为是连续的子串。但是如左右两种情况,如果是左边,负数其实很少,那么左边可以稍微抵消一部分最后还是正的,遇到后面的正的时候,我们就知道不应该抛弃前面的。但是同时,也有可能负数很多,前面的不能抵消,因此我们也可能在后面找到最大的子串。
(3)因为这个原因,我们让MoveOn去继续的加和,只要不为小于0的数字就说明不会被抵消而保留,并且让MaxNow始终等于两个数字里面的最大值。
(4)假如MoveOn不幸的为负数了,我们只能抛弃前面的,让MoveOn归零并且继续往前加,再不断的比较MaxNow和MoveOn后来的值,看看需不需要改变MaxNow。
(5)根据以上的思路我们可以写出下面的代码。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
MaxNow, MoveOn = nums[0],nums[0]
for i in range(1,len(nums)):
if MoveOn < 0 :
MoveOn = nums[i]
else:
MoveOn += nums[i]
MaxNow = max(MaxNow,MoveOn)
return MaxNow
这个就是找到数组里面相加之和等于给定的目标值的数字,并且给出两个数字的位置的索引,且根据题目,这样的解只有一个,就很简单了,思路是直接开始遍历,看看后面有没有,有的话先记下来第一个数字的索引,再在数组的后面的部分遍历,找到那个数字,记下索引,返回就可。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
index = []
for i in range(len(nums)):
if target - nums[i] in nums[i+1:]:
index.append(i)
break
for j in range(i+1,len(nums)):
if nums[j] == target - nums[i]:
index.append(j)
return index
直接放代码~比较长度是因为我觉得这样会快一些,不过其实最后就是50%左右的水平 ~
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
len1, len2 = len(nums1),len(nums2)
intersect = []
if len1> len2:
for i in nums2:
if i in nums1:
nums1.remove(i)
intersect.append(i)
else:
for i in nums1:
if i in nums2:
nums2.remove(i)
intersect.append(i)
return intersect
这一题在评论区有个哥们讲的很清楚,比我表达的还清楚,根据他的说法,我本来在两个分支都写了right+=1,后来改出来if循环了,结果快了一些
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) == 1:
return 0
left = 0
right = 1
max_profit = 0
while right <= len(prices)-1:
if prices[right]-prices[left]<0:
left = right
else:
max_profit = max(max_profit,prices[right]-prices[left])
right+=1
return max_profit
这一题感觉只要知道矩阵是一行行写怎么计算就可以
(我最开始的时候import numpy写的居然也可以,但是效率太低了哈哈哈…不过实际中,大家都会用numpy吧?)
class Solution:
def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
row = len(mat)
col = len(mat[0])
if r*c != row*col:
return mat
reshapeMatrix = [[0]*c for i in range(r)]
m,n = 0,0
for i in range(r*c):
reshapeMatrix[m][n] = mat[i//col][i%col]
if n<c-1: n+= 1
else:
m+=1
n = 0
return reshapeMatrix
这一题我最后做到runtime是95.08%,memory是81.87%。但是其实我写了三版,这一版最快。主要是矩阵的赋值的地方不一样。但是原因很奇怪,因为这样手动计算复杂度没区别啊!
我放在下面:
这个只要索引能够对上就很简单啦!这个的runtime做到了95.6%
class Solution:
def generate(self, numRows: int) -> List[List[int]]:
pascal = [[0]*(i+1) for i in range(numRows)]
count = 1
for i in range(numRows*(numRows+1)//2):
index = i - (count-1)*count//2 # positionin the row
if index > count-1:
count+=1
index = i - (count-1)*count//2 # positionin the row
if index == 0 or index == count-1:
pascal[count-1][index] = 1
else:
pascal[count-1][index] = pascal[count-2][index-1]+pascal[count-2][index]
return pascal
这题的思路其实非常的简单,即需要分别判断,行有没有重复,列有没有重复,以及每一个小的sub方阵有没有重复。关键的是怎么写可以效率更高。一开始我是这样写的,使用了列表解析,后面的小方阵的判断是直接写了坐标。
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
for i in board:
extract = [j for j in i if j!='.']
if len(extract)!= len(set(extract)):
return False
for i in range(9):
extract = [j[i] for j in board if j[i]!='.']
if len(extract)!= len(set(extract)):
return False
location = [(0,0),(3,0),(6,0),
(0,3),(3,3),(6,3),
(0,6),(3,6),(6,6)]
loc_box = [(0,0), (0,1), (0,2),
(1,0), (1,1), (1,2),
(2,0), (2,1), (2,2)]
for k in location:
i = k[0]
j = k[1]
extract = [board[m+i][n+j] for m,n in loc_box if board[m+i][n+j]!='.']
if len(extract)!= len(set(extract)):return False
return True
后来看见评论区里面更快的方式是:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
def init_mapper():
return {i: {} for i in range(9)}
mapper_row = init_mapper()
mapper_col = init_mapper()
mapper_box = init_mapper()
pos_box = {(0,0): 0, (0,1): 1, (0,2): 2,
(1,0): 3, (1,1): 4, (1,2): 5,
(2,0): 6, (2,1): 7, (2,2): 8,}
for r, row in enumerate(board):
for c, item in enumerate(row):
if item == ".":
continue
b = pos_box[(r // 3, c // 3)]
if mapper_row[r].get(item, False) or mapper_col[c].get(item, False) or mapper_box[b].get(item, False):
# repeated item
return False
else:
mapper_row[r][item] = True
mapper_col[c][item] = True
mapper_box[b][item] = True
return True
这个里面很巧妙的使用了 enumerate,这样相当于可以把所有的信息利用坐标的方式进行存储
于是可以一次遍历直接得到结果,而不用遍历三次,效率很低。这个字典的使用是很值得学习的。(查找判断很方便)