diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/T11_1_EraseOverlapIntervals.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_1_EraseOverlapIntervals.java new file mode 100644 index 0000000..48f9b6f --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_1_EraseOverlapIntervals.java @@ -0,0 +1,129 @@ +package com.markilue.leecode.greedy; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Comparator; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy + *@Author: markilue + *@CreateTime: 2023-02-12 10:28 + *@Description: + * TODO 力扣435题 无重叠区间: + * 给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 + *@Version: 1.0 + */ +public class T11_1_EraseOverlapIntervals { + + @Test + public void test() { +// int[][] intervals = {{ +// 1, 2 +// }, { +// 2, 3 +// }, { +// 3, 4 +// }, { +// 1, 3 +// }}; + + int[][] intervals = {{0, 2}, {1, 3}, {2, 4}, {3, 5}, {4, 6}}; + System.out.println(eraseOverlapIntervals(intervals)); + } + + /** + * 思路:与上题11的射爆重叠气球的思路类似,只不过这题是需要去除的区间,还是按xstart进行排序 + * 速度击败52.7%,内存击败15.57% 50ms + * @param intervals + * @return + */ + public int eraseOverlapIntervals(int[][] intervals) { + + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + + for (int[] interval : intervals) { + System.out.println(Arrays.toString(interval)); + } + + int threshold = intervals[0][1]; + int result = 0; + + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] < threshold) { + //需要移除 + result++; + threshold = Math.min(intervals[i][1], threshold); + } else { + threshold = intervals[i][1]; + } + } + + + return result; + + } + + + /** + * 官方思路:根据xend右排序,记录不会重叠的位置,最后的结果就是总区间数n-不会重叠区间ans + * 因为是按右排序,所以不用考虑子集情况 + * 速度击败98.28%,内存击败71% 47ms + * @param intervals + * @return + */ + public int eraseOverlapIntervals1(int[][] intervals) { + if (intervals.length == 0) { + return 0; + } + Arrays.sort(intervals, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return o1[1] - o2[1]; + } + }); + int n = intervals.length; + int right = intervals[0][1]; + int ans = 1; + for (int i = 1; i < n; i++) { + if (intervals[i][0] >= right) { + ans++; + right = intervals[i][1]; + } + } + return n - ans; + } + + + /** + * 官方动态规划法:时间复杂度比较高O(n^2) + * @param intervals + * @return + */ + public int eraseOverlapIntervals2(int[][] intervals) { + if (intervals.length == 0) { + return 0; + } + + Arrays.sort(intervals, new Comparator() { + public int compare(int[] interval1, int[] interval2) { + return interval1[0] - interval2[0]; + } + }); + + int n = intervals.length; + int[] f = new int[n]; + Arrays.fill(f, 1); + for (int i = 1; i < n; ++i) { + for (int j = 0; j < i; ++j) { + if (intervals[j][1] <= intervals[i][0]) { + f[i] = Math.max(f[i], f[j] + 1); + } + } + } + return n - Arrays.stream(f).max().getAsInt(); + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/T11_2_PartitionLabels.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_2_PartitionLabels.java new file mode 100644 index 0000000..241e830 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_2_PartitionLabels.java @@ -0,0 +1,103 @@ +package com.markilue.leecode.greedy; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy + *@Author: markilue + *@CreateTime: 2023-02-12 11:14 + *@Description: + * TODO 力扣763题 划分字母区间: + * 给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 + * 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 + * 返回一个表示每个字符串片段的长度的列表。 + *@Version: 1.0 + */ +public class T11_2_PartitionLabels { + + @Test + public void test() { + String s = "eccbbbbdec"; + System.out.println(partitionLabels(s)); + } + + + /** + * 思路:Map记录,如果num不在map的话count+1重新分片,并把它加入map + * 速度击败7.77%,内存击败9.27% 10ms + * @param s + * @return + */ + public List partitionLabels(String s) { + + char[] chars = s.toCharArray(); + HashMap map = new HashMap<>();// + List result = new ArrayList<>(); + + for (int i = 0; i < chars.length; i++) { + if (map.containsKey(chars[i])) { + Integer startIndex = map.get(chars[i]); + //看看需要更改result的位置 + int lastThreshold = -1;//count转index + int count = 0; + for (; count < result.size(); count++) { + int index=result.get(count); + if (startIndex <= lastThreshold+index) {//count变index + //撞了 + result.set(count, i - lastThreshold); + break; + } + lastThreshold += index; + } + //移除撞了之后的,全部变成一个 + for (; result.size() > count + 1; ) { + result.remove(result.size() - 1); + } + } else { + //没遇到过 + result.add(1); + map.put(chars[i], i); + } + } + + return result; + + } + + + /** + * 代码随想录思路:本质上就是看那个字母出现的最远,如果遍历到了最远的位置还没有比她更远的,那么就把他加入result中;从而转换为11.1和11之中完全一样的解法了 + * TODO + * 在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。 + * 可以分为如下两步: + * 1.统计每一个字符最后出现的位置 + * 2.从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点 + * 速度击败98.47% 内存击败38.58% 2ms + * @param S + * @return + */ + public List partitionLabels1(String S) { + List list = new LinkedList<>(); + int[] edge = new int[26]; + char[] chars = S.toCharArray(); + for (int i = 0; i < chars.length; i++) { + edge[chars[i] - 'a'] = i; + } + int idx = 0; + int last = -1; + for (int i = 0; i < chars.length; i++) { + idx = Math.max(idx, edge[chars[i] - 'a']); + if (i == idx) { + list.add(i - last); + last = i; + } + } + return list; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_FindMinArrowShots.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java rename to Leecode/src/main/java/com/markilue/leecode/greedy/T11_FindMinArrowShots.java index bb70692..8ae76ec 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/T11_FindMinArrowShots.java @@ -16,7 +16,7 @@ import java.util.Comparator; * 给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。 * @Version: 1.0 */ -public class FindMinArrowShots { +public class T11_FindMinArrowShots { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/second/T11_FindMinArrowShots.java b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T11_FindMinArrowShots.java new file mode 100644 index 0000000..bacc4e6 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/second/T11_FindMinArrowShots.java @@ -0,0 +1,92 @@ +package com.markilue.leecode.greedy.second; + +import org.junit.Test; + +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.greedy.second + *@Author: markilue + *@CreateTime: 2023-02-12 09:40 + *@Description: + * TODO 力扣452题 用最少数量的箭引爆气球: + * 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 + * 一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。 + * 在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。 + * 可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。 + * 给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。 + *@Version: 1.0 + */ +public class T11_FindMinArrowShots { + + + @Test + public void test(){ + int[][] points = {{9, 12}, {1, 10}, {4, 11}, {8, 12}, {3, 9}, {6, 9}, {6, 7}}; + System.out.println(findMinArrowShots(points)); + } + + /** + * 思路:按xstart排序,当下一个的xstart大于上一个的xend那么就需要增加一支箭 + * 速度击败81.41%,内存击败21.99% 53ms + * @param points + * @return + */ + public int findMinArrowShots(int[][] points) { + + Arrays.sort(points, (a, b) -> { + if(a[1]!=b[1]){ + if(a[1]>b[1]){ + return 1; + }else { + return -1; + } + }else { + if(a[0]>b[0]){ + return 1; + }else { + return -1; + } + } + }); + + int threshold = points[0][1]; + int arrow = 1; + + for (int i = 1; i < points.length; i++) { + if (threshold < points[i][0]) { + //不够了 + arrow++; + threshold = points[i][1]; + } + } + + + return arrow; + + } + + + /** + * 代码随想录思路:与本人之前的思路类似,按xstart排序,如果是自己,更新threshold + * 速度击败68.99%,内存击败87.94% 54ms + * @param points + * @return + */ + public int findMinArrowShots1(int[][] points) { + // 根据气球直径的开始坐标从小到大排序 + // 使用Integer内置比较方法,不会溢出 + Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0])); + + int count = 1; // points 不为空至少需要一支箭 + for (int i = 1; i < points.length; i++) { + if (points[i][0] > points[i - 1][1]) { // 气球i和气球i-1不挨着,注意这里不是>= + count++; // 需要一支箭 + } else { // 气球i和气球i-1挨着 + points[i][1] = Math.min(points[i][1], points[i - 1][1]); // 更新重叠气球最小右边界 + } + } + return count; + } +}