diff --git a/算法周更班/class_2022_10_3_week/Code01_MaximumWidthRamp.java b/算法周更班/class_2022_10_3_week/Code01_MaximumWidthRamp.java new file mode 100644 index 0000000..beae183 --- /dev/null +++ b/算法周更班/class_2022_10_3_week/Code01_MaximumWidthRamp.java @@ -0,0 +1,42 @@ +package class_2022_10_3_week; + +// 给定一个整数数组 A,坡是元组 (i, j),其中  i < j 且 A[i] <= A[j] +// 这样的坡的宽度为 j - i +// 找出 A 中的坡的最大宽度,如果不存在,返回 0 +// 示例 1: +// 输入:[6,0,8,2,1,5] +// 输出:4 +// 解释: +// 最大宽度的坡为 (i, j) = (1, 5): A[1] = 0 且 A[5] = 5 +// 示例 2: +// 输入:[9,8,1,0,1,9,4,0,4,1] +// 输出:7 +// 解释: +// 最大宽度的坡为 (i, j) = (2, 9): A[2] = 1 且 A[9] = 1 +// 测试链接 : https://leetcode.cn/problems/maximum-width-ramp/ +public class Code01_MaximumWidthRamp { + + public static int maxWidthRamp(int[] arr) { + int n = arr.length; + // 栈中只放下标 + int[] stack = new int[n]; + // 栈的大小 + int r = 0; + for (int i = 0; i < n; i++) { + if (r == 0 || arr[stack[r - 1]] > arr[i]) { + stack[r++] = i; + } + } + int ans = 0; + // 从右往左遍历 + // j = n - 1 + for (int j = n - 1; j >= 0; j--) { + while (r != 0 && arr[stack[r - 1]] <= arr[j]) { + int i = stack[--r]; + ans = Math.max(ans, j - i); + } + } + return ans; + } + +} diff --git a/算法周更班/class_2022_10_3_week/Code02_ThreeEqualParts.java b/算法周更班/class_2022_10_3_week/Code02_ThreeEqualParts.java new file mode 100644 index 0000000..486c45c --- /dev/null +++ b/算法周更班/class_2022_10_3_week/Code02_ThreeEqualParts.java @@ -0,0 +1,80 @@ +package class_2022_10_3_week; + +// 给定一个由 0 和 1 组成的数组 arr ,将数组分成 3 个非空的部分 +// 使得所有这些部分表示相同的二进制值。 +// 如果可以做到,请返回任何 [i, j],其中 i+1 < j,这样一来 +// arr[0], arr[1], ..., arr[i] 为第一部分 +// arr[i + 1], arr[i + 2], ..., arr[j - 1] 为第二部分 +// arr[j], arr[j + 1], ..., arr[arr.length - 1] 为第三部分 +// 这三个部分所表示的二进制值相等 +// 如果无法做到,就返回 [-1, -1] +// 注意,在考虑每个部分所表示的二进制时,应当将其看作一个整体 +// 例如,[1,1,0] 表示十进制中的 6,而不会是 3。此外,前导零也是被允许的 +// 所以 [0,1,1] 和 [1,1] 表示相同的值。 +// 测试链接 : https://leetcode.cn/problems/three-equal-parts/ +public class Code02_ThreeEqualParts { + + public static int[] threeEqualParts(int[] arr) { + // 计算arr中1的数量 + int ones = 0; + for (int num : arr) { + ones += num == 1 ? 1 : 0; + } + // 如果1的数量不能被3整除,肯定不存在方案 + if (ones % 3 != 0) { + return new int[] { -1, -1 }; + } + int n = arr.length; + // 如果1的数量是0,怎么划分都可以了,因为全是0 + if (ones == 0) { + return new int[] { 0, n - 1 }; + } + // 接下来的过程 + // 因为1的数量能被3整除,比如一共有12个1 + // 那么第一段肯定含有第1个1~第4个1 + // 那么第二段肯定含有第5个1~第8个1 + // 那么第三段肯定含有第9个1~第12个1 + // 所以把第1个1,当做第一段的开头,start1 + // 所以把第5个1,当做第二段的开头,start2 + // 所以把第9个1,当做第三段的开头,start3 + int part = ones / 3; + int start1 = -1; + int start2 = -1; + int start3 = -1; + int cnt = 0; + // 1个数21个 + // part = 7 + // 1 8 + for (int i = 0; i < n; i++) { + if (arr[i] == 1) { + cnt++; + if (start1 == -1 && cnt == 1) { + start1 = i; + } + if (start2 == -1 && cnt == part + 1) { + start2 = i; + } + if (start3 == -1 && cnt == 2 * part + 1) { + start3 = i; + } + } + } + // 第一段的开头往下的部分 + // 第二段的开头往下的部分 + // 第三段的开头往下的部分 + // 要都一样,这三段的状态才是一样的 + // 所以接下来就验证这一点,是不是每一步都一样 + while (start3 < n) { + if (arr[start1] != arr[start2] || arr[start1] != arr[start3]) { + // 一旦不一样,肯定没方案了 + return new int[] { -1, -1 }; + } + start1++; + start2++; + start3++; + } + // 如果验证通过,返回断点即可 + return new int[] { start1 - 1, start2 }; + } + +} diff --git a/算法周更班/class_2022_10_3_week/Code03_AddToSorted.java b/算法周更班/class_2022_10_3_week/Code03_AddToSorted.java new file mode 100644 index 0000000..5af84b8 --- /dev/null +++ b/算法周更班/class_2022_10_3_week/Code03_AddToSorted.java @@ -0,0 +1,312 @@ +package class_2022_10_3_week; + +// 来自阿里 +// 给定一个长度n的数组,每次可以选择一个数x +// 让这个数组中所有的x都变成x+1,问你最少的操作次数 +// 使得这个数组变成一个非降数组 +// n <= 3 * 10^5 +// 0 <= 数值 <= 10^9 +public class Code03_AddToSorted { + + // 方法1 + // 暴力方法 + // 为了验证 + public static int minOp1(int[] arr) { + int max = 0; + for (int num : arr) { + max = Math.max(max, num); + } + return process1(0, max, new boolean[max + 1], arr); + } + + public static int process1(int num, int max, boolean[] op, int[] arr) { + if (num == max + 1) { + int cnt = 0; + int[] help = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + help[i] = arr[i]; + } + for (int i = 0; i <= max; i++) { + if (op[i]) { + cnt++; + add(help, i); + } + } + for (int i = 1; i < arr.length; i++) { + if (help[i - 1] > help[i]) { + return Integer.MAX_VALUE; + } + } + return cnt; + } else { + op[num] = true; + int p1 = process1(num + 1, max, op, arr); + op[num] = false; + int p2 = process1(num + 1, max, op, arr); + return Math.min(p1, p2); + } + } + + public static void add(int[] arr, int num) { + for (int i = 0; i < arr.length; i++) { + if (arr[i] == num) { + arr[i]++; + } + } + } + + // 方法2 + // 最优解的思路 + // 但依然不能通过考试数据量 + // 为了验证 + public static int minOp2(int[] arr) { + if (arr.length < 2) { + return 0; + } + int n = arr.length; + int[] min = new int[n]; + min[n - 1] = arr[n - 1]; + for (int i = n - 2; i >= 0; i--) { + min[i] = Math.min(min[i + 1], arr[i]); + } + int max = 0; + for (int num : arr) { + max = Math.max(max, num); + } + boolean[] add = new boolean[max + 1]; + for (int i = 0; i < n - 1; i++) { + if (arr[i] > min[i + 1]) { + for (int j = min[i + 1]; j < arr[i]; j++) { + add[j] = true; + } + } + } + int ans = 0; + for (boolean is : add) { + ans += is ? 1 : 0; + } + return ans; + } + + // 方法3 + // 最优解 + 动态开点线段树 + // 时间复杂度O(N*logN) + public static int minOp3(int[] arr) { + int n = arr.length; + int m = 0; + for (int num : arr) { + m = Math.max(m, num); + } + // 0 ~ 1000000000 + DynamicSegmentTree dst = new DynamicSegmentTree(m); + for (int i = 1, max = arr[0]; i < n; i++) { + // max左边最大值 + // 10 4 + // 4...9 + if (max > arr[i]) { + dst.set(arr[i], max - 1); + } + max = Math.max(max, arr[i]); + } + return dst.sum(); + } + + public static class Node { + public int sum; + public boolean set; + public Node left; + public Node right; + } + + // 动态增加 + public static class DynamicSegmentTree { + public Node root; + public int size; + + public DynamicSegmentTree(int max) { + root = new Node(); + size = max; + } + + private void pushDown(Node p, int ln, int rn) { + if (p.left == null) { + p.left = new Node(); + } + if (p.right == null) { + p.right = new Node(); + } + if (p.set) { + p.left.set = true; + p.right.set = true; + p.left.sum = ln; + p.right.sum = rn; + p.set = false; + } + } + + public void set(int s, int e) { + update(root, 0, size, s, e); + } + + private void update(Node c, int l, int r, int s, int e) { + if (s <= l && r <= e) { + c.set = true; + c.sum = (r - l + 1); + } else { + int mid = (l + r) >> 1; + pushDown(c, mid - l + 1, r - mid); + if (s <= mid) { + update(c.left, l, mid, s, e); + } + if (e > mid) { + update(c.right, mid + 1, r, s, e); + } + c.sum = c.left.sum + c.right.sum; + } + } + + public int sum() { + return root.sum; + } + } + + // 方法4 + // 最优解 + 固定数组的动态开点线段树(多次运行更省空间) + // 时间复杂度O(N*logN) + public static int minOp4(int[] arr) { + int n = arr.length; + int m = 0; + for (int num : arr) { + m = Math.max(m, num); + } + for (int i = 0; i < cnt; i++) { + lchild[i] = -1; + rchild[i] = -1; + } + cnt = 0; + sum[cnt] = 0; + set[cnt] = false; + left[cnt] = 0; + right[cnt++] = m; + for (int i = 1, max = arr[0]; i < n; i++) { + if (max > arr[i]) { + set(arr[i], max - 1, 0); + } + max = Math.max(max, arr[i]); + } + return sum(); + } + + // 空间固定! + public static final int MAXM = 8000000; + public static int[] sum = new int[MAXM]; + public static boolean[] set = new boolean[MAXM]; + public static int[] left = new int[MAXM]; + public static int[] right = new int[MAXM]; + public static int[] lchild = new int[MAXM]; + public static int[] rchild = new int[MAXM]; + + static { + for (int i = 0; i < MAXM; i++) { + lchild[i] = -1; + rchild[i] = -1; + } + } + + public static int cnt = 0; + + public static void set(int s, int e, int i) { + int l = left[i]; + int r = right[i]; + if (s <= l && r <= e) { + set[i] = true; + sum[i] = (r - l + 1); + } else { + int mid = (l + r) >> 1; + down(i, l, mid, mid + 1, r, mid - l + 1, r - mid); + if (s <= mid) { + set(s, e, lchild[i]); + } + if (e > mid) { + set(s, e, rchild[i]); + } + sum[i] = sum[lchild[i]] + sum[rchild[i]]; + } + } + + public static void down(int i, int l1, int r1, int l2, int r2, int ln, int rn) { + if (lchild[i] == -1) { + sum[cnt] = 0; + set[cnt] = false; + left[cnt] = l1; + right[cnt] = r1; + lchild[i] = cnt++; + } + if (rchild[i] == -1) { + sum[cnt] = 0; + set[cnt] = false; + left[cnt] = l2; + right[cnt] = r2; + rchild[i] = cnt++; + } + if (set[i]) { + set[lchild[i]] = true; + set[rchild[i]] = true; + sum[lchild[i]] = ln; + sum[rchild[i]] = rn; + set[i] = false; + } + } + + public static int sum() { + return sum[0]; + } + + // 为了测试 + public static int[] randomArray(int n, int v) { + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + ans[i] = (int) (Math.random() * v); + } + return ans; + } + + // 为了测试 + public static void main(String[] args) { + int N = 10; + int V = 12; + int testTime = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + int[] arr = randomArray(n, V); + int ans1 = minOp1(arr); + int ans2 = minOp2(arr); + int ans3 = minOp3(arr); + int ans4 = minOp4(arr); + if (ans1 != ans2 || ans1 != ans3 || ans1 != ans4) { + System.out.println("出错了!"); + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + N = 300000; + V = 1000000000; + testTime = 10; + System.out.println("数组长度 : " + N); + System.out.println("数值范围 : " + V); + System.out.println("测试次数 : " + testTime); + long runTime = 0; + for (int i = 0; i < testTime; i++) { + int[] arr = randomArray(N, V); + long start = System.currentTimeMillis(); + minOp4(arr); + long end = System.currentTimeMillis(); + runTime += end - start; + } + System.out.println(testTime + "次测试总运行时间 : " + runTime + " 毫秒"); + System.out.println("性能测试结束"); + } + +} diff --git a/算法周更班/class_2022_10_3_week/Code04_EveryNodePickMostkEdgesMaxValue.java b/算法周更班/class_2022_10_3_week/Code04_EveryNodePickMostkEdgesMaxValue.java new file mode 100644 index 0000000..10533e3 --- /dev/null +++ b/算法周更班/class_2022_10_3_week/Code04_EveryNodePickMostkEdgesMaxValue.java @@ -0,0 +1,213 @@ +package class_2022_10_3_week; + +import java.util.ArrayList; +import java.util.Arrays; + +// 来自Lucid Air +// 给定一个无向图,保证所有节点连成一棵树,没有环 +// 给定一个正数n为节点数,所以节点编号为0~n-1,那么就一定有n-1条边 +// 每条边形式为{a, b, w},意思是a和b之间的无向边,权值为w +// 要求:给定一个正数k,表示在挑选之后,每个点相连的边,数量都不能超过k +// 注意:是每个点的连接数量,都不超过k!不是总连接数量不能超过k! +// 你可以随意挑选边留下,剩下的边删掉,但是要满足上面的要求 +// 返回不违反要求的情况下,你挑选边所能达到的最大权值累加和 +public class Code04_EveryNodePickMostkEdgesMaxValue { + +// public static class Edge { +// public int to; +// public int weight; +// +// public Edge(int t, int w) { +// to = t; +// weight = w; +// } +// } +// +// public static int bestSum(int n, int[][] edges, int k) { +// ArrayList> graph = new ArrayList<>(); +// for (int i = 0; i < n; i++) { +// graph.add(new ArrayList<>()); +// } +// for (int[] edge : edges) { +// int a = edge[0]; +// int b = edge[1]; +// int w = edge[2]; +// graph.get(a).add(new Edge(b, w)); +// graph.get(b).add(new Edge(a, w)); +// } +// Info info = process(graph, 0, -1); +// return info.no; +// } +// +// public static class Info { +// public int no; +// public int yes; +// +// public Info(int a, int b) { +// no = a; +// yes = b; +// } +// } +// +// public static Info process(ArrayList> graph, int cur, int father) { +// ArrayList childsInfo = new ArrayList<>(); +// for (Edge edge : graph.get(cur)) { +// if (edge.to != father) { +// childsInfo.add(process(graph, edge.to, cur)); +// } +// } +// // 孩子所有的信息,都在childsInfo +// // 整合了! +// // 父亲不跟当前节点相连 +// int no = 0; +// // 先把所有后代不连的加起来 +// // 挑选k个加成最大的 +// // 加成:连的边 + yes - no +// int yes = 0; +// // 先把所有后代不连的加起来 +// // 挑选k-1个加成最大的 +// // 加成:连的边 + yes - no +// +// return new Info(no, yes); +// } + + // 暴力方法 + // 为了验证 + public static int maxSum1(int n, int k, int[][] edges) { + return process(edges, 0, new boolean[edges.length], n, k); + } + + public static int process(int[][] edges, int i, boolean[] pick, int n, int k) { + if (i == edges.length) { + int[] cnt = new int[n]; + int ans = 0; + for (int j = 0; j < edges.length; j++) { + if (pick[j]) { + cnt[edges[j][0]]++; + cnt[edges[j][1]]++; + ans += edges[j][2]; + } + } + for (int j = 0; j < n; j++) { + if (cnt[j] > k) { + return -1; + } + } + return ans; + } else { + pick[i] = true; + int p1 = process(edges, i + 1, pick, n, k); + pick[i] = false; + int p2 = process(edges, i + 1, pick, n, k); + return Math.max(p1, p2); + } + } + + // 最优解 + // 时间复杂度O(N * logN) + public static int[][] dp = new int[100001][2]; + + public static int[] help = new int[100001]; + + public static int maxSum2(int n, int k, int[][] edges) { + ArrayList> graph = new ArrayList<>(); + for (int i = 0; i < n; i++) { + graph.add(new ArrayList<>()); + } + for (int[] edge : edges) { + int a = edge[0]; + int b = edge[1]; + int c = edge[2]; + graph.get(a).add(new int[] { b, c }); + graph.get(b).add(new int[] { a, c }); + } + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + dfs(0, -1, k, graph); + return dp[0][0]; + } + + public static void dfs(int cur, int father, int k, ArrayList> graph) { + ArrayList edges = graph.get(cur); + for (int i = 0; i < edges.size(); i++) { + int next = edges.get(i)[0]; + if (next != father) { + dfs(next, cur, k, graph); + } + } + int ans0 = 0; + int ans1 = 0; + int m = 0; + for (int i = 0; i < edges.size(); i++) { + int next = edges.get(i)[0]; + int weight = edges.get(i)[1]; + if (next != father) { + ans0 += dp[next][0]; + ans1 += dp[next][0]; + if (dp[next][0] < dp[next][1] + weight) { + help[m++] = dp[next][1] + weight - dp[next][0]; + } + } + } + Arrays.sort(help, 0, m); + for (int i = m - 1, cnt = 1; i >= 0 && cnt <= k; i--, cnt++) { + if (cnt <= k - 1) { + ans0 += help[i]; + ans1 += help[i]; + } + if (cnt == k) { + ans0 += help[i]; + } + } + dp[cur][0] = ans0; + dp[cur][1] = ans1; + } + + // 为了测试 + // 生成无环无向图 + public static int[][] randomEdges(int n, int v) { + int[] order = new int[n]; + for (int i = 0; i < n; i++) { + order[i] = i; + } + for (int i = n - 1; i >= 0; i--) { + swap(order, i, (int) (Math.random() * (i + 1))); + } + int[][] edges = new int[n - 1][3]; + for (int i = 1; i < n; i++) { + edges[i - 1][0] = order[i]; + edges[i - 1][1] = order[(int) (Math.random() * i)]; + edges[i - 1][2] = (int) (Math.random() * v) + 1; + } + return edges; + } + + // 为了测试 + public static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + // 为了测试 + public static void main(String[] args) { + int N = 16; + int V = 50; + int testTimes = 2000; + System.out.println("测试开始"); + for (int i = 0; i < testTimes; i++) { + int n = (int) (Math.random() * N) + 1; + int k = (int) (Math.random() * n) + 1; + int[][] edges = randomEdges(n, V); + int ans1 = maxSum1(n, k, edges); + int ans2 = maxSum2(n, k, edges); + if (ans1 != ans2) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/算法周更班/class_2022_10_3_week/Code05_RemoveSubstringRestPalindrome.java b/算法周更班/class_2022_10_3_week/Code05_RemoveSubstringRestPalindrome.java new file mode 100644 index 0000000..8d89855 --- /dev/null +++ b/算法周更班/class_2022_10_3_week/Code05_RemoveSubstringRestPalindrome.java @@ -0,0 +1,163 @@ +package class_2022_10_3_week; + +// 给定一个字符串str +// 如果删掉连续一段子串,剩下的字符串拼接起来是回文串 +// 那么该删除叫做有效的删除 +// 返回有多少种有效删除 +// 注意 : 不能全删除,删成空串不允许 +// 字符串长度 <= 3000 +public class Code05_RemoveSubstringRestPalindrome { + // 暴力方法 + // 时间复杂度O(N^3) + // 为了验证而写,不是正式方法 + // 删掉每一个可能的子串 + // 然后验证剩下的子串是不是回文串 + public static int good1(String str) { + int n = str.length(); + int ans = 0; + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (isPalindrome(str.substring(0, i) + str.substring(j + 1))) { + ans++; + } + } + } + return ans - 1; + } + + public static boolean isPalindrome(String str) { + for (int l = 0, r = str.length() - 1; l <= r; l++, r--) { + if (str.charAt(l) != str.charAt(r)) { + return false; + } + } + return true; + } + + // 正式方法 + // 时间复杂度O(N^2) + public static int good2(String str) { + if (str.length() == 1) { + return 0; + } + // O(N) + int[] pArr = manacher(str); + char[] s = str.toCharArray(); + int n = s.length; + // 左右逆序对应的最大长度 + int range = 0; + for (int l = 0, r = n - 1; l <= r && s[l] == s[r]; l++, r--) { + range++; + } + int ans = 0; + for (int l = 0; l < n; l++) { + for (int r = l; r < n; r++) { + if (l < n - r - 1) { + if (range >= l && check(pArr, r + 1, n - l - 1)) { + ans++; + } + } else if (l > n - r - 1) { + if (range >= n - r - 1 && check(pArr, n - r - 1, l - 1)) { + ans++; + } + } else { + if (range >= l) { + ans++; + } + } + } + } + return ans - 1; + } + + public static int[] manacher(String s) { + char[] str = manacherString(s); + int[] pArr = new int[str.length]; + int C = -1; + int R = -1; + for (int i = 0; i < str.length; i++) { // 0 1 2 + pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1; + while (i + pArr[i] < str.length && i - pArr[i] > -1) { + if (str[i + pArr[i]] == str[i - pArr[i]]) + pArr[i]++; + else { + break; + } + } + if (i + pArr[i] > R) { + R = i + pArr[i]; + C = i; + } + } + return pArr; + } + + public static char[] manacherString(String str) { + char[] charArr = str.toCharArray(); + char[] res = new char[str.length() * 2 + 1]; + int index = 0; + for (int i = 0; i != res.length; i++) { + res[i] = (i & 1) == 0 ? '#' : charArr[index++]; + } + return res; + } + + // 根据原字符串的回文半径数组,判断原字符串l...r这一段是不是回文 + public static boolean check(int[] pArr, int l, int r) { + int n = r - l + 1; + l = l * 2 + 1; + r = r * 2 + 1; + return pArr[(l + r) / 2] - 1 >= n; + } + + // 为了测试 + public static String randomString(int n, int v) { + char[] str = new char[n]; + for (int i = 0; i < n; i++) { + str[i] = (char) ((int) (Math.random() * v) + 'a'); + } + return String.valueOf(str); + } + + // 为了测试 + public static void main(String[] args) { + int N = 50; + int V = 3; + int testTime = 5000; + System.out.println("功能测试开始"); + for (int i = 0; i < testTime; i++) { + int n = (int) (Math.random() * N) + 1; + String str = randomString(n, V); + int ans1 = good1(str); + int ans2 = good2(str); + if (ans1 != ans2) { + System.out.println("出错了!"); + System.out.println(ans1); + System.out.println(ans2); + break; + } + } + System.out.println("功能测试结束"); + + System.out.println("性能测试开始"); + int n = 3000; + int v = 26; + String str = randomString(n, v); + System.out.println("字符串的长度 : " + n); + System.out.println("小写字符种类 : " + v); + long start; + long end; + start = System.currentTimeMillis(); + int ans1 = good1(str); + end = System.currentTimeMillis(); + System.out.println("方法1(暴力方法)答案 : " + ans1); + System.out.println("方法1(暴力方法)运行时间 : " + (end - start) + " 毫秒"); + start = System.currentTimeMillis(); + int ans2 = good2(str); + end = System.currentTimeMillis(); + System.out.println("方法2(正式方法)答案 : " + ans2); + System.out.println("方法2(正式方法)运行时间 : " + (end - start) + " 毫秒"); + System.out.println("性能测试结束"); + } + +} diff --git a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) index 348c91b..11e37a0 100644 --- a/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) +++ b/算法课堂笔记/课堂内容汇总/每周有营养的大厂算法面试题(正在直播) @@ -2183,7 +2183,58 @@ n <= 1000 +第045节 2022年10月第3周流行算法题目解析 +给定一个整数数组 A,坡是元组 (i, j),其中  i < j 且 A[i] <= A[j] +这样的坡的宽度为 j - i +找出 A 中的坡的最大宽度,如果不存在,返回 0 +示例 1: +输入:[6,0,8,2,1,5] +输出:4 +解释: +最大宽度的坡为 (i, j) = (1, 5): A[1] = 0 且 A[5] = 5 +示例 2: +输入:[9,8,1,0,1,9,4,0,4,1] +输出:7 +解释: +最大宽度的坡为 (i, j) = (2, 9): A[2] = 1 且 A[9] = 1 +测试链接 : https://leetcode.cn/problems/maximum-width-ramp/ + +给定一个由 0 和 1 组成的数组 arr ,将数组分成 3 个非空的部分 +使得所有这些部分表示相同的二进制值。 +如果可以做到,请返回任何 [i, j],其中 i+1 < j,这样一来 +arr[0], arr[1], ..., arr[i] 为第一部分 +arr[i + 1], arr[i + 2], ..., arr[j - 1] 为第二部分 +arr[j], arr[j + 1], ..., arr[arr.length - 1] 为第三部分 +这三个部分所表示的二进制值相等 +如果无法做到,就返回 [-1, -1] +注意,在考虑每个部分所表示的二进制时,应当将其看作一个整体 +例如,[1,1,0] 表示十进制中的 6,而不会是 3。此外,前导零也是被允许的 +所以 [0,1,1] 和 [1,1] 表示相同的值。 +测试链接 : https://leetcode.cn/problems/three-equal-parts/ + +来自阿里 +给定一个长度n的数组,每次可以选择一个数x +让这个数组中所有的x都变成x+1,问你最少的操作次数 +使得这个数组变成一个非降数组 +n <= 3 * 10^5 +0 <= 数值 <= 10^9 + +来自Lucid Air +给定一个无向图,保证所有节点连成一棵树,没有环 +给定一个正数n为节点数,所以节点编号为0~n-1,那么就一定有n-1条边 +每条边形式为{a, b, w},意思是a和b之间的无向边,权值为w +要求:给定一个正数k,表示在挑选之后,每个点相连的边,数量都不能超过k +注意:是每个点的连接数量,都不超过k!不是总连接数量不能超过k! +你可以随意挑选边留下,剩下的边删掉,但是要满足上面的要求 +返回不违反要求的情况下,你挑选边所能达到的最大权值累加和 + +给定一个字符串str +如果删掉连续一段子串,剩下的字符串拼接起来是回文串 +那么该删除叫做有效的删除 +返回有多少种有效删除 +注意 : 不能全删除,删成空串不允许 +字符串长度 <= 3000