2021/1/28 最长递增子序列

最长递增子序列

http://www.nowcoder.com/questionTerminal/9cf027bf54714ad889d4f30ff0ae5481

题目描述

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)

示例1

输入

[2,1,5,3,6,4,8,9,7]

返回值

[1,3,4,8,9]

示例2

输入

[1,2,8,6,4]

返回值

[1,2,4]

说明

其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)

解题思路

下面的“标号”讲的过于抽象,我推荐《最长递增子序列 - murphy_gb - 博客园》 这篇文章,里面的动图很形象。

  1. 我们将大问题拆分为小问题,把数组缩短,再慢慢加长直到完整。如示例 1 中 原数组 arr = [2, 1, 5, 3, 6, 4, 8, 9, 7] 我们把最初的子问题定为 [2, 1],下一个子问题既往后加长一位 [2, 1, 5],以此类推。
  2. 为了能在遍历完子问题后精确地在原数组 arr 中找出组成最长递增子序列 LCS 的元素,我们可以使用“标号”的方法,在 arr 中组成 LCS 的元素上标上序号,比如示例 1 中 arr = [2, 1, 5, 3, 6, 4, 8, 9, 7], LCS = [1, 3, 4, 8, 9],LCS[0] = arr[1],LCS[1] = arr[3],LCS[2] = arr[5]。所以如何标号就是这个问题的关键。
    图片说明
  3. 如何标号呢?我们需要新增一个数组 temp,每当子问题增加一个元素 e 时,e 就与 temp 最后一个元素就进行比较,如果 e 比 temp 的最后一个元素大,则直接在 temp 最后面添加 e;反之,则在 temp 中从左往右寻找第一个比 e 大的数,并用 e 替换之。然后 e 在 temp 中的索引就是我们要找的标号,我们将标号存起来,继续下一个子问题。
    图片说明
  4. 在 nums 中标完号后,为了满足题目要求的字典序最小,我们需要从后往前遍历,标号从大到小,倒着填入 LCS 中,最后我们获得结果 LCS。
    图片说明

Java代码实现

public class Solution {

    public int[] LIS (int[] arr) {
        int[] nums = new int[arr.length];
        int[] temp = new int[arr.length];
        nums[0] = 1;
        int tempIndex = 0;
        temp[tempIndex] = arr[0];
        for (int i = 1; i < arr.length; ++i) {
            int left = 0, right = tempIndex;
            if (arr[i] > temp[tempIndex]) {
                ++tempIndex;
                nums[i] = tempIndex + 1;
                temp[tempIndex] = arr[i];
            } else {
                while (left <= right) {        // 注意这里 left <= right 而不是 left < right,我们要替换的是第一个比 arr[i] 大的元素
                    int mid = (right + left) / 2;
                    if (temp[mid] > arr[i]) {
                        right = mid - 1;
                    } else {
                        left = mid + 1;
                    }
                }
                temp[left] = arr[i];
                nums[i] = left + 1;
            }
        }

        int[] res = new int[tempIndex + 1];
        for (int i = nums.length - 1; i >= 0; --i) {
            if (nums[i] == tempIndex + 1) {
                res[tempIndex] = arr[i];
                --tempIndex;
            }
        }
        return res;
    }

}
全部评论
二分查找第一个大于等于arr[i]的索引,上述18行应该为“if (temp[mid] >= arr[i])”
2 回复 分享
发布于 2021-07-23 19:32
瞎扯淡,用例都过不了
1 回复 分享
发布于 2021-09-05 17:59
if (nums[i] == tempIndex + 1) { res[tempIndex] = arr[i]; --tempIndex; } 请问这段代码是怎么从原始数组中找到最小递增子序列的呀 实在是没理解
点赞 回复 分享
发布于 2021-02-26 14:06
兄弟,你的解释和实现好像有点不一样呀
点赞 回复 分享
发布于 2021-04-19 10:20
nb
点赞 回复 分享
发布于 2021-06-24 17:01
[1,3,8,6,5,2,5]通过不了
点赞 回复 分享
发布于 2021-07-09 15:23
用例输入 [1,3,8,6,5,2,5] 预期输出 [1,2,5] 实际输出 [1,3,5]
点赞 回复 分享
发布于 2021-07-29 23:01
有点问题需要考虑等于情况 public class Solution { public int[] LIS (int[] arr) { int[] nums = new int[arr.length]; int[] temp = new int[arr.length]; nums[0] = 1; int tempIndex = 0; temp[tempIndex] = arr[0]; for (int i = 1; i < arr.length; ++i) { int left = 0, right = tempIndex; if (arr[i] > temp[tempIndex]) { ++tempIndex; nums[i] = tempIndex + 1; temp[tempIndex] = arr[i]; } else { while (left <= right) { // 注意这里 left <= right 而不是 left < right,我们要替换的是第一个比 arr[i] 大的元素 int mid = (right + left) / 2; if (temp[mid] > arr[i]) { right = mid - 1; } else { left = mid + 1; } } if(left>0&&temp[left-1]==arr[i]){ left--; } temp[left] = arr[i]; nums[i] = left + 1; } } int[] res = new int[tempIndex + 1]; for (int i = nums.length - 1; i >= 0; --i) { if (nums[i] == tempIndex + 1) { res[tempIndex] = arr[i]; --tempIndex; } } return res; } }
点赞 回复 分享
发布于 2021-08-15 22:21
我也是服了,代码都过不了,好意思发上来
点赞 回复 分享
发布于 2021-09-10 16:33
这个是最后找最小值有问题
点赞 回复 分享
发布于 2021-09-12 12:20
while(left <= right){ int mid = (left + right) >> 1; if(temp[mid] > arr[i]){ right = mid - 1; }else if(temp[mid] < arr[i]){ left = mid + 1; }else{ left = mid; break; } } temp[left] = arr[i]; dp[i] = left + 1; }
点赞 回复 分享
发布于 2021-09-17 00:41
深受启发!
点赞 回复 分享
发布于 2021-09-20 17:54
思路和解释都没问题的,代码跑不动是因为有一种情况没考虑到:[1,3,8,6,5,2,5],最后最大值跟原先最大值一样大,但因为没有考虑导致输出的是[1,3,5]而不是[1,2,5],解决办法,考虑最大值等于情况,for循环判断条件中加入:else if(arr[i] == temp[tempIndex]){ nums[i] = tempIndex + 1; temp[tempIndex] = arr[i]; }
点赞 回复 分享
发布于 2021-12-12 11:22

相关推荐

评论
28
4
分享

创作者周榜

更多
牛客网
牛客企业服务