力扣刷满100题,第二周

力扣刷满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,最后不断循环交换
  • 具体实现

    • 方法一:
    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
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值