From 467586e982568e66f1bf5cdf1845fec027169c2f Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Fri, 18 Nov 2022 14:49:22 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leecode/greedy/FindMinArrowShots.java | 56 ++++++- .../com/markilue/leecode/greedy/Merge.java | 113 +++++++++++++++ .../greedy/MonotoneIncreasingDigits.java | 137 ++++++++++++++++++ 3 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java b/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java index 81cdf3d..bb70692 100644 --- a/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/FindMinArrowShots.java @@ -29,13 +29,14 @@ public class FindMinArrowShots { // System.out.println((long)(-2147483646)-(long)(2147483647)); //-4294967293 // System.out.println((int)((long)(-2147483646)-(long)(2147483647)));//3 // System.out.println((int)(-2147483646)-(int)(2147483647)); //3 - System.out.println(findMinArrowShots(points)); + System.out.println(findMinArrowShots(points5)); } /** * 本人思路:先根据xstart进行排序,然后寻找第一个比xend大的xstart则result+1以此类推 * 排序的时间复杂度为O(nlogn),所以总复杂度为O(nlogn) - * 速度击败12.72%,内存击败88.71% + * 速度击败12.72%,内存击败88.71% =>修改compare方法后速度击败97.21%,内存击败91.4% 耗时52ms + * 代码随想录方法与本人一致 * @param points * @return */ @@ -46,7 +47,15 @@ public class FindMinArrowShots { @Override public int compare(int[] o1, int[] o2) { //警惕两数相减超过int范围 - return o1[0] == o2[0] ? (o1[1] > o2[1]?1:-1) : (o1[0] > o2[0]?1:-1); +// return o1[0] == o2[0] ? (o1[1] > o2[1]?1:-1) : (o1[0] > o2[0]?1:-1); + //这里改为一层if速度超过97.21% + if(o1[0] > o2[0]){ + return 1; + }else if(o1[0] < o2[0]){ + return -1; + }else{ + return 0; + } } }); @@ -70,4 +79,45 @@ public class FindMinArrowShots { return result; } + + + /** + * 官方思路:先根据xend进行排序,然后寻找第一个比xend大的xstart则result+1以此类推 + * 排序的时间复杂度为O(nlogn),所以总复杂度为O(nlogn) + * 速度击败30.68%,内存击败69.54% 59ms 这个应该有bug次次提交不一样,本质上应该比本人方法好,每次for都少判断一次 + * @param points + * @return + */ + public int findMinArrowShots1(int[][] points) { + + //按xend进行排序 + Arrays.sort(points, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + //这里两层if不太好,改为if写法,官方按xend进行排序 + if (o1[1] > o2[1]) { + return 1; + } else if (o1[1] < o2[1]) { + return -1; + } else { + return 0; + } + + } + }); + + + //遍历points,找到比xend大的xstart就+1 + int result=1; + int lastEnd=points[0][1]; + for (int i = 1; i < points.length; i++) { + if(points[i][0]>lastEnd){ + result++; + lastEnd=points[i][1]; + } + } + + return result; + + } } diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java b/Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java new file mode 100644 index 0000000..8ead320 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/Merge.java @@ -0,0 +1,113 @@ +package com.markilue.leecode.greedy; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.greedy + * @Author: markilue + * @CreateTime: 2022-11-18 10:58 + * @Description: TODO 力扣56题 合并区间: + * 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 + * @Version: 1.0 + */ +public class Merge { + + @Test + public void test() { + int[][] points = {{1, 2}, {3, 4},{5, 6},{7, 8}}; + int[][] points1 = {{1, 3}, {2, 6},{8, 10},{15, 18}}; + int[][] points2 = {{1, 2}, {2, 3},{3, 4},{4, 5}}; + int[][] points3 = {{1, 4}, {0, 4}}; + int[][] points4 = {{-2147483646, -2147483645}, {2147483646, 2147483647}}; + int[][] points5 = {{2,3}, {2, 2},{3, 3},{1, 3}, {5, 7}, {2, 2},{4, 6}}; +// System.out.println((long)(-2147483646)-(long)(2147483647)); //-4294967293 +// System.out.println((int)((long)(-2147483646)-(long)(2147483647)));//3 +// System.out.println((int)(-2147483646)-(int)(2147483647)); //3 + int[][] merge = merge(points2); + for (int[] ints : merge) { + System.out.println(Arrays.toString(ints)); + } + } + + + /** + * 本人思路:与射箭那题类似,本质上是找到重叠区间,因此仍然是按xstart排序,然后再一次循环寻找重叠区间 + * 速度击败71.63%,内存击败90.55% 7ms =>compare优化以后 速度击败97.55%,内存击败53.75% + * @param intervals + * @return + */ + public int[][] merge(int[][] intervals) { + + //按xstart排序 + Arrays.sort(intervals, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { +// if(o1[0]>o2[0]){ +// return 1; +// } else if (o1[0]=intervals[i][0]){ + //重叠区间,需要合并 + lastEnd=Math.max(lastEnd,intervals[i][1]); + result[length][1]=lastEnd; + }else { + //非重叠区间,直接添加 + length++; + result[length]=intervals[i]; + lastEnd=intervals[i][1]; + } + } + //缩短至result的真实长度 + return Arrays.copyOf(result,length+1); + } + + + /** + * 官方代码:与本人类似,并使用动态扩容的arrayList记录添加 + * 速度击败97.55%,内存击败9.14% + * @param intervals + * @return + */ + public int[][] merge1(int[][] intervals) { + if (intervals.length == 0) { + return new int[0][2]; + } + Arrays.sort(intervals, new Comparator() { + public int compare(int[] interval1, int[] interval2) { + return interval1[0] - interval2[0]; + } + }); + List merged = new ArrayList(); + for (int i = 0; i < intervals.length; ++i) { + int L = intervals[i][0], R = intervals[i][1]; + if (merged.size() == 0 || merged.get(merged.size() - 1)[1] < L) { + merged.add(new int[]{L, R}); + } else { + merged.get(merged.size() - 1)[1] = Math.max(merged.get(merged.size() - 1)[1], R); + } + } + return merged.toArray(new int[merged.size()][]); + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java b/Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java new file mode 100644 index 0000000..60eb359 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/greedy/MonotoneIncreasingDigits.java @@ -0,0 +1,137 @@ +package com.markilue.leecode.greedy; + +import org.junit.Test; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.greedy + * @Author: markilue + * @CreateTime: 2022-11-18 11:44 + * @Description: TODO 力扣738题 单调递增的数字: + * 当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。 + * 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。 + * @Version: 1.0 + */ +public class MonotoneIncreasingDigits { + + @Test + public void test() { + int n = 1234; + int n1 = 10; + int n2 = 332; + int n3=15; + + + System.out.println(monotoneIncreasingDigits2(n2)); + } + + /** + * 本人思路:可以发现的是,如果一个数最高位小于其后一位,那么无论如何都是需要把最高位减1的,而这时后面的数就可以随便取9了;后续位依次类推,直至/10=0为止 + * 这里有个矛盾的点是我们需要从最高位开始判,但是事实上我们不知道数字具体有多少位,只能不断/10来判 =>因此可以把n转为string获取长度 + * + * 速度击败94.57%,内存击败33.72% + * @param n + * @return + */ + public int monotoneIncreasingDigits(int n) { + + String s = n + ""; + int length = s.length(); + if (length == 1) { + return n; + } + + int[] num = new int[length]; + num[0]=n%10; + int index=1; + + n=n/10; + //这里仍然使用从后往前遍历,因为如果从前往后,后续又出现借的情况前面的逻辑可能有需要重新判一次 + while (n>0&&n/10>=0){ + + int now = n%10; + if(now>num[index-1]){ + now=now-1; + //当前减1后面全9 + num[index]=now; + for (int i = 0; i <= index - 1; i++) { + num[i]=9; + } + }else { + num[index]=now; + } + n=n/10; + index++; + } + int result=0; + for (int i = length-1; i >= 0; i--) { + result*=10; + result+=num[i]; + } + + return result; + + + + } + + + /** + * 官方题解:从前往后遍历,找到不合题解的位置,则把前面的数-1,后面的数全部置为9 + * 这种其实是比本人的优化的,因为本人是从后往前遍历,因此置为9的循环操作可能会持续很多次 + * 速度击败94.57%,内存击败57.77% + * @param n + * @return + */ + public int monotoneIncreasingDigits1(int n) { + char[] strN = Integer.toString(n).toCharArray(); + int i = 1; + while (i < strN.length && strN[i - 1] <= strN[i]) { + i += 1; + } + if (i < strN.length) { + //找到不合适的位置,一次性全部完成,所以最多只需要执行一次置为9循环 + while (i > 0 && strN[i - 1] > strN[i]) { + strN[i - 1] -= 1; + i -= 1; + } + for (i += 1; i < strN.length; ++i) { + strN[i] = '9'; + } + } + return Integer.parseInt(new String(strN)); + } + + + /** + * 评论里的题解:整体思路和官方差不多,但是他用n - Integer.parseInt(s.substring(1)) - 1;这种操作,避免了for循环,速度增加 + * 速度击败100%,内部击败79.4%, + * + * @param n + * @return + */ + public int monotoneIncreasingDigits2(int n) { + if (n < 10){ + return n; + } + String s = Integer.toString(n); + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) < s.charAt(i - 1)){ + int temp = i - 1; + while (true){ + if (temp == 0){ + return n - Integer.parseInt(s.substring(1)) - 1; //整体需要少一位,所以n-他后面的数-1如1056-056-1=999 + }else if (s.charAt(temp) - s.charAt(temp - 1) >= 1){ //当前数与前面的数之差大于1,则放心的把当前数减1 + return n - Integer.parseInt(s.substring(temp + 1)) - 1;//如1306-06-1=1299 + } + temp--;//如果相差小于1了,则不能直接减,因此从上一个点来看看 + } + } + } + return n; + } + + + + +}