leetcode
leetcode 551 ~ 600
最长数对链

最长数对链

难度:

标签:

题目描述

代码结果

运行时间: 54 ms, 内存: 16.5 MB


/*
 * Solution in Java using Streams
 * 
 * 思路:
 * 1. 使用 Java Stream 进行排序。
 * 2. 用 reduce 方法计算最长数对链。
 */
 
import java.util.Arrays;
 
public class SolutionStream {
    public int findLongestChain(int[][] pairs) {
        return Arrays.stream(pairs)
                .sorted((a, b) -> Integer.compare(a[1], b[1]))
                .reduce(new int[]{Integer.MIN_VALUE, 0}, 
                        (acc, pair) -> pair[0] > acc[0] ? new int[]{pair[1], acc[1] + 1} : acc,
                        (acc1, acc2) -> acc1)[1];
    }
}
 

解释

方法:

此题解采用了贪心算法。首先,对输入的数对数组按照每个数对的第二个元素升序排序,如果第二个元素相同,则按第一个元素降序排序。这样可以优先处理右端点较小的数对,以便使得后续有更多的可能数对可以接上去。初始化一个计数器 cnt 和变量 last,分别用来记录最长链的长度和上一个数对的右端点。遍历排序后的数对,只有当当前数对的左端点大于 last 时,才能将其加入到当前的数对链中,此时更新 cnt 和 last。最后返回 cnt,即为最长数对链的长度。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在排序数对时选择按第二个元素升序排序,而在相同的第二元素情况下按第一个元素降序排序?
在排序数对时,选择按第二个元素进行升序排序是为了尽可能地让数对链的右端点小,这样后续在添加新的数对时有更大的空间和更多的选择。这种排序方式有助于尽快完成数对的连接,减少被排除的可能性。当第二个元素相同时,按第一个元素降序排序则有助于在相同的结束点中选择开始点较大的数对,这样做可以减少后续数对的连接冲突,因为更早开始的数对可能阻碍后续数对的加入。
🦆
在题解中提到的贪心策略是基于什么理论或直觉?为什么选择这种策略可以得到最长的数对链?
题解中采用的贪心策略基于“最优子结构”的直觉,即在每一步选择当前最优的决策将导致全局最优的解决方案。具体到这个问题,每次选择右端点最小的数对,可以最大化后续添加更多数对的可能性,从而延长数对链。这种策略有效的原因是,每次扩展链时保证不会有更好的选择来替代当前的数对而导致错过更长的链,因为更长的右端点会减少未来连接数对的机会。
🦆
如果数对数组中存在重复的数对,例如[[1,2],[1,2],[2,3]],这种情况下算法是否还能正确运行?会有什么特别的处理吗?
算法仍然可以正确运行,即使存在重复的数对。在排序和遍历的过程中,重复的数对会被视为独立的个体。在贪心策略下,尽管重复数对可能会被多次考虑,但是它们的处理不会影响最终结果的正确性,因为每个数对都是基于其左端点和右端点来决定是否加入到当前链中。不需要特别的处理来去除或区分这些重复数对,因为它们在排序和选择中自然会被适当处理。
🦆
题解中提到初始化cnt为1而不是0,这是基于什么考虑?
初始化cnt为1而不是0的原因是至少存在一个数对会被加入到数对链中(假设输入的数对数组非空)。因此,从第一个数对开始,链已经有一个元素了。这是一种计数的开始方法,保证计数从实际的数对数量开始,而不是从0开始,后者需要在添加第一个数对时特别将计数器增加。这样的初始化简化了逻辑,使得后续只需在找到新的可连接数对时增加计数器即可。

相关问题

最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7]子序列

 

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

 

提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

 

进阶:

  • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

非递减子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

 

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]

 

提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100