代码随想录算法训练营第三十五天 | 435. 无重叠区间、763. 划分字母区间、56. 合并区间

52 阅读3分钟

435. 无重叠区间

链接

题目链接

文章链接

第一想法

由于昨天做过区间的题,所以今天很容易就想到了右区间,我先进行排序,在排序的时候有两种,第一种按照左边界,第二种按照右边界,由于[1,6]的区间没有[2,3]的区间好,因为删[1,6]比删[2,3]更可能让区间不重叠,所以我用右区间进行排序,代码如下:

function eraseOverlapIntervals(intervals: number[][]): number {
    intervals.sort((a, b) => a[1] - b[1]) //右区间排序
    let res: number = 0
    for (let i = 1; i < intervals.length; i++) {
        if (intervals[i][0] < intervals[i - 1][1]) { //有重叠的情况
            res++ //删除当前节点
            intervals[i][1] = intervals[i - 1][1]//将当前节点的右区间变为前一个的右区间  模拟删除
        }
    }
    return res
};

看完文章后的想法

文章的理解和我的是不一样的,文章还是考右边界,文章的右边界是一个动态的,直接上图:

image.png 也是利用右边界排序(尽可能小),之后计算可以不重复的有几个区间,之后那总数减去不重复的区间就是要删除的个数,代码如下:

function eraseOverlapIntervals(intervals: number[][]): number {
    intervals.sort((a, b) => a[1] - b[1])//排序
    let res: number = 1
    let end: number = intervals[0][1]
    for (let i = 1; i < intervals.length; i++) {
        if (intervals[i][0] >= end) {//算出不重叠的
            res++
            end = intervals[i][1]//动态右边界
        }
    }
    return intervals.length - res
};

思考

这道题和用箭射气球类似,但是我认为这道题用右边界排序是比较好理解的,所以没有写左边界的解法,感兴趣的可以去代码随想录中查看.

763. 划分字母区间

链接

文章链接

题目链接

第一想法

这个刚开始没啥想法,但是想了一会发现和重叠区间类似,所以尝试把这道问题转换为重叠区间问题,求有几个不重叠的区间,代码如下:

function partitionLabels(s: string): number[] {
    let res: number[] = []//求结果
    let arr: number[][] = new Array(27) //因为只有26个字母 这里设置27有妙用
    for (let i = 0; i < 27; i++) {
        arr[i] = new Array()
    }
    for (let i = 0; i < s.length; i++) {//构造每个字母出现的区间
        if (arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)].length == 0) {
            arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)][0] = i
            arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)][1] = i
        } else arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)][1] = i
    }
    arr.sort((a, b) => b.length - a.length)//把没出现的字母区间往后排
    arr.sort((a, b) => a[0] - b[0])//按照左边界排序
    for (let i = 1; i < arr.length; i++) {
        if (arr[i].length == 0) {//用于最后一个区间的结果收集  这就是为什么前面设置27的原因
            res.push(arr[i - 1][1] - arr[i - 1][0] + 1)
            break
        };
        if (arr[i][0] > arr[i - 1][1]) res.push(arr[i - 1][1] - arr[i - 1][0] + 1)//如果当前区间的左边界与前一个区间的右边界没有重合则记录这段的长度
        else {//有重合
            arr[i][1] = Math.max(arr[i][1], arr[i - 1][1]) //重合右边界取两者大的
            arr[i][0] = arr[i - 1][0]//左边界取小的  用于计算个数
        }
    }
    return res
};

看完文章的想法

文章的方法还是简便,只需要找到每一个字符最后一个出现的位置就可以了,之后循环判断当前的右边界是不是等于i,如果等于则是一个结果值,图解:

image.png 代码如下:

function partitionLabels(s: string): number[] {
    let res: number[] = []
    let arr: number[] = new Array(26)
    for (let i = 0; i < s.length; i++) { //记录每个字符最后出现的位置
        arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)] = i
    }
    let left: number = 0
    let right: number = 0
    for (let i = 0; i < s.length; i++) {
        right = Math.max(right, arr[s[i].charCodeAt(0) - 'a'.charCodeAt(0)])//right记录当前字符串的区间的右边界
        if (right == i) {//到达右边界则记录值
            res.push(right - left + 1)
            left = i + 1
        }
    }
    return res
};

思考

这道题偶然想到利用重叠区间的解法,尝试了一下发现能写出来.之后又看了文章的写法,文章的写法更加精妙和便捷,比我的解法好太多了,要多学习学习思路和方法了.

56. 合并区间

链接

文章链接

题目链接

第一想法

这道题比较简单,通过左边界排序后,当满足当前左边界大于前一个的右边界则存储结果,代码如下:

function merge(intervals: number[][]): number[][] {
    let res: number[][] = []
    intervals.sort((a, b) => a[0] - b[0])//排序
    for (let i = 1; i <= intervals.length; i++) {//这里的等号是为了让最后一个区间也能被res接收
        if (i == intervals.length || intervals[i][0] > intervals[i - 1][1]) res.push(intervals[i - 1].slice())//这里的intervals[i - 1]的左右边界就是整体的边界
        else {
            intervals[i][0] = Math.min(intervals[i][0], intervals[i - 1][0]) //当前整体区间的左边界
            intervals[i][1] = Math.max(intervals[i][1], intervals[i - 1][1])//当前整体区间的右边界
        }
    }
    return res
};

看完文章的想法

文章的写法和我的写法是一致的,所以这里就复制代码了。

思考

这道题一个是最简单的一道题,这道题考的是区间重叠的基础,所以不需要考虑太多,一下子就写出来了。

今日总结

今天耗时2.5小时,三道题都不难但是都很巧妙,值得学习。