文章目录
1、把数组中的0移动到数组尾部
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
begin = 0
for i, num in enumerate(nums):
if num:
nums[i], nums[begin] = nums[begin], nums[i]
begin += 1
时间复杂度:O(n)
空间复杂度:O(1)
2、改变矩阵分维度
class Solution:
def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
row = len(mat)
col = len(mat[0])
if row * col != r * c:
return mat
res = [[0] * c for i in range(r)]
for i in range(row * col):
res[i // c][i % c] = mat[i // col][i % col]
return res
3、数组中连续1的最大个数
485. 最大连续 1 的个数(Easy)
方法一:
将数组尾部插入一个0,每碰见一个0,就统计两个0之间有多少个1
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
pre_zero_index = -1
res = 0
nums.append(0)
for i, num in enumerate(nums):
if not num:
cur_zero_index = i
res = max(res, cur_zero_index - pre_zero_index - 1)
pre_zero_index = cur_zero_index
return res
方法二:
遍历数组,记录最大的连续 1 的个数和当前的连续 1 的个数。如果当前元素是 1,则将当前的连续 1 的个数加 1,否则,更新最大的连续 1 的个数,并将当前的连续 1 的个数清零。
将数组尾部放置一个 0,这样最后一个1也能统计到
class Solution:
def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
count = 0
res = 0
nums.append(0)
for i, num in enumerate(nums):
if num:
count += 1
else:
res = max(res, count)
count = 0
return res
4、有序矩阵中查找目标数
矩阵具有特殊性:每行从左到右的升序排列;每列从上到下升序排列。
从矩阵的右上角开始搜索,若目标值小于矩阵元素,向左运动,若目标值大于矩阵元素,向下运动。
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
row = len(matrix)
col = len(matrix[0])
i, j = 0, col -1
while 0 <= i < row and 0 <= j < col:
if matrix[i][j] == target:
return True
elif matrix[i][j] < target:
i += 1
else:
j -= 1
return False
5、有序矩阵中第k小的数
方法:二分查找
区间头尾分别为矩阵最小值和最大值,每次区间统计矩阵中小于等于 mid 的数有多少个。循环求解是为了找到使得矩阵中元素 matrix[i][j]<=mid 的 mid 的最小值,也就是第 k 小的数。
每次统计矩阵中小于等于 mid 的数有多少个,从右上角向左下角移动。
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
left, right = matrix[0][0], matrix[-1][-1]
n = len(matrix)
def check(num):
count = 0
i, j = 0, n - 1
while i < n and j >= 0:
if num >= matrix[i][j]:
count += (j + 1)
i += 1
else:
j -= 1
return count
while left < right:
mid = left + (right - left) // 2
cnt = check(mid)
if cnt >= k:
right = mid
else:
left = mid + 1
return left
6、1-n的数中一个数被另一个替换,找重复和丢失的数
方法一:排序
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
nums.sort()
res = [0, 0]
n = len(nums)
for i in range(1, n):
if nums[i] == nums[i-1]:
res[0] = nums[i]
res[1] = int((1+n)*n/2 - (sum(nums)-res[0]))
return res
方法二:统计每个元素出现的次数
用数组统计每个元素出现的次数,数组下标为对应的数值,数组中元素为每个元素出现的次数。
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
nums_dict = collections.Counter(nums)
res = [0, 0]
n = len(nums)
for i in range(1, n+1):
if i not in nums_dict:
res[1] = i
elif nums_dict[i] >= 2:
res[0] = i
return res
方法三:元素置反
nums 数组中的所有元素都是1到n的正数。对于 nums 中的每个元素 n,找数组中的第 n 个元素,如果为正数,乘以-1。 如果其中一个元素 num 出现两次,当第二次遇到这个数字时,将发现元素 nums[ abs(num)-1] 为负数。,此时 num 就是重复的元素。
完成反转后,如果其中一个数字 num 缺失,则第 num 个元素将为正数,即找到了缺失的数字。
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
res = []
for _, num in enumerate(nums):
if nums[abs(num)-1] > 0:
nums[abs(num) - 1] *= -1
else:
res.append(abs(num))
for i, num in enumerate(nums):
if num > 0:
res.append(i + 1)
return res
方法四:
python的set可以直接去重
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
n = len(nums)
res = [0, 0]
res[0] = sum(nums) - sum(set(nums))
res[1] = (1+n)*n//2 - sum(set(nums))
return res
7、寻找数组中丢失的数
448. 找到所有数组中消失的数字(Easy)
方法:元素置反
1-n 的每个元素,如果出现,就将对应位置上的元素置反,最后没有置反的位置就是没有出现的元素。
时间复杂度:O(n), 空间复杂度:O(1)。
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
res = []
for i, num in enumerate(nums):
if nums[abs(num)-1] > 0:
nums[abs(num)-1] *= -1
for i, num in enumerate(nums):
if num > 0:
res.append(i+1)
return res
8、寻找数组中重复的数
方法:元素置反
1-n 的每个元素,如果出现,就将对应位置上的元素置反。如果发现下表为 i 的元素已经置反,则元素i+1为重复元素。
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
res = []
for num in nums:
if nums[abs(num)-1] > 0:
nums[abs(num)-1] *= -1
else:
res.append(abs(num))
return res
9、寻找数组中重复的数(不修改数组)
方法一:Floyd判圈算法
元素有重复,可以看成数组中存在环,寻找环的入口结点。用快慢指针 slow,fast,slow 一次走一步,fast 一次走两步,二者相遇时,一定在环中。相遇之后让 fast 指向数组开始位置,slow,fast 一次走一步,则在环入口相遇。
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
slow = nums[0]
fast = nums[nums[0]]
while slow != fast:
slow = nums[slow]
fast = nums[nums[fast]]
fast = 0
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
链表找环的入口(证明)
链表的非环部分长度为L,环的长度为C,相遇时slow指针走了t步。则有如下表达式。
(t-L) mod C = (2t-L) mod C
(t-L) - mC = (2t-L) - nC
t = (n - m)*C
只要n,m为正整数,一定有解。此时将fast放回链表头部,slow原地不动,再走t1步,slow走到环的入口,则有
(t+t1-L) mod C = 0
解得t1=L。即两指针相遇处为环的入口。
10、构造相邻数有 k 种差值的数组
数组中有 n 个数 [1,n],用这些数构造数组,使得相邻元素差有 k 个。
问题分析:
让前 k+1 个数的差值分别为 k,k-1,k-1,……1。
前k+1个数为:1,k+1,2,k,3,k-1,4,k-2,5,……
剩下的数从 k+2 依次赋值,因为从 k+2 开始的数还没有使用,而且二者之差也被前面的差值包含了。
class Solution:
def constructArray(self, n: int, k: int) -> List[int]:
res = [1] * n
diff = k
for i in range(1, k+1):
res[i] = res[i-1] + diff if i%2 else res[i-1] - diff
diff -= 1
for i in range(k+1, n):
res[i] = i + 1
return res
11、数组的度
方法:哈希表
使用哈希表统计每一个数出现的次数,第一次,最后一次出现的位置。
每一个数映射到一个长度为 3 的数组,数组中的三个元素分别代表这个数出现的次数、第一次出现的位置和最后一次出现的位置。
class Solution:
def findShortestSubArray(self, nums: List[int]) -> int:
mp = dict()
for i, num in enumerate(nums):
if num in mp:
mp[num][0] += 1
mp[num][2] = i
else:
mp[num] = [1, i, i]
res = 0
max_count = 0
for count, begin, end in mp.values():
if count > max_count:
res = end - begin + 1
max_count = count
elif count == max_count:
res = min(res, end-begin+1)
return res
12、对角元素相等的矩阵
遍历该矩阵,将每一个元素和它左上角的元素相比对即可。
class Solution:
def isToeplitzMatrix(self, matrix: List[List[int]]) -> bool:
row = len(matrix)
col = len(matrix[0])
for i in range(1, row):
for j in range(1, col):
if matrix[i][j] != matrix[i-1][j-1]:
return False
return True
13、数组嵌套
方法一:伴随数组
对于每一个元素,搜索形成环,环中有多少元素。对于一个环来说,从任何一个节点访问,这个环中的元素都没必要再次访问了,可以用伴随数组表示每个元素是否访问。
class Solution:
def arrayNesting(self, nums: List[int]) -> int:
visited = [0] * len(nums)
res = 0
for i, num in enumerate(nums):
if visited[i] == 0:
value = num
cur_len = 0
while not visited[value]:
visited[value] = 1
value = nums[value]
cur_len += 1
res = max(cur_len, res)
return res
时间复杂度:O(n)
空间复杂度:O(n)
方法二:取消伴随数组,直接修改原数组,将访问过的元素置为-1
class Solution:
def arrayNesting(self, nums: List[int]) -> int:
res = 0
for i, num in enumerate(nums):
if nums[i] != -1:
start = num
cur_len = 1
nums[i] = -1
while nums[start] != -1:
cur_len += 1
tmp = nums[start]
nums[start] = -1
start = tmp
res = max(cur_len, res)
return res
时间复杂度:O(n)
空间复杂度:O(1)
14、分隔数组
解题思路:
对于前 i 个元素,如果前 i 个元素的最大值等于 i-1,说明前 i 个元素可以分离出来单独排序,也就是说前 i 个元素为{0,1,……,i-1 },或者其他顺序排列的集合。
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
cur = 0
for i, num in enumerate(nums):
if num:
nums[cur] = num
cur += 1
for i in range(cur, len(nums)):
nums[i] = 0
时间复杂度:O(n)
空间复杂度:O(1)