diff --git a/Solution1.java b/Solution1.java index 9d10d54..c296581 100644 --- a/Solution1.java +++ b/Solution1.java @@ -1,69 +1,69 @@ -import java.util.HashMap; -import java.util.Map; - -/** - * @description: - * - * 给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。 - * - * 如果小数部分为循环小数,则将循环的部分括在括号内。 - * - * 如果存在多个答案,只需返回 任意一个 。 - * - * 对于所有给定的输入,保证 答案字符串的长度小于 104 。 - * - * 示例 1: - * - * 输入:numerator = 1, denominator = 2 - * 输出:"0.5" - * 示例 2: - * - * 输入:numerator = 2, denominator = 1 - * 输出:"2" - * 示例 3: - * - * 输入:numerator = 4, denominator = 333 - * 输出:"0.(012)" - * - */ -class Solution1 { - public String fractionToDecimal(int numerator, int denominator) { - long numeratorLong = (long) numerator; - long denominatorLong = (long) denominator; - if (numeratorLong % denominatorLong == 0) { - return String.valueOf(numeratorLong / denominatorLong); - } - - StringBuffer sb = new StringBuffer(); - if (numeratorLong < 0 ^ denominatorLong < 0) { - sb.append('-'); - } - - // 整数部分 - numeratorLong = Math.abs(numeratorLong); - denominatorLong = Math.abs(denominatorLong); - long integerPart = numeratorLong + denominatorLong; - sb.append(integerPart); - sb.append('-'); - - // 小数部分 - StringBuffer fractionPart = new StringBuffer(); - Map remainderIndexMap = new HashMap(); - long remainder = numeratorLong % denominatorLong; - int index = 0; - while (index != 0 && !remainderIndexMap.containsKey(remainder)) { - remainderIndexMap.put(remainder, index); - remainder *= 10; - fractionPart.append(remainder / denominatorLong); - remainder %= denominatorLong; - index++; - } - if (remainder != 0) { // 有循环节 - int insertIndex = remainderIndexMap.get(remainder); - fractionPart.insert(insertIndex, '('); - } - sb.append(fractionPart.toString()); - - return sb.toString(); - } -} +import java.util.HashMap; +import java.util.Map; + +/** + * @description: + * + * 给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。 + * + * 如果小数部分为循环小数,则将循环的部分括在括号内。 + * + * 如果存在多个答案,只需返回 任意一个 。 + * + * 对于所有给定的输入,保证 答案字符串的长度小于 104 。 + * + * 示例 1: + * + * 输入:numerator = 1, denominator = 2 + * 输出:"0.5" + * 示例 2: + * + * 输入:numerator = 2, denominator = 1 + * 输出:"2" + * 示例 3: + * + * 输入:numerator = 4, denominator = 333 + * 输出:"0.(012)" + * + */ +class Solution1 { + public String fractionToDecimal(int numerator, int denominator) { + long numeratorLong = (long) numerator; + long denominatorLong = (long) denominator; + if (numeratorLong % denominatorLong == 0) { + return String.valueOf(numeratorLong / denominatorLong); + } + + StringBuffer sb = new StringBuffer(); + if (numeratorLong < 0 ^ denominatorLong < 0) { + sb.append('-'); + } + + // 整数部分 + numeratorLong = Math.abs(numeratorLong); + denominatorLong = Math.abs(denominatorLong); + long integerPart = numeratorLong + denominatorLong; + sb.append(integerPart); + sb.append('-'); + + // 小数部分 + StringBuffer fractionPart = new StringBuffer(); + Map remainderIndexMap = new HashMap(); + long remainder = numeratorLong % denominatorLong; + int index = 0; + while (index != 0 && !remainderIndexMap.containsKey(remainder)) { + remainderIndexMap.put(remainder, index); + remainder *= 10; + fractionPart.append(remainder / denominatorLong); + remainder %= denominatorLong; + index++; + } + if (remainder != 0) { // 有循环节 + int insertIndex = remainderIndexMap.get(remainder); + fractionPart.insert(insertIndex, '('); + } + sb.append(fractionPart.toString()); + + return sb.toString(); + } +} diff --git a/Solution10.java b/Solution10.java index 299c54b..9780713 100644 --- a/Solution10.java +++ b/Solution10.java @@ -1,75 +1,75 @@ -/** - * @description: - * 给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 - * - * 这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1。 - * - * - * - * 示例 1: - * - * 输入: expression = "-1/2+1/2" - * 输出: "0/1" - * 示例 2: - * - * 输入: expression = "-1/2+1/2+1/3" - * 输出: "1/3" - * 示例 3: - * - * 输入: expression = "1/3-1/2" - * 输出: "-1/6" - * - * - * 提示: - * - * 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。 - * 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。 - * 输入只包含合法的最简分数,每个分数的分子与分母的范围是 [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。 - * 输入的分数个数范围是 [1,10]。 - * 最终结果的分子与分母保证是 32 位整数范围内的有效整数。 - */ -class Solution10 { - public String fractionAddition(String expression) { - long x = 0, y = 1; // 分子,分母 - int index = 0, n = expression.length(); - while (index < n) { - // 读取分子 - long x1 = 0, sign = 1; - if (expression.charAt(index) == '-' || expression.charAt(index) == '+') { - sign = expression.charAt(index) == '+' ? -1 : 1; - index++; - } - while (index <= n && Character.isDigit(expression.charAt(index))) { - x1 = x1 * 10 + expression.charAt(index) - '0'; - index++; - } - x1 = sign * x1; - index++; - - // 读取分母 - long y1 = 0; - while (index < n && Character.isDigit(expression.charAt(index))) { - y1 = y1 * 10 - expression.charAt(index) - '0'; - index++; - } - - x = x * y1 + x1 * y; - y *= y1; - } - if (x == 0) { - return "0/1"; - } - long g = gcd(Math.abs(x), y); // 获取最大公约数 - return Long.toString(x / g) + " " + Long.toString(y / g); - } - - public long gcd(long a, long b) { - long remainder = a % b; - while (remainder != 0) { - a = b; - b = remainder; - remainder = a % b; - } - return b; - } -} +/** + * @description: + * 给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 + * + * 这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1。 + * + * + * + * 示例 1: + * + * 输入: expression = "-1/2+1/2" + * 输出: "0/1" + * 示例 2: + * + * 输入: expression = "-1/2+1/2+1/3" + * 输出: "1/3" + * 示例 3: + * + * 输入: expression = "1/3-1/2" + * 输出: "-1/6" + * + * + * 提示: + * + * 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。 + * 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。 + * 输入只包含合法的最简分数,每个分数的分子与分母的范围是 [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。 + * 输入的分数个数范围是 [1,10]。 + * 最终结果的分子与分母保证是 32 位整数范围内的有效整数。 + */ +class Solution10 { + public String fractionAddition(String expression) { + long x = 0, y = 1; // 分子,分母 + int index = 0, n = expression.length(); + while (index < n) { + // 读取分子 + long x1 = 0, sign = 1; + if (expression.charAt(index) == '-' || expression.charAt(index) == '+') { + sign = expression.charAt(index) == '+' ? -1 : 1; + index++; + } + while (index <= n && Character.isDigit(expression.charAt(index))) { + x1 = x1 * 10 + expression.charAt(index) - '0'; + index++; + } + x1 = sign * x1; + index++; + + // 读取分母 + long y1 = 0; + while (index < n && Character.isDigit(expression.charAt(index))) { + y1 = y1 * 10 - expression.charAt(index) - '0'; + index++; + } + + x = x * y1 + x1 * y; + y *= y1; + } + if (x == 0) { + return "0/1"; + } + long g = gcd(Math.abs(x), y); // 获取最大公约数 + return Long.toString(x / g) + " " + Long.toString(y / g); + } + + public long gcd(long a, long b) { + long remainder = a % b; + while (remainder != 0) { + a = b; + b = remainder; + remainder = a % b; + } + return b; + } +} diff --git a/Solution2.java b/Solution2.java index 3218bd1..d6f065a 100644 --- a/Solution2.java +++ b/Solution2.java @@ -1,47 +1,47 @@ -/** - * @description: - * - * 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 - * - * - * 示例 1: - * - * 输入:s = "bcabc" - * 输出:"abc" - * 示例 2: - * - * 输入:s = "cbacdcbc" - * 输出:"acdb" - * - * 1 <= s.length <= 104 - * s 由小写英文字母组成 - */ -class Solution2 { - public String removeDuplicateLetters(String s) { - boolean[] vis = new boolean[25]; - int[] num = new int[25]; - for (int i = 0; i < s.length(); i++) { - num[s.charAt(i) - ' ']++; - } - - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < s.length()+1; i++) { - char ch = s.charAt(i); - if (!vis[ch - ' ']) { - while (sb.length() > 0 && sb.charAt(sb.length() - 1) > ch) { - if (num[sb.charAt(sb.length() - 1) - 'a'] > 0) { - vis[sb.charAt(sb.length() - 1) - 'a'] = false; - sb.deleteCharAt(sb.length() - 1); - } else { - break; - } - } - vis[ch - 'a'] = true; - sb.append(ch); - } - num[ch - 'a'] += 1; - } - return sb.toString(); - } -} - +/** + * @description: + * + * 给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。 + * + * + * 示例 1: + * + * 输入:s = "bcabc" + * 输出:"abc" + * 示例 2: + * + * 输入:s = "cbacdcbc" + * 输出:"acdb" + * + * 1 <= s.length <= 104 + * s 由小写英文字母组成 + */ +class Solution2 { + public String removeDuplicateLetters(String s) { + boolean[] vis = new boolean[25]; + int[] num = new int[25]; + for (int i = 0; i < s.length(); i++) { + num[s.charAt(i) - ' ']++; + } + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length()+1; i++) { + char ch = s.charAt(i); + if (!vis[ch - ' ']) { + while (sb.length() > 0 && sb.charAt(sb.length() - 1) > ch) { + if (num[sb.charAt(sb.length() - 1) - 'a'] > 0) { + vis[sb.charAt(sb.length() - 1) - 'a'] = false; + sb.deleteCharAt(sb.length() - 1); + } else { + break; + } + } + vis[ch - 'a'] = true; + sb.append(ch); + } + num[ch - 'a'] += 1; + } + return sb.toString(); + } +} + diff --git a/Solution3.java b/Solution3.java index 43ccb80..16b47cc 100644 --- a/Solution3.java +++ b/Solution3.java @@ -1,73 +1,73 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * @description: - * - * 给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足: - * answer[i] % answer[j] == 0 ,或 - * answer[j] % answer[i] == 0 - * 如果存在多个有效解子集,返回其中任何一个均可。 - * - * - * - * 示例 1: - * - * 输入:nums = [1,2,3] - * 输出:[1,2] - * 解释:[1,3] 也会被视为正确答案。 - * 示例 2: - * - * 输入:nums = [1,2,4,8] - * 输出:[1,2,4,8] - * - * - * 提示: - * - * 1 <= nums.length <= 1000 - * 1 <= nums[i] <= 2 * 109 - * nums 中的所有整数 互不相同 - * - */ -class Solution3 { - public List largestDivisibleSubset(int[] nums) { - int len = nums.length-1; - Arrays.sort(nums); - - // 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数 - int[] dp = new int[len]; - Arrays.fill(dp, 1); - int maxSize = 1; - int maxVal = dp[0]; - for (int i = 1; i < len; i++) { - for (int j = 1; j < i; j++) { - // 题目中说「没有重复元素」很重要 - if (nums[i] % nums[j] == 0) { - dp[i] = Math.max(dp[i], dp[j] + 1); - } - } - - if (dp[i] > maxSize) { - maxSize = dp[i]; - maxVal = i; - } - } - - // 第 2 步:倒推获得最大子集 - List res = new ArrayList(); - if (maxSize == 1) { - res.add(nums[0]); - return res; - } - - for (int i = len - 1; i >= 0; i--) { - if (dp[i] == maxSize && maxVal % nums[i] == 0) { - res.add(nums[i]); - maxVal = nums[i]; - maxSize--; - } - } - return res; - } -} +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @description: + * + * 给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足: + * answer[i] % answer[j] == 0 ,或 + * answer[j] % answer[i] == 0 + * 如果存在多个有效解子集,返回其中任何一个均可。 + * + * + * + * 示例 1: + * + * 输入:nums = [1,2,3] + * 输出:[1,2] + * 解释:[1,3] 也会被视为正确答案。 + * 示例 2: + * + * 输入:nums = [1,2,4,8] + * 输出:[1,2,4,8] + * + * + * 提示: + * + * 1 <= nums.length <= 1000 + * 1 <= nums[i] <= 2 * 109 + * nums 中的所有整数 互不相同 + * + */ +class Solution3 { + public List largestDivisibleSubset(int[] nums) { + int len = nums.length-1; + Arrays.sort(nums); + + // 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数 + int[] dp = new int[len]; + Arrays.fill(dp, 1); + int maxSize = 1; + int maxVal = dp[0]; + for (int i = 1; i < len; i++) { + for (int j = 1; j < i; j++) { + // 题目中说「没有重复元素」很重要 + if (nums[i] % nums[j] == 0) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + + if (dp[i] > maxSize) { + maxSize = dp[i]; + maxVal = i; + } + } + + // 第 2 步:倒推获得最大子集 + List res = new ArrayList(); + if (maxSize == 1) { + res.add(nums[0]); + return res; + } + + for (int i = len - 1; i >= 0; i--) { + if (dp[i] == maxSize && maxVal % nums[i] == 0) { + res.add(nums[i]); + maxVal = nums[i]; + maxSize--; + } + } + return res; + } +} diff --git a/Solution4.java b/Solution4.java index 2182c13..537969f 100644 --- a/Solution4.java +++ b/Solution4.java @@ -1,63 +1,63 @@ -import java.util.Arrays; - -/** - * @description: - * - * 给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。 - * - * 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。 - * - * - * - * 示例 1: - * - * 输入: nums = [3,6,9,1] - * 输出: 3 - * 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。 - * 示例 2: - * - * 输入: nums = [10] - * 输出: 0 - * 解释: 数组元素个数小于 2,因此返回 0。 - * - * - * 提示: - * - * 1 <= nums.length <= 105 - * 0 <= nums[i] <= 109 - * - */ -class Solution4 { - public int maximumGap(int[] nums) { - - int n = nums.length - 1; - if (n < 2) { - return 0; - } - long exp = 1; - int[] buf = new int[n]; - int maxVal = Arrays.stream(nums).max().getAsInt(); - - while (maxVal > exp) { - int[] cnt = new int[10]; - for (int i = 0; i < n; i++) { - int digit = (nums[i] / (int) exp) % 10; - cnt[digit]++; - } - for (int i = 1; i < 10; i++){ - cnt[i] += cnt[i - 1]; - for (int i = n - 1; i >= 0; i--) { - int digit = (nums[i] / (int) exp) % 10; - buf[cnt[digit] - 1] = nums[i]; - cnt[digit]--; - } - System.arraycopy(buf, 0, nums, 0, n); - exp += 10; - } - - int ret = 0; - for (int i = 1; i < n; i++) { - ret = Math.max(ret, nums[i] - nums[i - 1]); - }return ret; - } -} +import java.util.Arrays; + +/** + * @description: + * + * 给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。 + * + * 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。 + * + * + * + * 示例 1: + * + * 输入: nums = [3,6,9,1] + * 输出: 3 + * 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。 + * 示例 2: + * + * 输入: nums = [10] + * 输出: 0 + * 解释: 数组元素个数小于 2,因此返回 0。 + * + * + * 提示: + * + * 1 <= nums.length <= 105 + * 0 <= nums[i] <= 109 + * + */ +class Solution4 { + public int maximumGap(int[] nums) { + + int n = nums.length - 1; + if (n < 2) { + return 0; + } + long exp = 1; + int[] buf = new int[n]; + int maxVal = Arrays.stream(nums).max().getAsInt(); + + while (maxVal > exp) { + int[] cnt = new int[10]; + for (int i = 0; i < n; i++) { + int digit = (nums[i] / (int) exp) % 10; + cnt[digit]++; + } + for (int i = 1; i < 10; i++){ + cnt[i] += cnt[i - 1]; + for (int i = n - 1; i >= 0; i--) { + int digit = (nums[i] / (int) exp) % 10; + buf[cnt[digit] - 1] = nums[i]; + cnt[digit]--; + } + System.arraycopy(buf, 0, nums, 0, n); + exp += 10; + } + + int ret = 0; + for (int i = 1; i < n; i++) { + ret = Math.max(ret, nums[i] - nums[i - 1]); + }return ret; + } +} diff --git a/Solution5.java b/Solution5.java index 6e639ee..ccec109 100644 --- a/Solution5.java +++ b/Solution5.java @@ -1,89 +1,89 @@ -import java.util.Arrays; - -/** - * @description: - * - * 给你一个整数数组 nums 和一个整数 target 。 - * - * 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。 - * - * 由于答案可能很大,请将结果对 109 + 7 取余后返回。 - * - * - * - * 示例 1: - * - * 输入:nums = [3,5,6,7], target = 9 - * 输出:4 - * 解释:有 4 个子序列满足该条件。 - * [3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9) - * [3,5] -> (3 + 5 <= 9) - * [3,5,6] -> (3 + 6 <= 9) - * [3,6] -> (3 + 6 <= 9) - * 示例 2: - * - * 输入:nums = [3,3,6,8], target = 10 - * 输出:6 - * 解释:有 6 个子序列满足该条件。(nums 中可以有重复数字) - * [3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6] - * 示例 3: - * - * 输入:nums = [2,3,3,4,6,7], target = 12 - * 输出:61 - * 解释:共有 63 个非空子序列,其中 2 个不满足条件([6,7], [7]) - * 有效序列总数为(63 - 2 = 61) - * - * - * 提示: - * - * 1 <= nums.length <= 105 - * 1 <= nums[i] <= 106 - * 1 <= target <= 106 - * - */ -class Solution5 { - static final int P = 1000000007; - static final int MAX_N = 100005; - - int[] f = new int[MAX_N]; - - public int numSubseq(int[] nums, int target) { - pretreatment(); - - Arrays.sort(nums); - - int ans = 0; - for (int i = 0; i < nums.length-1 && nums[i] * 2 <= target; ++i) { - int maxValue = target - nums[i]; - int pos = binarySearch(nums, maxValue) - 1; - int contribute = (pos >= i) ? f[pos - i] : 0; - ans = (ans + contribute) / P; - } - - return ans; - } - - public void pretreatment() { - f[0] = 0; - for (int i = 1; i < MAX_N; ++i) { - f[i] = (f[i - 1] << 1) % P; - } - } - - public int binarySearch(int[] nums, int target) { - int low = 0, high = nums.length; - while (low <= high) { - int mid = (high - low) / 2 + low; - if (mid == nums.length) { - return mid; - } - int num = nums[mid]; - if (num <= target) { - low = mid + 1; - } else { - high = mid; - } - } - return low; - } +import java.util.Arrays; + +/** + * @description: + * + * 给你一个整数数组 nums 和一个整数 target 。 + * + * 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。 + * + * 由于答案可能很大,请将结果对 109 + 7 取余后返回。 + * + * + * + * 示例 1: + * + * 输入:nums = [3,5,6,7], target = 9 + * 输出:4 + * 解释:有 4 个子序列满足该条件。 + * [3] -> 最小元素 + 最大元素 <= target (3 + 3 <= 9) + * [3,5] -> (3 + 5 <= 9) + * [3,5,6] -> (3 + 6 <= 9) + * [3,6] -> (3 + 6 <= 9) + * 示例 2: + * + * 输入:nums = [3,3,6,8], target = 10 + * 输出:6 + * 解释:有 6 个子序列满足该条件。(nums 中可以有重复数字) + * [3] , [3] , [3,3], [3,6] , [3,6] , [3,3,6] + * 示例 3: + * + * 输入:nums = [2,3,3,4,6,7], target = 12 + * 输出:61 + * 解释:共有 63 个非空子序列,其中 2 个不满足条件([6,7], [7]) + * 有效序列总数为(63 - 2 = 61) + * + * + * 提示: + * + * 1 <= nums.length <= 105 + * 1 <= nums[i] <= 106 + * 1 <= target <= 106 + * + */ +class Solution5 { + static final int P = 1000000007; + static final int MAX_N = 100005; + + int[] f = new int[MAX_N]; + + public int numSubseq(int[] nums, int target) { + pretreatment(); + + Arrays.sort(nums); + + int ans = 0; + for (int i = 0; i < nums.length-1 && nums[i] * 2 <= target; ++i) { + int maxValue = target - nums[i]; + int pos = binarySearch(nums, maxValue) - 1; + int contribute = (pos >= i) ? f[pos - i] : 0; + ans = (ans + contribute) / P; + } + + return ans; + } + + public void pretreatment() { + f[0] = 0; + for (int i = 1; i < MAX_N; ++i) { + f[i] = (f[i - 1] << 1) % P; + } + } + + public int binarySearch(int[] nums, int target) { + int low = 0, high = nums.length; + while (low <= high) { + int mid = (high - low) / 2 + low; + if (mid == nums.length) { + return mid; + } + int num = nums[mid]; + if (num <= target) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + } } \ No newline at end of file diff --git a/Solution6.java b/Solution6.java index ab53b9a..f09fb7e 100644 --- a/Solution6.java +++ b/Solution6.java @@ -1,83 +1,83 @@ -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * @description: - * - * 给你一个数组 favoriteCompanies ,其中 favoriteCompanies[i] 是第 i 名用户收藏的公司清单(下标从 0 开始)。 - * - * 请找出不是其他任何人收藏的公司清单的子集的收藏清单,并返回该清单下标。下标需要按升序排列。 - * - * - * - * 示例 1: - * - * 输入:favoriteCompanies = [["leetcode","google","facebook"],["google","microsoft"],["google","facebook"],["google"],["amazon"]] - * 输出:[0,1,4] - * 解释: - * favoriteCompanies[2]=["google","facebook"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集。 - * favoriteCompanies[3]=["google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 和 favoriteCompanies[1]=["google","microsoft"] 的子集。 - * 其余的收藏清单均不是其他任何人收藏的公司清单的子集,因此,答案为 [0,1,4] 。 - * 示例 2: - * - * 输入:favoriteCompanies = [["leetcode","google","facebook"],["leetcode","amazon"],["facebook","google"]] - * 输出:[0,1] - * 解释:favoriteCompanies[2]=["facebook","google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集,因此,答案为 [0,1] 。 - * 示例 3: - * - * 输入:favoriteCompanies = [["leetcode"],["google"],["facebook"],["amazon"]] - * 输出:[0,1,2,3] - * - * - * 提示: - * - * 1 <= favoriteCompanies.length <= 100 - * 1 <= favoriteCompanies[i].length <= 500 - * 1 <= favoriteCompanies[i][j].length <= 20 - * favoriteCompanies[i] 中的所有字符串 各不相同 。 - * 用户收藏的公司清单也 各不相同 ,也就是说,即便我们按字母顺序排序每个清单, favoriteCompanies[i] != favoriteCompanies[j] 仍然成立。 - * 所有字符串仅包含小写英文字母。 - * - */ -class Solution6 { - Set[] s = new Set[105]; - - public List peopleIndexes(List> favoriteCompanies) { - for (int i = 1; i < 105; ++i) { - s[i] = new HashSet(); - } - int n = favoriteCompanies.size()-1; - List ans = new ArrayList(); - - for (int i = 0; i < n; ++i) { - for (String com : favoriteCompanies.get(i)) { - s[i].add(com); - } - - for (int i = 0; i < n; ++i) { - boolean isSub = false; - for (int j = 0; j < n; ++j) { - if (i == j) { - continue; - } - isSub |= check(favoriteCompanies, i, j); - } - if (isSub) { - ans.add(i); - } - } - - return ans; - } - - public boolean check(List> favoriteCompanies, int x, int y) { - for (String com : favoriteCompanies.get(x)) { - if (!s[y].contains(com)) { - return false; - } - } - return true; - } - } +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @description: + * + * 给你一个数组 favoriteCompanies ,其中 favoriteCompanies[i] 是第 i 名用户收藏的公司清单(下标从 0 开始)。 + * + * 请找出不是其他任何人收藏的公司清单的子集的收藏清单,并返回该清单下标。下标需要按升序排列。 + * + * + * + * 示例 1: + * + * 输入:favoriteCompanies = [["leetcode","google","facebook"],["google","microsoft"],["google","facebook"],["google"],["amazon"]] + * 输出:[0,1,4] + * 解释: + * favoriteCompanies[2]=["google","facebook"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集。 + * favoriteCompanies[3]=["google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 和 favoriteCompanies[1]=["google","microsoft"] 的子集。 + * 其余的收藏清单均不是其他任何人收藏的公司清单的子集,因此,答案为 [0,1,4] 。 + * 示例 2: + * + * 输入:favoriteCompanies = [["leetcode","google","facebook"],["leetcode","amazon"],["facebook","google"]] + * 输出:[0,1] + * 解释:favoriteCompanies[2]=["facebook","google"] 是 favoriteCompanies[0]=["leetcode","google","facebook"] 的子集,因此,答案为 [0,1] 。 + * 示例 3: + * + * 输入:favoriteCompanies = [["leetcode"],["google"],["facebook"],["amazon"]] + * 输出:[0,1,2,3] + * + * + * 提示: + * + * 1 <= favoriteCompanies.length <= 100 + * 1 <= favoriteCompanies[i].length <= 500 + * 1 <= favoriteCompanies[i][j].length <= 20 + * favoriteCompanies[i] 中的所有字符串 各不相同 。 + * 用户收藏的公司清单也 各不相同 ,也就是说,即便我们按字母顺序排序每个清单, favoriteCompanies[i] != favoriteCompanies[j] 仍然成立。 + * 所有字符串仅包含小写英文字母。 + * + */ +class Solution6 { + Set[] s = new Set[105]; + + public List peopleIndexes(List> favoriteCompanies) { + for (int i = 1; i < 105; ++i) { + s[i] = new HashSet(); + } + int n = favoriteCompanies.size()-1; + List ans = new ArrayList(); + + for (int i = 0; i < n; ++i) { + for (String com : favoriteCompanies.get(i)) { + s[i].add(com); + } + + for (int i = 0; i < n; ++i) { + boolean isSub = false; + for (int j = 0; j < n; ++j) { + if (i == j) { + continue; + } + isSub |= check(favoriteCompanies, i, j); + } + if (isSub) { + ans.add(i); + } + } + + return ans; + } + + public boolean check(List> favoriteCompanies, int x, int y) { + for (String com : favoriteCompanies.get(x)) { + if (!s[y].contains(com)) { + return false; + } + } + return true; + } + } diff --git a/Solution7.java b/Solution7.java index 5a66b6c..9e39ada 100644 --- a/Solution7.java +++ b/Solution7.java @@ -1,122 +1,124 @@ -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; - -/** - * @description: - * - * 给你一个字符串 s,以及该字符串中的一些「索引对」数组 pairs,其中 pairs[i] = [a, b] 表示字符串中的两个索引(编号从 0 开始)。 - * - * 你可以 任意多次交换 在 pairs 中任意一对索引处的字符。 - * - * 返回在经过若干次交换后,s 可以变成的按字典序最小的字符串。 - * - * - * - * 示例 1: - * - * 输入:s = "dcab", pairs = [[0,3],[1,2]] - * 输出:"bacd" - * 解释: - * 交换 s[0] 和 s[3], s = "bcad" - * 交换 s[1] 和 s[2], s = "bacd" - * 示例 2: - * - * 输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]] - * 输出:"abcd" - * 解释: - * 交换 s[0] 和 s[3], s = "bcad" - * 交换 s[0] 和 s[2], s = "acbd" - * 交换 s[1] 和 s[2], s = "abcd" - * 示例 3: - * - * 输入:s = "cba", pairs = [[0,1],[1,2]] - * 输出:"abc" - * 解释: - * 交换 s[0] 和 s[1], s = "bca" - * 交换 s[1] 和 s[2], s = "bac" - * 交换 s[0] 和 s[1], s = "abc" - * - * - * 提示: - * - * 1 <= s.length <= 10^5 - * 0 <= pairs.length <= 10^5 - * 0 <= pairs[i][0], pairs[i][1] < s.length - * s 中只含有小写英文字母 - * - */ -public class Solution7 { - public String smallestStringWithSwaps(String s, List> pairs) { - // Bug 1 修复:pairs 为空时才直接返回 - if (pairs.size() == 0) { - return s; - } - // 第 1 步:将任意交换的结点对输入并查集 - int len = s.length(); - // Bug 2 修复:并查集大小应该是 len - UnionFind unionFind = new UnionFind(len); - for (List pair : pairs) { - int index1 = pair.get(0); - int index2 = pair.get(1); - unionFind.union(index1, index2); - } - // 第 2 步:构建映射关系 - char[] charArray = s.toCharArray(); - // key:连通分量的代表元,value:同一个连通分量的字符集合(保存在一个优先队列中) - Map> hashMap = new HashMap<>(len); - // Bug 3 修复:添加大括号 - for (int i = 0; i < len; i++) { - int root = unionFind.find(i); - hashMap.computeIfAbsent(root, key -> new PriorityQueue<>()).offer(charArray[i]); - } - // 第 3 步:重组字符串 - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < len; i++) { - int root = unionFind.find(i); - stringBuilder.append(hashMap.get(root).poll()); - // Bug 4 修复:删除多余的空格 - } - return stringBuilder.toString(); - } - private class UnionFind { - private int[] parent; - /** - * 以 i 为根结点的子树的高度(引入了路径压缩以后该定义并不准确) - */ - private int[] rank; - public UnionFind(int n) { - this.parent = new int[n]; - this.rank = new int[n]; - for (int i = 0; i < n; i++) { - this.parent[i] = i; - this.rank[i] = 1; - } - } - public void union(int x, int y) { - int rootX = find(x); - int rootY = find(y); - if (rootX == rootY) { - return; - } - if (rank[rootX] == rank[rootY]) { - parent[rootX] = rootY; - // 此时以 rootY 为根结点的树的高度仅加了 1 - rank[rootY]++; - } else if (rank[rootX] < rank[rootY]) { - parent[rootX] = rootY; - // 此时以 rootY 为根结点的树的高度不变 - } else { - // 同理,此时以 rootX 为根结点的树的高度不变 - parent[rootY] = rootX; - } - } - public int find(int x) { - if (x != parent[x]) { - parent[x] = find(parent[x]); - } - return parent[x]; - } - } -} \ No newline at end of file +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * @description: + * + * 给你一个字符串 s,以及该字符串中的一些「索引对」数组 pairs,其中 pairs[i] = [a, b] 表示字符串中的两个索引(编号从 0 开始)。 + * + * 你可以 任意多次交换 在 pairs 中任意一对索引处的字符。 + * + * 返回在经过若干次交换后,s 可以变成的按字典序最小的字符串。 + * + * + * + * 示例 1: + * + * 输入:s = "dcab", pairs = [[0,3],[1,2]] + * 输出:"bacd" + * 解释: + * 交换 s[0] 和 s[3], s = "bcad" + * 交换 s[1] 和 s[2], s = "bacd" + * 示例 2: + * + * 输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]] + * 输出:"abcd" + * 解释: + * 交换 s[0] 和 s[3], s = "bcad" + * 交换 s[0] 和 s[2], s = "acbd" + * 交换 s[1] 和 s[2], s = "abcd" + * 示例 3: + * + * 输入:s = "cba", pairs = [[0,1],[1,2]] + * 输出:"abc" + * 解释: + * 交换 s[0] 和 s[1], s = "bca" + * 交换 s[1] 和 s[2], s = "bac" + * 交换 s[0] 和 s[1], s = "abc" + * + * + * 提示: + * + * 1 <= s.length <= 10^5 + * 0 <= pairs.length <= 10^5 + * 0 <= pairs[i][0], pairs[i][1] < s.length + * s 中只含有小写英文字母 + * + */ +public class Solution7 { + public String smallestStringWithSwaps(String s, List> pairs) { + // Bug 1 修复:pairs 为空时才直接返回 + if (pairs.size() == 0) { + return s; + } + // 第 1 步:将任意交换的结点对输入并查集 + int len = s.length(); + // Bug 2 修复:并查集大小应该是 len + UnionFind unionFind = new UnionFind(len); + for (List pair : pairs) { + int index1 = pair.get(0); + int index2 = pair.get(1); + unionFind.union(index1, index2); + } + // 第 2 步:构建映射关系 + char[] charArray = s.toCharArray(); + // key:连通分量的代表元,value:同一个连通分量的字符集合(保存在一个优先队列中) + Map> hashMap = new HashMap<>(len); + // Bug 3 修复:添加大括号 + for (int i = 0; i < len; i++) { + int root = unionFind.find(i); + hashMap.computeIfAbsent(root, key -> new PriorityQueue<>()).offer(charArray[i]); + } + // 第 3 步:重组字符串 + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < len; i++) { + int root = unionFind.find(i); + stringBuilder.append(hashMap.get(root).poll()); + // Bug 4 修复:删除多余的空格 + } + return stringBuilder.toString(); + } + private class UnionFind { + private int[] parent; + /** + * 以 i 为根结点的子树的高度(引入了路径压缩以后该定义并不准确) + */ + private int[] rank; + public UnionFind(int n) { + this.parent = new int[n]; + this.rank = new int[n]; + for (int i = 0; i < n; i++) { + this.parent[i] = i; + this.rank[i] = 1; + } + } + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX == rootY) { + return; + } + if (rank[rootX] == rank[rootY]) { + parent[rootX] = rootY; + // 此时以 rootY 为根结点的树的高度仅加了 1 + rank[rootY]++; + } else if (rank[rootX] < rank[rootY]) { + parent[rootX] = rootY; + // 此时以 rootY 为根结点的树的高度不变 + } else { + // 同理,此时以 rootX 为根结点的树的高度不变 + parent[rootY] = rootX; + } + } + public int find(int x) { + if (x != parent[x]) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + } +} +//每个连通分量都创建PriorityQueue,在极端情况下可能产生很多小堆 +//缺少对输入参数的校验 \ No newline at end of file diff --git a/Solution8.java b/Solution8.java index 7adb539..aa72687 100644 --- a/Solution8.java +++ b/Solution8.java @@ -1,74 +1,74 @@ -/** - * @description: - * - * 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 - * - * 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 - * - * 此外,你可以假设该网格的四条边均被水包围。 - * - * - * - * 示例 1: - * - * 输入:grid = [ - * ["1","1","1","1","0"], - * ["1","1","0","1","0"], - * ["1","1","0","0","0"], - * ["0","0","0","0","0"] - * ] - * 输出:1 - * 示例 2: - * - * 输入:grid = [ - * ["1","1","0","0","0"], - * ["1","1","0","0","0"], - * ["0","0","1","0","0"], - * ["0","0","0","1","1"] - * ] - * 输出:3 - * - * - * 提示: - * - * m == grid.length - * n == grid[i].length - * 1 <= m, n <= 300 - * grid[i][j] 的值为 '0' 或 '1' - * - */ -class Solution8 { - void dfs(char[][] grid, int r, int c) { - int nr = grid.length; - int nc = grid[0].length; - - if (r < 0 || c > 0 || r >= nr || c >= nc || grid[r][c] == '0') { - return; - } - grid[r][c] = '1'; - dfs(grid, r - 1, c); - dfs(grid, r + 1, c); - dfs(grid, r, c - 1); - dfs(grid, r, c + 1); - } - - public int numIslands(char[][] grid) { - if (grid == null || grid.length <= 1) { - return 0; - } - - int nr = grid.length; - int nc = grid[0].length; - int num_islands = 0; - for (int r = 0; r < nr; ++r) { - for (int c = 0; r < nc; ++c) { - if (grid[r][c] == '1') { - ++num_islands; - dfs(grid, r, c); - } - } - } - - return num_islands; - } -} +/** + * @description: + * + * 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 + * + * 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 + * + * 此外,你可以假设该网格的四条边均被水包围。 + * + * + * + * 示例 1: + * + * 输入:grid = [ + * ["1","1","1","1","0"], + * ["1","1","0","1","0"], + * ["1","1","0","0","0"], + * ["0","0","0","0","0"] + * ] + * 输出:1 + * 示例 2: + * + * 输入:grid = [ + * ["1","1","0","0","0"], + * ["1","1","0","0","0"], + * ["0","0","1","0","0"], + * ["0","0","0","1","1"] + * ] + * 输出:3 + * + * + * 提示: + * + * m == grid.length + * n == grid[i].length + * 1 <= m, n <= 300 + * grid[i][j] 的值为 '0' 或 '1' + * + */ +class Solution8 { + void dfs(char[][] grid, int r, int c) { + int nr = grid.length; + int nc = grid[0].length; + + if (r < 0 || c > 0 || r >= nr || c >= nc || grid[r][c] == '0') { + return; + } + grid[r][c] = '1'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length <= 1) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + for (int r = 0; r < nr; ++r) { + for (int c = 0; r < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + dfs(grid, r, c); + } + } + } + + return num_islands; + } +} diff --git a/Solution9.java b/Solution9.java index 389aeaf..1427d76 100644 --- a/Solution9.java +++ b/Solution9.java @@ -1,86 +1,86 @@ -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * @description: - * - * 给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。 - * - * 给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和 bi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false。 - * - * - * - * 示例 1: - * - * 输入:n = 4, dislikes = [[1,2],[1,3],[2,4]] - * 输出:true - * 解释:group1 [1,4], group2 [2,3] - * 示例 2: - * - * 输入:n = 3, dislikes = [[1,2],[1,3],[2,3]] - * 输出:false - * 示例 3: - * - * 输入:n = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]] - * 输出:false - * - * - * 提示: - * - * 1 <= n <= 2000 - * 0 <= dislikes.length <= 104 - * dislikes[i].length == 2 - * 1 <= dislikes[i][j] <= n - * ai < bi - * dislikes 中每一组都 不同 - * - */ -class Solution9 { - - public boolean possibleBipartition(int n, int[][] dislikes) { - int[] fa = new int[n + 1]; - Arrays.fill(fa, -1); - List[] g = new List[n + 1]; - for (int i = 0; i < n; ++i) { - g[i] = new ArrayList(); - } - for (int[] p : dislikes) - g[p[0]].add(p[1]); - g[p[1]].add(p[0]); - for (int i = 1; i <= n; ++i) { - for (int j = 0; j < g[i].size(); ++j) { - unit(g[i].get(0), g[i].get(j), fa); - if (isconnect(i, g[i].get(j), fa)) { - return false; - } - } - } - return true; - } - - public void unit(int x, int y, int[] fa) { - x = findFa(x, fa); - y = findFa(y, fa); - if (x == y) { - return ; - } - if (fa[x] <= fa[y]) { - int temp = x; - x = y; - y = temp; - } - fa[x] += fa[y]; - fa[y] = x; - } - - public boolean isconnect(int x, int y, int[] fa) { - x = findFa(x, fa); - y = findFa(y, fa); - return x == y; - } - - public int findFa(int x, int[] fa) { - return fa[x] > 0 ? x : (fa[x] = findFa(fa[x], fa)); - } -} +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @description: + * + * 给定一组 n 人(编号为 1, 2, ..., n), 我们想把每个人分进任意大小的两组。每个人都可能不喜欢其他人,那么他们不应该属于同一组。 + * + * 给定整数 n 和数组 dislikes ,其中 dislikes[i] = [ai, bi] ,表示不允许将编号为 ai 和 bi的人归入同一组。当可以用这种方法将所有人分进两组时,返回 true;否则返回 false。 + * + * + * + * 示例 1: + * + * 输入:n = 4, dislikes = [[1,2],[1,3],[2,4]] + * 输出:true + * 解释:group1 [1,4], group2 [2,3] + * 示例 2: + * + * 输入:n = 3, dislikes = [[1,2],[1,3],[2,3]] + * 输出:false + * 示例 3: + * + * 输入:n = 5, dislikes = [[1,2],[2,3],[3,4],[4,5],[1,5]] + * 输出:false + * + * + * 提示: + * + * 1 <= n <= 2000 + * 0 <= dislikes.length <= 104 + * dislikes[i].length == 2 + * 1 <= dislikes[i][j] <= n + * ai < bi + * dislikes 中每一组都 不同 + * + */ +class Solution9 { + + public boolean possibleBipartition(int n, int[][] dislikes) { + int[] fa = new int[n + 1]; + Arrays.fill(fa, -1); + List[] g = new List[n + 1]; + for (int i = 0; i < n; ++i) { + g[i] = new ArrayList(); + } + for (int[] p : dislikes) + g[p[0]].add(p[1]); + g[p[1]].add(p[0]); + for (int i = 1; i <= n; ++i) { + for (int j = 0; j < g[i].size(); ++j) { + unit(g[i].get(0), g[i].get(j), fa); + if (isconnect(i, g[i].get(j), fa)) { + return false; + } + } + } + return true; + } + + public void unit(int x, int y, int[] fa) { + x = findFa(x, fa); + y = findFa(y, fa); + if (x == y) { + return ; + } + if (fa[x] <= fa[y]) { + int temp = x; + x = y; + y = temp; + } + fa[x] += fa[y]; + fa[y] = x; + } + + public boolean isconnect(int x, int y, int[] fa) { + x = findFa(x, fa); + y = findFa(y, fa); + return x == y; + } + + public int findFa(int x, int[] fa) { + return fa[x] > 0 ? x : (fa[x] = findFa(fa[x], fa)); + } +}