数组
189.轮转数组
-
题目:
给你一个数组,将数组中的元素向右轮转 `k` 个位置,其中 `k` 是非负数。
-
解法1:将最后一个元素取出,放在索引为0的位置,循环k次
-
class Solution: def rotate(self, nums: List[int], k: int) -> None: """ Do not return anything, modify nums in-place instead. """ for _ in range(k): num = nums.pop() nums.insert(0,num)
-
解法2:定义一个reverse函数,传进nums,旋转的起始位置,终止位置,先对整个nums进行旋转,此时nums直接成为倒序的,然后对前k元素进行旋转,再对后n-k个元素进行旋转
-
class Solution: def rotate(self, nums: List[int], k: int) -> None: """ Do not return anything, modify nums in-place instead. """ k = k %len(nums) n = len(nums) -1 self.reverse(nums,0,n) self.reverse(nums,0,k-1) self.reverse(nums,k,n) def reverse(self,nums:List[int],left:int,right:int) ->None: while left < right: tmp = nums[left] nums[left] = nums[right] nums[right] = tmp left+=1 right-=1
66.加一
-
题目:
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。你可以假设除了整数 0 之外,这个整数不会以零开头
-
解法1:遍历数组,将其添加到字符串中,然后将字符串转化为整形,对其进行+1操作,然后转化为字符串类型,再将字符串类型转化为列表类型,再将列表里元素类型转化为整形
-
class Solution: def plusOne(self, digits: List[int]) -> List[int]: a = "" for i in digits: a = a + str(i) li = list(str(int(a) + 1)) return [int(x) for x in li]
-
解法2:
这道题把整个数组看成了一个整数,然后个位数 +1。问题的实质是利用数组模拟加法运算。 如果个位数不为 9 的话,直接把个位数 +1 就好。如果个位数为 9 的话,还要考虑进位。 具体步骤: 数组前补 0 位。 将个位数字进行 +1 计算。 遍历数组 如果该位数字 大于等于 10,则向下一位进 1,继续下一位判断进位。 如果该位数字 小于 10,则跳出循环。
-
def plusOne(self, digits: List[int]) -> List[int]: digits = [0] + digits digits[len(digits)-1] += 1 for i in range(len(digits)-1,0,-1): if digits[i] != 10: break else: digits[i] = 0 digits[i-1] += 1 if digits[0] == 0: return digits[1:] else: return digits
724. 寻找数组的中心下标
-
题目:
给你一个整数数组 nums ,请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。 如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。
-
分析:
因为如果有多个中心下标,应该返回最靠左边的一个,如果采用二分法对num[i]进行测试,无法确定返回的是最左边的下标,所以二分法不可取。 先计算出所有元素的值,然后依次对nums[i]进行测试,cur_sum也可以一步一步计算出来,每对num[i]进行测试后,如果不符合条件则将nums[i]加入到cur_sum中
-
class Solution: def pivotIndex(self, nums: List[int]) -> int: n = len(nums) sum_nums = sum([x for x in nums]) cur_sum = 0 for i in range(n): if cur_sum * 2 + nums[i] == sum_nums: return i cur_sum +=nums[i] return -1
485.最大连续1的个数
-
题目:
给定一个二进制数组 nums , 计算其中最大连续 1 的个数。
-
解法1:
定义temp=0,来记录当前最长1的个数,依次遍历数组,如果为1,则i+=1,j+=1,如果为0,将j与temp比较大小,如果比temp大,则赋值给temp,i+=1,j=0,最后循环结束后,再次比较j与temp的大小,最后返回tmep
-
class Solution: def findMaxConsecutiveOnes(self, nums: List[int]) -> int: temp = 0 n = len(nums) i = 0 j = 0 while i < n: if nums[i] == 1: i +=1 j +=1 elif nums[i] == 0: if temp < j: temp = j j =0 i +=1 if temp < j: temp = j return temp
238.除自身以外数组的乘积
-
题目:
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请不要使用除法,且在 O(n) 时间复杂度内完成此题。
-
分析:
因为不能使用除法,且在O(n)时间复杂度内完成,除自身以外数组的乘积等于该元素左边所有元素*右边所有元素,先左遍历一遍,存储左边所有元素的乘积,且将left*当前元素的值,再从右边遍历一遍
-
class Solution: def productExceptSelf(self, nums: List[int]) -> List[int]: n = len(nums) result = [1] * n left = 1 for i in range(n): result[i] *= left left *= nums[i] right = 1 for i in range(n - 1, -1, -1): result[i] *= right right *= nums[i] return result
二维数组
498.对角线的遍历
-
题目:
给你一个大小为 m x n 的矩阵 mat ,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。
-
分析:
这道题的关键是「找规律」和「考虑边界问题」。 找规律: 当行号 + 列号为偶数时,遍历方向为从左下到右上。可以记为右上方向(-1, +1),即行号 -1,列号 +1。 当行号 + 列号为奇数时,遍历方向为从右上到左下。可以记为左下方向(+1, -1),即行号 +1,列号 -1。 边界情况: 向右上方向移动时: 如果在最后一列,则向下方移动,即 x += 1。 如果在第一行,则向右方移动,即 y += 1。 其余情况想右上方向移动,即 x -= 1、y += 1。 向左下方向移动时: 如果在最后一行,则向右方移动,即 y += 1。 如果在第一列,则向下方移动,即 x += 1。 其余情况向左下方向移动,即 x += 1、y -= 1。
-
class Solution: def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]: m = len(mat) n = len(mat[0]) count = m * n result = [] x, y = 0, 0 for i in range(count): result.append(mat[x][y]) if (x + y) % 2 == 0: if y == n - 1: x += 1 elif x == 0: y += 1 else: x -= 1 y += 1 else: if x == m - 1: y += 1 elif y == 0: x += 1 else: x += 1 y -= 1 return result
48.旋转图像
-
题目:
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
-
分析:
先进行列翻转,然后以主对角线翻转,就可以将图像翻转90°
-
class Solution: def rotate(self, matrix: List[List[int]]) -> None: """ Do not return anything, modify matrix in-place instead. """ n = len(matrix) for i in range(n // 2): matrix[i], matrix[n - i - 1] = matrix[n - i - 1], matrix[i] for i in range(n): for j in range(i): matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j]
118.杨辉三角
-
题目:
给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。
-
分析:
杨辉三角result[i][j] = result[i-1][j] + result[i-1][j-1] 因为要考虑越界情况,所以先将杨辉三角的形状用1填充出来,然后再从2开始,就不会出现越界的情况
-
class Solution: def generate(self, numRows: int) -> List[List[int]]: result = [[1] * i for i in range(1, numRows + 1)] if numRows < 3: return result for i in range(numRows-2): for j in range(i+1): result[2+i][1+j] = result[1+i][j+1] + result[1+i][j] return result
119.杨辉三角II
-
题目:
给定一个非负整数 numRows,生成「杨辉三角」的第 numRows 行。 在「杨辉三角」中,每个数是它左上方和右上方的数的和。
-
分析:
先将杨辉三角的前n行计算出,然后取其第numRows行
-
class Solution: def getRow(self, rowIndex: int) -> List[int]: rowIndex_new = rowIndex +1 result = [[1] * i for i in range(1, rowIndex_new + 1)] if rowIndex_new < 3: return result[rowIndex_new-1] for i in range(rowIndex_new - 2): for j in range(i + 1): result[2 + i][1 + j] = result[1 + i][j + 1] + result[1 + i][j] return result[rowIndex_new-1]
73.矩阵置零
-
题目:
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
-
分析:
先将矩阵中的0全部取出,放入到一个列表中,再从列表中弹出0的位置,将0所在的行和列全部置为0
-
class Solution: def setZeroes(self, matrix: List[List[int]]) -> None: """ Do not return anything, modify matrix in-place instead. """ m = len(matrix) n = len(matrix[0]) p = [] for i in range(m): for j in range(n): if matrix[i][j] == 0: p.append((i, j)) while p: i, j = p.pop() for q in range(n): matrix[i][q] = 0 for q in range(m): matrix[q][j] = 0
54.螺旋矩阵
-
题目:
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
-
分析:
按照题意进行模拟。可以实现定义一下上、下、左、右的边界,然后按照逆时针的顺序从边界上依次访问元素。 当访问完当前边界之后,要更新一下边界位置,缩小范围,方便下一轮进行访问。
-
class Solution: def spiralOrder(self, matrix: List[List[int]]) -> List[int]: m = len(matrix) n = len(matrix[0]) result = [] up, down, left, right = 0, m - 1, 0, n - 1 while True: for i in range(left, right + 1): result.append(matrix[up][i]) up += 1 if up > down: break for i in range(up, down + 1): result.append(matrix[i][right]) right -= 1 if right < left: break for i in range(right, left - 1, -1): result.append(matrix[down][i]) down -= 1 if down < up: break for i in range(down, up - 1, -1): result.append(matrix[i][left]) left += 1 if left > right: break return result
289.生命游戏
-
题目:
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡; 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活; 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡; 如果死细胞周围正好有三个活细胞,则该位置死细胞复活; 下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
-
分析:
先将原矩阵扩展一圈0来应对边界情况,其中细胞的出生和死亡是同时发生的,所以得一直使用原数组,不能使用每次操作后的数组,就直接将扩展后的数组拿来使用 注:使用深拷贝,不然如果直接简单赋值,在扩展的时候,原来的矩阵也会随之扩展, 再定义一个方法,用来求周围8个数的和,然后进行定律判断,如果需要改变,则直接操作原数组。
-
class Solution: def gameOfLife(self, board: List[List[int]]) -> None: """ Do not return anything, modify board in-place instead. """ m = len(board) n = len(board[0]) board_n = copy.deepcopy(board) board_n.insert(0, [0] * (n + 2)) board_n.append([0] * (n + 2)) for i in range(1, m + 1): board_n[i].insert(0, 0) board_n[i].append(0) for i in range(1,m+1): for j in range(1,n+1): self.sum_8(i,j,board_n[i][j],board,board_n) def sum_8(self,i, j, num, board,board_n): sum_num = sum(board_n[i - 1][j - 1:j + 2]) + sum(board_n[i][j - 1:j + 2]) + sum(board_n[i + 1][j - 1:j + 2])-num if num: if sum_num < 2 or sum_num > 3: board[i-1][j-1] = 0 else: if sum_num == 3: board[i-1][j-1] = 1
排序
笔记
希尔排序
-
希尔排序:将整个序列切按照一定的间隔取值划分为若干个子序列,每个子序列分别进行插入排序。然后逐渐缩小间隔进行下一轮划分子序列和插入排序。直至最后一轮排序间隔为
1
,对整个序列进行插入排序。 -
算法步骤:
-
首先确定一个元素间隔数
gap
,然后将参加排序的序列按此间隔数从第1
个元素开始一次分成若干个子序列,即分别将所有位置相隔为gap
的元素视为一个子序列,在各个子序列中采用某种排序方法进行插入排序。 -
然后减少间隔数,并重新将整个序列按新的间隔数分成若干个子序列,再分别对各个子序列进行排序,如此下去,直到间隔数
gap = 1
。
-
-
代码
-
class shell: def shell(self, arr): size = len(arr) gap = size // 2 while gap > 0: # 当gap 为1时停止循环,当为1时,就是整个数组,就不用进行排序了 for i in range(gap, size): temp = arr[i] j = i while j >= gap and arr[j - gap] > temp: #j>=gap,如果j<gap,j-gap就会为负值, 判断当前值进行插入排序j的位置为nums[i]要插入的位置 arr[j] = arr[j - gap] j -= gap arr[j] = temp gap = gap // 2 return arr def sheel_sort(self, arr): return self.shell(arr)
-
归并排序
-
归并排序:采用经典的分治策略,先递归地将当前序列平均分成两半。然后将有序序列两两合并,最终合并成一个有序序列。
-
算法步骤:
-
先写出merge函数:传入两个数组,一直取其中最小的数,拼接到新的数组中,则新的数组就是有序数组
-
再写出merge_sort函数:递归调用merge_sort,直到size小于2返回数组,
-
-
代码:
class MergeSort: def merge(self, left_arr, right_arr): arr = [] while left_arr and right_arr: if left_arr[0] < right_arr[0]: arr.append(left_arr.pop(0)) else: arr.append(right_arr.pop(0)) while left_arr: arr.append(left_arr.pop(0)) while right_arr: arr.append(right_arr.pop(0)) return arr def merge_sort(self, arr): size = len(arr) if size < 2: return arr mid = size // 2 left_arr, right_arr = arr[0:mid], arr[mid:size] return self.merge(self.merge_sort(left_arr), self.merge_sort(right_arr))
计数排序
-
计数排序:使用一个额外的数组
counts
,其中第i
个元素counts[i]
是待排序数组arr
中值等于i
的元素个数。然后根据数组counts
来将arr
中的元素排到正确的位置。 -
算法步骤:
-
找出待排序数组中最大值元素和最小值元素。
-
统计数组中每个值为
i
的元素出现的次数,存入数组的第i
项。 -
对所有的计数累加(从
counts
中的第一个元素开始,每一项和前一项累加)。#处理稳定性 -
反向填充目标数组:将每个元素
i
放在新数组的第counts[i]
项,每放一个元素就要将counts[i] -= 1
。#处理稳定性
-
-
代码:
class Solution: def sortArray(self, nums): # 计数排序 min_nums = min(nums) max_nums = max(nums) count = max_nums - min_nums + 1 # 找到最大值元素与最小值元素,并计算出之间的个数 count_nums = [0] * count for i in nums: count_nums[i - min_nums] += 1 # 统计每个元素的个数,并添加到count_nums的对应位置 sum = 0 for i in range(count): # 对所有计数进行累加 sum += count_nums[i] count_nums[i] = sum result = [0] * len(nums) for i in range(len(nums) - 1, -1, -1): # 反向遍历填充目标数组,并将 count_nums[i] -1 result[count_nums[nums[i] - min_nums]-1] = nums[i] count_nums[nums[i] - min_nums] -=1 return result
快速排序
-
快速排序:通过一趟排序将无序序列分为独立的两个序列,第一个序列的值均比第二个序列的值小。然后递归地排列两个子序列,以达到整个序列有序。
-
算法步骤:
-
从数组中找到一个基准数,最好使用随机数,均摊之后的平均时间复杂度为O(nlgn),主要是为了减少输入原因导致遇到最坏情况O(n^2)
-
让后让数字中,比基准数大的元素移动到基准数的右边,然后比他小的元素移动到基准数的左边,从而把数组才分成两部分
-
再对左右两个部分分别重复第二步,直到各个部分只有一个数,则排序结束
-
-
代码:
import random class quick_sort: """ partition:对传过来的数组、基准数,使数组中比基准数大的元素都在右边,比基准数小的都在左边 """ def randomPartition(self, arr: [int], low: int, high: int): i = random.randint(low, high) arr[i], arr[high] = arr[high], arr[i] return self.partition(arr, low, high) def partition(self, arr, low, high): i = low - 1 pivot = arr[high] for j in range(low, high): if arr[j] < pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i + 1], arr[high] = arr[high], arr[i + 1] return i + 1 def quick_sort(self, nums, low, high): if low < high: pi = self.randomPartition(nums, low, high) self.quick_sort(nums, low, pi - 1) self.quick_sort(nums, pi + 1, high) return nums def sortArray(self, nums): return self.quick_sort(nums, 0, len(nums) - 1) if __name__ == '__main__': a = [4, 5, 9, 2, 6, 4, 8, 2, 16, 59, 12, 78, 26, 10] sort = quick_sort() sort.sortArray(a) print(a)
剑指 Offer 45. 把数组排成最小的数
-
题目:
输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
-
分析:
需要改变排序规则,不能按数值大小进行比较,将规则改为:x和y拼接与y与x拼接后进行比较大小,如果前者小于后者,则x在y前面
-
class Solution: def minNumber(self, nums: List[int]) -> str: for i in range(len(nums)-1): for j in range(len(nums)-i-1): nums[j],nums[j+1] = self.com(nums[j],nums[j+1]) result = "" for i in nums: result +=str(i) return result def com(self,x,y): a = int(str(x)+str(y)) b = int(str(y)+str(x)) if a <= b: return x,y else: return y,x
283.移动零
-
题目:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。
-
分析:
定义一个双指针,i用来遍历数组,j用来表示非零的位置,当i遍历玩数组后,从j到末尾,将数组中元素置为0
-
class Solution: def moveZeroes(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ j = 0 for i in range(len(nums)): if nums[i]: nums[j] = nums[i] j += 1 for t in range(j,len(nums)): nums[t] = 0
215.数组中第k个最大的元素
-
题目:
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
-
分析1:
将数组进行排序,排到第k个最大元素时进行返回 # 关键在于时间复杂度 使用选择排序
-
# 选择排序 class Solution: def findKthLargest(self,nums,k): len_nums = len(nums) for i in range(k): temp =i for j in range(i+1,len_nums): if nums[j] > nums[temp]: temp = j nums[temp],nums[i]=nums[i],nums[temp] return nums[k-1]
75.颜色分类
-
题目:
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库的sort函数的情况下解决这个问题。
-
分析1:
直接对原数组进行排序即可
-
class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ len_nums = len(nums) for i in range(len_nums): min = i for j in range(i+1,len_nums): if nums[j] < nums[min]: min = j nums[min],nums[i] = nums[i],nums[min]
-
分析2:
使用双指针,left和right,left指向当前处理好的红色元素的尾部,right指向当前处理好蓝色的头部,在用一个下标index遍历数组,如果遇到nums[index]==0,则交换nums[index]和nums[left]同时left右移,nums[index]==2,则交换nums[index]和nums[right]同时right右移, 注意:移动的时候需要判断index和left的位置,index需要大于等于left
-
class Solution: def sortColors(self, nums: List[int]) -> None: left = 0 right = len(nums) - 1 index = 0 while index <= right: if index < left: index += 1 elif nums[index] == 0: nums[index], nums[left] = nums[left], nums[index] left += 1 elif nums[index] == 2: nums[index], nums[right] = nums[right], nums[index] right -= 1 else: index += 1
912.排序数组
-
题目:
给你一个整数数组 nums,请你将该数组升序排列。
-
分析1:
直接对数组nums进行排序即可 # 希尔排序
-
class Solution: def sortArray(self, nums: List[int]) -> List[int]: # shell sort size = len(nums) gap = size//2 while gap > 0: for i in range(gap,size): j = i temp = nums[i] while j >= gap and nums[j - gap] > temp: nums[j] = nums[j -gap] j -=gap nums[j] = temp gap //=2 return nums
-
分析2:
直接对数组nums进行排序即可 # 归并排序
-
class Solution: def sortArray(self, nums: List[int]) -> List[int]: #mergesort return self.mergesort(nums) def mergesort(self,arr): size = len(arr) if size < 2: return arr mid = size //2 left_arr, right_arr = arr[0:mid],arr[mid:size] return self.merge(self.mergesort(left_arr),self.mergesort(right_arr)) def merge(self,left_arr,right_arr): arr = [] while left_arr and right_arr: if left_arr[0]< right_arr[0]: arr.append(left_arr.pop(0)) else: arr.append(right_arr.pop(0)) while left_arr: arr.append(left_arr.pop(0)) while right_arr: arr.append(right_arr.pop(0)) return arr
506.相对名次
-
题目:
给你一个长度为 n 的整数数组 score ,其中 score[i] 是第 i 位运动员在比赛中的得分。所有得分都 互不相同 。 运动员将根据得分 决定名次 ,其中名次第 1 的运动员得分最高,名次第 2 的运动员得分第 2 高,依此类推。运动员的名次决定了他们的获奖情况: 名次第 1 的运动员获金牌 "Gold Medal" 。 名次第 2 的运动员获银牌 "Silver Medal" 。 名次第 3 的运动员获铜牌 "Bronze Medal" 。 从名次第 4 到第 n 的运动员,只能获得他们的名次编号(即,名次第 x 的运动员获得编号 "x")。 使用长度为 n 的数组 answer 返回获奖,其中 answer[i] 是第 i 位运动员的获奖情况。
-
分析:
先将score deepcopy一次命名为score_new,直接对score进行排序,然后遍历score_new,在score查找得分所对应的索引,然后如果index为0或1或2,返回字符串,其他情况返回str(index+1)
-
class Solution: def findRelativeRanks(self, score: List[int]) -> List[str]: len_score = len(score) score_new = copy.deepcopy(score) gap = len_score//2 while gap > 0: for i in range(gap,len_score): j = i temp = score[i] while j >=gap and score[j-gap]<temp: score[j] = score[j-gap] j -=gap score[j] = temp gap //=2 answer = [] for i in score_new: a = score.index(i) if a ==0: answer.append("Gold Medal") elif a == 1: answer.append("Silver Medal") elif a == 2: answer.append("Bronze Medal") else: answer.append(str(a+1)) return answer
88.合并两个有序数组
-
题目:
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
-
分析:
nums1前m个数表示的是一个非递减顺排列,后n个位置是0,如果选取最小值进行插入,后面的值都需要移动,但选取最大值就不需要移动,即可以选去两个数组中最大值插入到nums1中的最后位置,
-
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. """ tail = m+n-1 index1, index2 = m-1,n-1 while index1>=0 and index2 >= 0: if nums1[index1] > nums2[index2]: nums1[tail] = nums1[index1] index1 -=1 tail -= 1 else: nums1[tail] = nums2[index2] index2 -=1 tail -=1 if index1 == -1: while index2>=0: nums1[tail] = nums2[index2] index2 -=1 tail -=1
剑指offer51.数组中的逆序对
-
题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
-
分析:
可以使用归并排序,先将数组进行分至只有每组只有一个元素 当在并时,有两个有序数组,[1,5,9,11,25]和[2,4,6,8,14], 当在归并时,i,j,分别指向两个数组的头部,mid指向i的尾部,选取第一个数组时,就算取贡献,取第二个时,其贡献为0 例:先取1,j - mid -1 =0,所以它的贡献是0,选取2时,贡献为0,当是4时,j-mid-1 = 1,所以贡献为1,依次类推,将贡献累加起来,进行返回 如果不许要明确计算该元素的贡献,逆序对还有另外一种计算方法,选取第一个数组时,不需要计算逆序对,当选取后一个数组时,逆序对为第一个数组剩余的元素 后一种方法的优点:当数组有剩余进行归并时,不需要再计算了。前一种方法在第一个数组有剩余时,仍然需要计算逆序对
-
# 因为不需要明确每个元素的逆序对即可以使用方法一 class Solution: def reversePairs(self, nums: List[int]) -> int: temp = [0] * len(nums) return self.merge(nums,temp,0,len(nums)-1) def merge(self,nums,temp,l,r): if l >= r: return 0 mid = (l + r) //2 count = self.merge(nums,temp,l,mid) + self.merge(nums,temp,mid+1,r) # 将数组进行分组 i,j,pos = l,mid+1,l while i <= mid and j<=r: # 进行并 if nums[i] <= nums[j]: temp[pos] = nums[i] pos +=1 i +=1 else: temp[pos] = nums[j] pos +=1 j +=1 count += (mid -i+1) # 累计贡献值 if i <= mid: for q in range(i,mid+1): temp[pos] = nums[q] pos +=1 if j <= r: for q in range(j,r+1): temp[pos] = nums[q] pos +=1 nums[l:r+1] = temp[l:r+1] return count
315.计算右侧小于当前元素的个数
-
题目:
给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
-
分析1:
归并排序:基本思想同上,只是将贡献值不是累加到一个值上,而是加到贡献值对应的元素上,
-
class Solution: def countSmaller(self, nums: List[int]) -> List[int]: # 归并排序 n = len(nums) if n < 2: return [0] nums_new = [i for i in range(n)] temp = [0] * n # 用来记录index temp_arr = [0] * n # 用来记录nums count = [0] * n # 用来计算贡献值 self.merge(nums_new, nums, temp, 0, n - 1, count, temp_arr) return count def merge(self, nums_new, nums, temp, l, r, count, temp_arr): if l >= r: return 0 mid = (l + r) // 2 self.merge(nums_new, nums, temp, l, mid, count, temp_arr) # 将数组进行分组 self.merge(nums_new, nums, temp, mid + 1, r, count, temp_arr) # 将数组进行分组 i, j, pos = l, mid + 1, l while i <= mid and j <= r: # 开始归并 if nums[i] <= nums[j]: temp[pos] = nums_new[i] # 将index加入到数组中 temp_arr[pos] = nums[i] # 将nums将入到数组中 count[nums_new[i]] += (j - mid - 1) # 将贡献值加到对应元素上 pos += 1 i += 1 else: temp[pos] = nums_new[j] temp_arr[pos] = nums[j] pos += 1 j += 1 if i <= mid: while i <= mid: temp[pos] = nums_new[i] temp_arr[pos] = nums[i] count[nums_new[i]] += (j - mid - 1) pos += 1 i += 1 if j <= r: while j <= r: temp[pos] = nums_new[j] temp_arr[pos] = nums[j] pos += 1 j += 1 nums_new[l:r + 1] = temp[l:r + 1] # 更新nums_new中 nums[l:r + 1] = temp_arr[l:r + 1] # 更新nums
-
分析2:
使用有序数组,维护一个有序数组sl,然后反向遍历,nums的元素,向sl中添加元素,添加前基于二分查找判断出sl中比当前元素小的元素个数,计入答案
-
代码
class Solution: def countSmaller(self, nums: List[int]) -> List[int]: # 有序数组 def binset(arr,x,low,heigh): # 二分查找判断出比x小的元素的个数 left,right = low,heigh while left < right: mid = (left + right) //2 if arr[mid] < x: left = mid + 1 else: right = mid return left n = len(nums) res = [0]*n sl = [] for i in range(n-1,-1,-1): pos = binset(sl,nums[i],0,len(sl)) res[i] = pos sl.insert(pos,nums[i]) # 因为pos是基于sl的,所以pos不会超出列表范围 return res
912.排序数组
-
题目:
给你一个整数数组 nums,请你将该数组升序排列。
-
分析:
使用计数排序,即非比较排序
-
class Solution: def sortArray(self, nums: List[int]) -> List[int]: # 计数排序 有稳定性 min_nums = min(nums) max_nums = max(nums) count = max_nums - min_nums + 1 count_nums = [0] * count for i in nums: count_nums[i - min_nums] += 1 sum = 0 for i in range(count): sum += count_nums[i] count_nums[i] = sum result = [0] * len(nums) for i in range(len(nums) - 1, -1, -1): result[count_nums[nums[i] - min_nums]-1] = nums[i] count_nums[nums[i] - min_nums] -=1 return result
1122.数组的相对排序
-
题目:
给你两个数组,arr1 和 arr2,arr2 中的元素各不相同,arr2 中的每个元素都出现在 arr1 中。 对 arr1 中的元素进行排序,使 arr1 中项的相对顺序和 arr2 中的相对顺序相同。未在 arr2 中出现过的元素需要按照升序放在 arr1 的末尾。
-
分析:
使用计数排序。将arr1进行统计,并且计入count中,然后按照arr2的顺序将arr1取出至结果result中,取出的同时将对应的count_nums置为0,再遍历一遍count,如果有非零元素,直接加入到尾部
-
class Solution: def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: min_nums = min(arr1) max_nums = max(arr1) count = max_nums -min_nums +1 count_nums= [0] * count for i in arr1: count_nums[i-min_nums] +=1 result = [] for i in arr2: for _ in range(count_nums[i-min_nums]): result.append(i) count_nums[i-min_nums] =0 for i in range(len(count_nums)): if count_nums[i] != 0: for _ in range(count_nums[i]): result.append(i + min_nums) return result
169. 多数元素
-
题目:
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
-
分析:
使用快速排序,然后返回[len(nums)//2]就可以返回多数元素
-
class Solution: def majorityElement(self, nums: List[int]) -> int: # 使用快速排序 length = len(nums) self.quick_sort(nums,0,length-1) return nums[length//2] def partition(self,nums,left,right): i = left -1 pivot = nums[right] for j in range(left,right): if nums[j] < pivot: i += 1 nums[i],nums[j] = nums[j], nums[i] nums[i+1], nums[right] = nums[right],nums[i+1] return i+1 def quick_sort(self,nums,left,right): if left < right: p = self.partition(nums,left,right) self.quick_sort(nums,left,p-1) self.quick_sort(nums,p+1,right) return nums
-
分析2:
使用哈希表,用数组中元素值作为键,用出现的次数作为值,再次遍历哈希表,取出值最大的键返回
-
class Solution: def majorityElement(self, nums: List[int]) -> int: count = {} for i in nums: if i not in count: count[i] = 1 else: count[i] +=1 max_val,max_count = 0,0 for i,j in count.items(): if j>max_count: max_val = i max_count = j return max_val