LeetCode Top100之55,56题

55. 跳跃游戏
① 题目描述
  • 给定一个非负整数数组,你最初位于数组的第一个位置。
  • 数组中的每个元素代表你在该位置可以跳跃的最大长度。
  • 判断你是否能够到达最后一个位置。
  • 示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

  • 示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 ,所以你永远不可能到达最后一个位置。

② 相同题型—— 45. 跳跃游戏 II
(1)顺向顺藤摸瓜(贪心算法)
  • 贪心算法,每次在可跳范围内选择可以使得跳的更远的位置。
  • 如下图,开始的位置是 2,可跳的范围是橙色的。然后因为 3 可以跳的更远,所以跳到 3 的位置。
    在这里插入图片描述
  • 如下图,然后现在的位置就是 3 了,能跳的范围是橙色的,然后因为 4 可以跳的更远,所以下次跳到 4 的位置。
    在这里插入图片描述
  • 发现,最后只需要跳2步,就可以达到末尾,意思是given > need
  • 时间复杂度 O ( n ) O(n) O(n),代码如下:
public int jump(int[] nums) {
    int end = 0;
    int maxPosition = 0;
    int step = 0;
    for (int i = 0; i < nums.length - 1; i++) {
        // 找到能跳的最远的位置
        maxPosition = Math.max(maxPosition, nums[i] + i);
        if (i == end) {// 到达了目前能跳的边界,更新边界,步数加一。跳向的位置一定在边界内
            end = maxPosition;
            step++;
        }
    }
    return step;
}
(2)逆向顺藤摸瓜
  • 假设能达到末尾,则从末尾开始往前推,找到能达到末尾的最远的那个位置作为新的末尾;以此类推,直到末尾的下标变为0。
  • 如下图,末尾位置是 1,从前往后遍历,发现能跳到末尾的最远位置是橙色的4,,因此橙色4是跳到末尾的那个位置。
    在这里插入图片描述
  • 将4作为末尾位置,从前往后遍历,能跳到4的最远位置是橙色的3。
    在这里插入图片描述
  • 将3作为末尾位置,从前往后遍历,能跳到3的最远位置是橙色的1,此时已经到达首位,说明跳跃已经完成!
    在这里插入图片描述
  • 最坏的时间复杂度 O ( n 2 ) O(n^2) O(n2),代码如下:
public int jump(int[] nums) {
    int step = 0;
    int last = nums.length - 1;
    while (last != 0) {
        for (int i = 0; i < last; i++) {// 从前往后遍历,找到的第一个符合要求的位置就是最远的,停止遍历
            if (nums[i] >= last - i) {// 能跳的距离大于等于该位置和last之间的距离,说明该位置符合要求
                last = i;
                step++;
                break;
            }
        }
    }
    return step;
}
③ 改进相似题型的求解
(1)顺序顺藤摸瓜
  • 45题是肯定能跳到末尾,要让你求解跳到末尾的最少step;而55题不一定能跳到末尾,要让你自己判断是否能达到末尾。
  • 如果采用45题的第一种解法,那么在循环遍历的过程中,如果能跳的边界小于下标i,说明不能到达下标i,肯定不能跳到末尾,直接退出遍历!
  • 直接改进45题第一种解法的代码,代码如下:
public boolean canJump(int[] nums) {
    int end = 0;
    int maxPosition = 0;
    for (int i = 0; i < nums.length - 1; i++) {
        if (i > end) {// 能跳的边界小于下标i,说明不能到达该位置,肯定不能达到末尾
            return false;
        }
        maxPosition = Math.max(maxPosition, nums[i] + i);
        if (i == end) {
            end = maxPosition;
        }
    }
    return maxPosition >= nums.length - 1;
}
(2)逆序顺藤摸瓜
  • 假设能跳到末尾,从后往前推,一定能推到下标为0;如果中途发现nums[i] < last-i,肯定不能跳到末尾。
  • 改进45题的第二种解法,代码如下:
public boolean canJump(int[] nums) {
    int last = nums.length - 1;
    while (last != 0) {
        boolean flag = false;
        for (int i = 0; i < last; i++) {
            if (nums[i] >= last - i) {
                last = i;
                flag = true;
                break;
            }
        }
        // flag为false,说明没有找到满足条件的最远位置,即没有位置能跳到last位置,直接返回false
        if (!flag) {
            return false;
        }
    }
    return true;
}
(3)当前的最大距离是否超过当前i
  • 比如[0,3,1,1,4],当到数字3时,当前的最大距离(能跳的距离的累积)为0,而下标i = 1,肯定不能从数字0跳到数字3。
  • 先比较当前的最大距离和下标i的大小关系,然后不小于下标i,说明能到达下标i处,更新最大距离。
  • 代码如下:
public boolean canJump(int[] nums) {
    int max_sum = 0;
    for (int i = 0; i < nums.length; i++) {
        if (max_sum < i) {
            return false;
        }
        max_sum = Math.max(max_sum, nums[i] + i);
    }
    return true;
}
56. 合并区间
① 题目描述
  • 给出一个区间的集合,请合并所有重叠的区间。
  • 示例 1:

输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间[1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

  • 示例 2:

输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

② 先排序再合并
  • 为区间构造区间类,然后构造区间类的比较器,通过比较器对 List<Interval>进行排序。
  • 排序后的区间,只需要前后合并即可。
  • 代码如下:
 // 创建区间类
 public class Interval {
     int start;
     int end;
     Interval(int start, int end) {
         this.start = start;
         this.end = end;
     }
 }

 public int[][] merge(int[][] intervals) {
     // 为区间类创建比较器
     Comparator<Interval> comparator = new Comparator<Interval>() {
         @Override
         public int compare(Interval o1, Interval o2) {
             return o1.start - o2.start;
         }
     };
     // 将二维数组转化为List
     List<Interval> intervalList = new ArrayList<>();
     for (int i = 0; i < intervals.length; i++) {
         intervalList.add(new Interval(intervals[i][0], intervals[i][1]));
     }
     // 通过创建的区间类比较器,将List中的区间进行排序
     Collections.sort(intervalList, comparator);
     // 遍历List,实现区间合并
     for (int i = 0; i < intervalList.size() - 1; ) {
         // 区间可以合并,指针i不变,用新区间继续合并后面的区间
         if (intervalList.get(i).end >= intervalList.get(i + 1).start) {
             intervalList.get(i).end = Math.max(intervalList.get(i).end, intervalList.get(i + 1).end);
             intervalList.remove(i + 1);
         } else {
             i++;// 区间不可以合并,移动指针i
         }
     }
     // 将List转换二维数组,返回结果
     intervals = new int[intervalList.size()][2];
     for (int i = 0; i < intervalList.size(); i++) {
         intervals[i][0] = intervalList.get(i).start;
         intervals[i][1] = intervalList.get(i).end;
     }
     return intervals;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值