暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数据结构和算法【30】俄罗斯套娃信封问题

皮皮克克 2023-08-18
4

点击关注公众号,干货第一时间送达


一道很有意思的题,

也是一道 "困难" 的题

一起来看看。

力扣原题链接:

https://leetcode.cn/problems/russian-doll-envelopes/


一、各位,请听题


举个例子:


这怎么求呢?

感觉有点无从下手啊!

不过,

这题和之前的文章:数据结构和算法【22】最长递增子序列

有点类似。


二、解题

信封数组 evelops = [[5,4], [6,4], [6,7], [2,3]]

每个信封有长 和 宽,分别可以用 evelops[i][0] 和 evelops[i][1] 表示。


如果想要层层嵌套,我们需要考虑小的在里面,大的在外面。

我们按照长度把信封从小到大排序,如果长度相等,则按照宽度从大到小排序

这个排序很关键!!!


可以发现什么?

长度已经从小到大排序了,所以我们再从左往右遍历中,后面的信封长度一定是不比左边小的,只要右侧的信封宽度比左侧的大,那么前面的信封一定可以装在当前信封里面。

只考虑宽度之间的相互关系!!!

嵌套的最大层数,就是此时宽度数组中,最长递增子序列的长度。

这个。。。

是不是用到了之前文章:数据结构和算法【22】最长递增子序列 的方法

(不过,如果完全使用上篇文章的方法,力扣上面会超时,所以本题的求解做了改进,代码里面用到了一个辅助数组 ends[])

看看代码。



完整代码:

public class CodingDemo {

    /**
     * TODO: 俄罗斯套娃信封问题
     * @param envelopes
     * @return
     */

    private static int maxEnvelopes(int[][] envelopes) {
        if (envelopes == null || envelopes.length == 0 || envelopes[0] == null || envelopes[0].length == 0){
            return 0;
        }

        //1,把原始数组转换为排序信封数组
        Envelop[] arr = getEnvelops(envelopes);

        //2, 创建辅助数组ends[]
        // ends[b]=c,表示长度为 b+1 的最长递增子序列,最小的结尾值是c
        // 初始的时候 ends[0] = arr[0].wide, 即长度为1的序列,就是 arr[0].wide
        int[] ends = new int[arr.length];
        ends[0] = arr[0].wide;

        //右边界
        int right = 0;

        for (int i = 1; i < arr.length; i++) {

            //在 ends[l ~ r] 范围内查找是否有值大于arr[i].wide
            //如果有,则更新对应的值为 arr[i].wide,
            //因为 ends[b]=e 中 e 是长度为b+1的最长递增子序列的最小结尾

            // 如果 ends[l ~ r] 值,没有大于 arr[i].wide的,则说明最长递增子序列的值需要增加了,也就是right边界右移
            //否则,替换 arr[i]
            int l = 0;
            int r = right;

            //二分查找
            while (l <= r){
                int mid = (l+r)/2;
                if (arr[i].wide > ends[mid]){
                    l = mid+1;
                } else {
                    r = mid-1;
                }
            }

            //判断right边界
            right = Math.max(right, l);
            //更新递增子序列最小结尾
            ends[l] = arr[i].wide;
        }
        //最长递增子序列就是 right+1
        return right+1;
    }

    /**
     * 把给定数组转换为有序信封数组
     * @param envelopes
     * @return
     */

    private static Envelop[] getEnvelops(int[][] envelopes){
        Envelop[] res = new Envelop[envelopes.length];
        for (int i = 0; i < envelopes.length; i++) {
            res[i] = new Envelop(envelopes[i][0], envelopes[i][1]);
        }
        //使用自定义比较器对生成的信封数组排序
        Arrays.sort(res, new EnvelopComparator());
        return res;
    }

    //自定义信封类,包括信封长度和宽度
    private static class Envelop{
        int len;
        int wide;

        public Envelop(int len, int wide) {
            this.len = len;
            this.wide = wide;
        }
    }

    //自定义信封类比较器
    private static class EnvelopComparator implements Comparator<Envelop{

        @Override
        public int compare(Envelop o1, Envelop o2) {
            //长度相等时,宽度大的在前
            //否则按照长度由小到大排列
            return o1.len == o2.len ? o2.wide - o1.wide : o1.len - o2.len;
        }
    }

    public static void main(String[] args) {
        int[][] arr = {
                {5,4},
                {6,4},
                {6,7},
                {2,3}
        };
        System.out.println(maxEnvelopes(arr));
    }
}


输出:

D:\java\bin\java.exe
3


去力扣试试水:


结束语:
Ok,到此为止,就是本篇文章的全部内容了。
该文章主要讲了 数据结构和算法: 俄罗斯套娃信封问题。
如果各位有不懂的地方,欢迎发消息给小编,小编会进行详细地解答。
最后,请屏幕前的吴彦祖和刘亦菲们,动动你们的小手,给小编一个

文章转载自皮皮克克,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论