力扣刷满100题,第二周
3. 最长连续序列
-
描述
- 给定一个未排序的整数数组
nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
- 给定一个未排序的整数数组
-
输入示例
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。 -
算法思想
-
方法一:数组
-
如何取出连续数值的第一个数? 判断 num - 1 是否在数组中 不在
-
如何取出连续数值的最后一个数? 判断num + 1 是否在数组中 不在
-
只需要考虑数字是否在数组中,无需考虑数字的索引
-
在遍历的过程中,每次首先判断当前序列下的第一个数是否存在
-
如果存在,则序列长度初始为 1
- 然后继续遍历数组的 num + 1 是否在数组中
- 在则更新连续的个数,不在则返回
-
不存在则将每次循环得到的连续长度和之前的比较
-
-
-
方法二:并查集
-
并查集的结构,类似于树,不等于二叉树
-
并查集:如果当前值和它的下一位都在集合里,就把它们合并,用较小数作为根节点,并更新根节点的大小。
-
最后,历遍所有的根节点的大小,返回最大的结果。
-
例如 [2, 3, 4, 6] 初始根节点为自己 [2, 3, 4, 6] 大小都为 1 [1, 1, 1, 1],
合并后(使用路径压缩),根节点为 [2, 2, 2, 6] 大小为 [3, 1, 1, 1]
也就是2,3,4的根节点都是2
-
-
方法三:动态规划
- 动态规划一般从 dp[i] 推导出 dp[i+1],而不会更新之前的 dp[i]
- 用一个哈希集合 h 记录每个点所在的连续数组的长度,默认是 0
- 每次读取一个值,先判断是否已经在 h 里,已经在的话,就可以略过,原因是,在 h 里的点随着读取新的值,在不断的被更新,我们只需要处理不在 h 里的新的值
- 更新的过程就是,找到左边点和右边点的长度,合并为当前点的长度,更新结果,再更新目前区间的左右端点的长度
-
-
具体实现
- 方法一:数组
class Solution: def longestConsecutive(self, nums: List[int]) -> int: # 连续数的最大长度 res = 0 nums_set = set(nums) # 遍历取出连续数的第一个和最后一个 for num in nums_set: # 找到连续数的开始数num if (num - 1) not in nums_set: # 初始值已有num一个 seq_num = 1 while (num + 1) in nums_set: seq_num += 1 num += 1 # 更新最大连续长度 res = max(res, seq_num) return res
- 方法二:并查集
class Solution: def longestConsecutive(self, nums: List[int]) -> int: def find(u): # 找出父节点的值 if parent[u] != u: parent[u] = find(parent[u]) return parent[u] def union(u, v): u, v = find(u), find(v) # 合并到同一个父节点 if u != v: # 注意传进去的 u < v,把小值 u 作为根节点 parent[v] = u # 更新父节点下面的值 size[u] += size[v] nums = set(nums) # 初始化并查集 parent = {num : num for num in nums} # 初始化根节点大小 size = {num : 1 for num in nums} res = 0 for num in nums: if num + 1 in parent: union(num, num + 1) # 找出每次num的根节点的值进行比较 # 一定要通过find方法找出根节点的值 res = max(res, size[find(num)]) return res
- 方法三:动态规划
class Solution: def longestConsecutive(self, nums: List[int]) -> int: ## (动态规划) hash = {} res = 0 for num in set(nums): if num not in hash: # 哈希集合记录所在连续区间的长度 # get方法:有值则直接返回,没有返回给定的值 left = hash.get(num - 1, 0) right = hash.get(num + 1, 0) cur = left + right + 1 res = max(res, cur) hash[num] = cur # 当前的值可以是任意值,此步的主要目的是把当前值放入 hash 集合里,以后直接跳过 hash[num - left] = cur # 巧妙之处,用当前的长度取更新左右端点的长度 hash[num + right] = cur return res
4. 移动零
-
描述
- 给定一个数组
nums
,编写一个函数将所有0
移动到数组的末尾,同时保持非零元素的相对顺序。
- 给定一个数组
-
输入示例
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
-
算法思想
- 方法一:
- 统计0的个数,将不是0的元素前移,最后再填充0
- 定义一个i指针,遍历数组,将非0元素和j指针指向的元素交换
- 同时更新j的值,最后j指向非0的最后一个
- 统计0的个数,将不是0的元素前移,最后再填充0
- 方法二:
- 在遍历的过程中,交换0和非0数,相当于两个指针分为快慢指针,
- 快指针指向非0数,慢指针指向0,最后不断循环交换
- 方法一:
-
具体实现
- 方法一:
class Solution: def moveZeroes(self, nums: List[int]) -> None: n = len(nums) j = 0 # 完成非0数的前移,同时更新j的位置 for i in range(n): if nums[i] != 0: nums[j] = nums[i] j += 1 # 填充0 while j < n: nums[j] = 0 j += 1 return nums
- 方法二:
class Solution: def moveZeroes(self, nums: List[int]) -> None: n = len(nums) # i 指向当前 0 的前一位置,j 表示非 0 要交换的数 i = -1 for j in range(n): if nums[j] != 0: i += 1 # 同时交换 nums[i], nums[j] = nums[j], nums[i] return nums
5. 盛最多水的容器
-
描述
- 给定一个长度为
n
的整数数组height
。有n
条垂线,第i
条线的两个端点是(i, 0)
和(i, height[i])
。 - 找出其中的两条线,使得它们与
x
轴共同构成的容器可以容纳最多的水。
- 给定一个长度为
-
输入示例
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 -
算法思想
- 方法一:双指针
- 初始化i、j两个指针,分别指向数组的首和尾
- 遍历数组,比较nums[i] 和 nums[j] 的大小,统计之间的间隔数
- 得出此次的最大盛水容量
- 与之前的盛水容量比较更新最大的
- 方法二:
- 所谓盛水最多,也就是矩形的面积最大
- S = (j - i) * min( nums[i], nums[j] )
- 由于j - i 是肯定在变小的,可以暂时不用考虑,要想S最大,则需要高度差最大
- 也就是将num[i] 和 nums[j] 中的最小值变得更大
- 最后再去更新S
- 所谓盛水最多,也就是矩形的面积最大
- 方法一:双指针
-
具体实现
- 方法一:双指针
class Solution: def maxArea(self, height: List[int]) -> int: n = len(height) i, j, res = 0, n -1, 0 while i < j: # 记录当前的最大值 count = 0 if height[i] <= height[j]: count = j - i res = max(res, count * height[i]) i += 1 elif height[i] > height[j]: count = j - i res = max(res, count * height[j]) j -= 1 return res
- 方法二:贪心思想
class Solution: def maxArea(self, height: List[int]) -> int: n = len(height) i, j, area = 0, n -1, 0 while i < j: # 记录当前的最大值 area = max(area, min(height[i], height[j]) * (j - i)) if height[i] < height[j]: i += 1 else: j -= 1 return area