diff --git a/problems/0005.最长回文子串.md b/problems/0005.最长回文子串.md index a13daf1e..b3d3b938 100644 --- a/problems/0005.最长回文子串.md +++ b/problems/0005.最长回文子串.md @@ -256,7 +256,60 @@ public: * 时间复杂度:O(n^2) * 空间复杂度:O(1) +### Manacher 算法 +Manacher 算法的关键在于高效利用回文的对称性,通过插入分隔符和维护中心、边界等信息,在线性时间内找到最长回文子串。这种方法避免了重复计算,是处理回文问题的最优解。 + +```c++ +//Manacher 算法 +class Solution { +public: + string longestPalindrome(string s) { + // 预处理字符串,在每个字符之间插入 '#' + string t = "#"; + for (char c : s) { + t += c; // 添加字符 + t += '#';// 添加分隔符 + } + int n = t.size();// 新字符串的长度 + vector p(n, 0);// p[i] 表示以 t[i] 为中心的回文半径 + int center = 0, right = 0;// 当前回文的中心和右边界 + + + // 遍历预处理后的字符串 + for (int i = 0; i < n; i++) { + // 如果当前索引在右边界内,利用对称性初始化 p[i] + if (i < right) { + p[i] = min(right - i, p[2 * center - i]); + } + // 尝试扩展回文 + while (i - p[i] - 1 >= 0 && i + p[i] + 1 < n && t[i - p[i] - 1] == t[i + p[i] + 1]) { + p[i]++;// 增加回文半径 + } + // 如果当前回文扩展超出右边界,更新中心和右边界 + if (i + p[i] > right) { + center = i;// 更新中心 + right = i + p[i];// 更新右边界 + } + } + // 找到最大回文半径和对应的中心 + int maxLen = 0, centerIndex = 0; + for (int i = 0; i < n; i++) { + if (p[i] > maxLen) { + maxLen = p[i];// 更新最大回文长度 + centerIndex = i;// 更新中心索引 + } + } + // 计算原字符串中回文子串的起始位置并返回 + return s.substr((centerIndex - maxLen) / 2, maxLen); + } +}; +``` + + + +* 时间复杂度:O(n) +* 空间复杂度:O(n) ## 其他语言版本 @@ -682,3 +735,4 @@ public class Solution { + diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 1c7ff0cd..b8ad2e80 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -240,6 +240,42 @@ class Solution: res = max(res, dp[i]) return res ``` + +动态规划 + +```python +class Solution: + def maxSubArray(self, nums): + if not nums: + return 0 + dp = [0] * len(nums) # dp[i]表示包括i之前的最大连续子序列和 + dp[0] = nums[0] + result = dp[0] + for i in range(1, len(nums)): + dp[i] = max(dp[i-1]+nums[i], nums[i]) # 状态转移公式 + if dp[i] > result: + result = dp[i] # result 保存dp[i]的最大值 + return result +``` + +动态规划优化 + +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + max_sum = float("-inf") # 初始化结果为负无穷大,方便比较取最大值 + current_sum = 0 # 初始化当前连续和 + + for num in nums: + + # 更新当前连续和 + # 如果原本的连续和加上当前数字之后没有当前数字大,说明原本的连续和是负数,那么就直接从当前数字开始重新计算连续和 + current_sum = max(current_sum+num, num) + max_sum = max(max_sum, current_sum) # 更新结果 + + return max_sum +``` + ### Go 贪心法 ```go diff --git a/problems/0055.跳跃游戏.md b/problems/0055.跳跃游戏.md index 01fd9513..82b433d7 100644 --- a/problems/0055.跳跃游戏.md +++ b/problems/0055.跳跃游戏.md @@ -143,6 +143,23 @@ class Solution: return False ``` +```python +## 基于当前最远可到达位置判断 +class Solution: + def canJump(self, nums: List[int]) -> bool: + far = nums[0] + for i in range(len(nums)): + # 要考虑两个情况 + # 1. i <= far - 表示 当前位置i 可以到达 + # 2. i > far - 表示 当前位置i 无法到达 + if i > far: + return False + far = max(far, nums[i]+i) + # 如果循环正常结束,表示最后一个位置也可以到达,否则会在中途直接退出 + # 关键点在于,要想明白其实列表中的每个位置都是需要验证能否到达的 + return True +``` + ### Go ```go diff --git a/problems/0112.路径总和.md b/problems/0112.路径总和.md index 6709a2fb..e6ccc6ae 100644 --- a/problems/0112.路径总和.md +++ b/problems/0112.路径总和.md @@ -564,10 +564,10 @@ class Solution: return False - def hasPathSum(self, root: TreeNode, sum: int) -> bool: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: if root is None: return False - return self.traversal(root, sum - root.val) + return self.traversal(root, targetSum - root.val) ``` (版本二) 递归 + 精简 @@ -579,12 +579,12 @@ class Solution: # self.left = left # self.right = right class Solution: - def hasPathSum(self, root: TreeNode, sum: int) -> bool: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: if not root: return False - if not root.left and not root.right and sum == root.val: + if not root.left and not root.right and targetSum == root.val: return True - return self.hasPathSum(root.left, sum - root.val) or self.hasPathSum(root.right, sum - root.val) + return self.hasPathSum(root.left, targetSum - root.val) or self.hasPathSum(root.right, targetSum - root.val) ``` (版本三) 迭代 @@ -596,7 +596,7 @@ class Solution: # self.left = left # self.right = right class Solution: - def hasPathSum(self, root: TreeNode, sum: int) -> bool: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: if not root: return False # 此时栈里要放的是pair<节点指针,路径数值> @@ -659,13 +659,13 @@ class Solution: return - def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]: + def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]: self.result.clear() self.path.clear() if not root: return self.result self.path.append(root.val) # 把根节点放进路径 - self.traversal(root, sum - root.val) + self.traversal(root, targetSum - root.val) return self.result ``` @@ -678,7 +678,7 @@ class Solution: # self.left = left # self.right = right class Solution: - def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]: + def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]: result = [] self.traversal(root, targetSum, [], result) @@ -703,7 +703,7 @@ class Solution: # self.left = left # self.right = right class Solution: - def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]: + def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]: if not root: return [] stack = [(root, [root.val])] diff --git a/problems/0134.加油站.md b/problems/0134.加油站.md index 7ac9f0f9..5cf50b3e 100644 --- a/problems/0134.加油站.md +++ b/problems/0134.加油站.md @@ -158,7 +158,7 @@ i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i 如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。 -区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。 +区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择起始位置了。 **那么局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置**。 diff --git a/problems/0135.分发糖果.md b/problems/0135.分发糖果.md index 6805857e..29eaa06d 100644 --- a/problems/0135.分发糖果.md +++ b/problems/0135.分发糖果.md @@ -177,21 +177,20 @@ class Solution { ```python class Solution: def candy(self, ratings: List[int]) -> int: - candyVec = [1] * len(ratings) + n = len(ratings) + candies = [1] * n - # 从前向后遍历,处理右侧比左侧评分高的情况 - for i in range(1, len(ratings)): + # Forward pass: handle cases where right rating is higher than left + for i in range(1, n): if ratings[i] > ratings[i - 1]: - candyVec[i] = candyVec[i - 1] + 1 + candies[i] = candies[i - 1] + 1 - # 从后向前遍历,处理左侧比右侧评分高的情况 - for i in range(len(ratings) - 2, -1, -1): + # Backward pass: handle cases where left rating is higher than right + for i in range(n - 2, -1, -1): if ratings[i] > ratings[i + 1]: - candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1) + candies[i] = max(candies[i], candies[i + 1] + 1) - # 统计结果 - result = sum(candyVec) - return result + return sum(candies) ``` diff --git a/problems/0150.逆波兰表达式求值.md b/problems/0150.逆波兰表达式求值.md index 48e99c5b..7d4031d7 100644 --- a/problems/0150.逆波兰表达式求值.md +++ b/problems/0150.逆波兰表达式求值.md @@ -108,7 +108,7 @@ public: } } - int result = st.top(); + long long result = st.top(); st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事) return result; } diff --git a/problems/0151.翻转字符串里的单词.md b/problems/0151.翻转字符串里的单词.md index bf486bdc..9a0cbea4 100644 --- a/problems/0151.翻转字符串里的单词.md +++ b/problems/0151.翻转字符串里的单词.md @@ -440,11 +440,10 @@ class Solution { ```Python class Solution: def reverseWords(self, s: str) -> str: - # 删除前后空白 - s = s.strip() # 反转整个字符串 s = s[::-1] # 将字符串拆分为单词,并反转每个单词 + # split()函数能够自动忽略多余的空白字符 s = ' '.join(word[::-1] for word in s.split()) return s @@ -1029,3 +1028,4 @@ public string ReverseWords(string s) { + diff --git a/problems/0226.翻转二叉树.md b/problems/0226.翻转二叉树.md index e501b298..55dc3cbf 100644 --- a/problems/0226.翻转二叉树.md +++ b/problems/0226.翻转二叉树.md @@ -459,11 +459,10 @@ class Solution: queue = collections.deque([root]) while queue: - for i in range(len(queue)): - node = queue.popleft() - node.left, node.right = node.right, node.left - if node.left: queue.append(node.left) - if node.right: queue.append(node.right) + node = queue.popleft() + node.left, node.right = node.right, node.left + if node.left: queue.append(node.left) + if node.right: queue.append(node.right) return root ``` @@ -1033,4 +1032,3 @@ public TreeNode InvertTree(TreeNode root) { - diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 5c86e122..2be8922b 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -64,7 +64,7 @@ 如果有一个字符串s,在 s + s 拼接后, 不算首尾字符,如果能凑成s字符串,说明s 一定是重复子串组成。 -如图,字符串s,图中数字为数组下标,在 s + s 拼接后, 不算首尾字符,中间凑成s字符串。 +如图,字符串s,图中数字为数组下标,在 s + s 拼接后, 不算首尾字符,中间凑成s字符串。 (图中数字为数组下标) ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240910115555.png) @@ -163,9 +163,7 @@ KMP算法中next数组为什么遇到字符不匹配的时候可以找到上一 如果一个字符串s是由重复子串组成,那么 最长相等前后缀不包含的子串一定是字符串s的最小重复子串。 -证明: 如果s 是由最小重复子串p组成。 - -即 s = n * p +如果s 是由最小重复子串p组成,即 s = n * p 那么相同前后缀可以是这样: @@ -203,12 +201,14 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3 最长相等前后缀不包含的子串已经是字符串s的最小重复子串,那么字符串s一定由重复子串组成,这个不需要证明了。 -关键是要要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。 +关键是要证明:最长相等前后缀不包含的子串什么时候才是字符串s的最小重复子串呢。 -情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串 +情况一, 最长相等前后缀不包含的子串的长度 比 字符串s的一半的长度还大,那一定不是字符串s的重复子串,如图: ![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240911110236.png) +图中:前后缀不包含的子串的长度 大于 字符串s的长度的 二分之一 + -------------- 情况二,最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,如图: @@ -230,7 +230,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3 即 s[0]s[1] 是最小重复子串 -以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能使最小重复子串。 +以上推导中,录友可能想,你怎么知道 s[0] 和 s[1] 就不相同呢? s[0] 为什么就不能是最小重复子串。 如果 s[0] 和 s[1] 也相同,同时 s[0]s[1]与s[2]s[3]相同,s[2]s[3] 与 s[4]s[5]相同,s[4]s[5] 与 s[6]s[7] 相同,那么这个字符串就是有一个字符构成的字符串。 @@ -246,7 +246,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3 或者说,自己举个例子,`aaaaaa`,这个字符串,他的最长相等前后缀是什么? -同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,就是一定是最小重复子串。 +同上以上推导,最长相等前后缀不包含的子串的长度只要被 字符串s的长度整除,最长相等前后缀不包含的子串一定是最小重复子串。 ---------------- @@ -267,7 +267,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3 以上推导,可以得出 s[0],s[1],s[2] 与 s[3],s[4],s[5] 相同,s[3]s[4] 与 s[6]s[7]相同。 -那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,就不是s的重复子串 +那么 最长相等前后缀不包含的子串的长度 不被 字符串s的长度整除 ,最长相等前后缀不包含的子串就不是s的重复子串 ----------- @@ -277,7 +277,7 @@ p2 = p1,p3 = p2 即: p1 = p2 = p3 在必要条件,这个是 显而易见的,都已经假设 最长相等前后缀不包含的子串 是 s的最小重复子串了,那s必然是重复子串。 -关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串。 +**关键是需要证明, 字符串s的最长相等前后缀不包含的子串 什么时候才是 s最小重复子串**。 同上我们证明了,当 最长相等前后缀不包含的子串的长度 可以被 字符串s的长度整除,那么不包含的子串 就是s的最小重复子串。 diff --git a/problems/0513.找树左下角的值.md b/problems/0513.找树左下角的值.md index d69ceb6f..c7446726 100644 --- a/problems/0513.找树左下角的值.md +++ b/problems/0513.找树左下角的值.md @@ -55,7 +55,7 @@ 参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。 -本题还需要类里的两个全局变量,maxLen用来记录最大深度,result记录最大深度最左节点的数值。 +本题还需要类里的两个全局变量,maxDepth用来记录最大深度,result记录最大深度最左节点的数值。 代码如下: diff --git a/problems/0518.零钱兑换II.md b/problems/0518.零钱兑换II.md index bef62b30..70231212 100644 --- a/problems/0518.零钱兑换II.md +++ b/problems/0518.零钱兑换II.md @@ -168,23 +168,43 @@ for (int j = 0; j <= amount; j++) { // 遍历背包容量 class Solution { public: int change(int amount, vector& coins) { - vector dp(amount + 1, 0); - dp[0] = 1; + vector dp(amount + 1, 0); // 防止相加数据超int + dp[0] = 1; // 只有一种方式达到0 for (int i = 0; i < coins.size(); i++) { // 遍历物品 for (int j = coins[i]; j <= amount; j++) { // 遍历背包 dp[j] += dp[j - coins[i]]; } } - return dp[amount]; + return dp[amount]; // 返回组合数 } }; ``` +C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。 + * 时间复杂度: O(mn),其中 m 是amount,n 是 coins 的长度 * 空间复杂度: O(m) +为了防止相加的数据 超int 也可以这么写: + +```CPP +class Solution { +public: + int change(int amount, vector& coins) { + vector dp(amount + 1, 0); + dp[0] = 1; // 只有一种方式达到0 + for (int i = 0; i < coins.size(); i++) { // 遍历物品 + for (int j = coins[i]; j <= amount; j++) { // 遍历背包 + if (dp[j] < INT_MAX - dp[j - coins[i]]) { //防止相加数据超int + dp[j] += dp[j - coins[i]]; + } + } + } + return dp[amount]; // 返回组合数 + } +}; +``` -是不是发现代码如此精简 ## 总结 diff --git a/problems/0541.反转字符串II.md b/problems/0541.反转字符串II.md index 3e304fab..5e75d3c3 100644 --- a/problems/0541.反转字符串II.md +++ b/problems/0541.反转字符串II.md @@ -37,7 +37,7 @@ 因为要找的也就是每2 * k 区间的起点,这样写,程序会高效很多。 -**所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。** +**所以当需要固定规律一段一段去处理字符串的时候,要想想在for循环的表达式上做做文章。** 性能如下: @@ -505,3 +505,4 @@ impl Solution { + diff --git a/problems/0977.有序数组的平方.md b/problems/0977.有序数组的平方.md index effa9055..1a6604c2 100644 --- a/problems/0977.有序数组的平方.md +++ b/problems/0977.有序数组的平方.md @@ -100,6 +100,7 @@ public: ## 其他语言版本 ### Java: + 排序法 ```Java class Solution { @@ -209,6 +210,43 @@ class Solution: return new_list[::-1] ``` +```python3 +(双指针优化版本) 三步优化 + class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + """ + 整体思想:有序数组的绝对值最大值永远在两头,比较两头,平方大的插到新数组的最后 + 优 化:1. 优化所有元素为非正或非负的情况 + 2. 头尾平方的大小比较直接将头尾相加与0进行比较即可 + 3. 新的平方排序数组的插入索引可以用倒序插入实现(针对for循环,while循环不适用) + """ + + # 特殊情况, 元素都非负(优化1) + if nums[0] >= 0: + return [num ** 2 for num in nums] # 按顺序平方即可 + # 最后一个非正,全负有序的 + if nums[-1] <= 0: + return [x ** 2 for x in nums[::-1]] # 倒序平方后的数组 + + # 一般情况, 有正有负 + i = 0 # 原数组头索引 + j = len(nums) - 1 # 原数组尾部索引 + new_nums = [0] * len(nums) # 新建一个等长数组用于保存排序后的结果 + # end_index = len(nums) - 1 # 新的排序数组(是新数组)尾插索引, 每次需要减一(优化3优化了) + + for end_index in range(len(nums)-1, -1, -1): # (优化3,倒序,不用单独创建变量) + # if nums[i] ** 2 >= nums[j] ** 2: + if nums[i] + nums[j] <= 0: # (优化2) + new_nums[end_index] = nums[i] ** 2 + i += 1 + # end_index -= 1 (优化3) + else: + new_nums[end_index] = nums[j] ** 2 + j -= 1 + # end_index -= 1 (优化3) + return new_nums +``` + ### Go: ```Go diff --git a/problems/1047.删除字符串中的所有相邻重复项.md b/problems/1047.删除字符串中的所有相邻重复项.md index 7232008a..51ec4e62 100644 --- a/problems/1047.删除字符串中的所有相邻重复项.md +++ b/problems/1047.删除字符串中的所有相邻重复项.md @@ -164,7 +164,7 @@ class Solution { int top = -1; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); - // 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top-- + // 当 top >= 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top-- if (top >= 0 && res.charAt(top) == c) { res.deleteCharAt(top); top--; diff --git a/problems/kamacoder/0047.参会dijkstra朴素.md b/problems/kamacoder/0047.参会dijkstra朴素.md index c0a490b3..465ad16d 100644 --- a/problems/kamacoder/0047.参会dijkstra朴素.md +++ b/problems/kamacoder/0047.参会dijkstra朴素.md @@ -869,6 +869,65 @@ if __name__ == "__main__": ### Javascript +```js +function dijkstra(grid, start, end) { + const visited = Array.from({length: end + 1}, () => false) + const minDist = Array.from({length: end + 1}, () => Number.MAX_VALUE) + minDist[start] = 0 + + for (let i = 1 ; i < end + 1 ; i++) { + let cur = -1 + let tempMinDist = Number.MAX_VALUE + // 1. 找尋與起始點距離最近且未被訪的節點 + for (let j = 1 ; j < end + 1 ; j++) { + if (!visited[j] && minDist[j] < tempMinDist) { + cur = j + tempMinDist = minDist[j] + } + } + if (cur === -1) break; + + // 2. 更新節點狀態為已拜訪 + visited[cur] = true + + // 3. 更新未拜訪節點與起始點的最短距離 + for (let j = 1 ; j < end + 1 ; j++) { + if(!visited[j] && grid[cur][j] != Number.MAX_VALUE + && grid[cur][j] + minDist[cur] < minDist[j] + ) { + minDist[j] = grid[cur][j] + minDist[cur] + } + } + } + + return minDist[end] === Number.MAX_VALUE ? -1 : minDist[end] +} + + +async function main() { + // 輸入 + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [n, m] = (await readline()).split(" ").map(Number) + const grid = Array.from({length: n + 1}, + () => Array.from({length:n + 1}, () => Number.MAX_VALUE)) + for (let i = 0 ; i < m ; i++) { + const [s, e, w] = (await readline()).split(" ").map(Number) + grid[s][e] = w + } + + // dijkstra + const result = dijkstra(grid, 1, n) + + // 輸出 + console.log(result) +} + + +main() +``` + ### TypeScript ### PhP diff --git a/problems/kamacoder/0053.寻宝-Kruskal.md b/problems/kamacoder/0053.寻宝-Kruskal.md index cb24fd17..6a227985 100644 --- a/problems/kamacoder/0053.寻宝-Kruskal.md +++ b/problems/kamacoder/0053.寻宝-Kruskal.md @@ -549,6 +549,62 @@ if __name__ == "__main__": ### Javascript +```js +function kruskal(v, edges) { + const father = Array.from({ length: v + 1 }, (_, i) => i) + + function find(u){ + if (u === father[u]) { + return u + } else { + father[u] = find(father[u]) + return father[u] + } + + } + + function isSame(u, v) { + let s = find(u) + let t = find(v) + return s === t + } + + function join(u, v) { + let s = find(u) + let t = find(v) + if (s !== t) { + father[s] = t + } + } + + edges.sort((a, b) => a[2] - b[2]) + let result = 0 + for (const [v1, v2, w] of edges) { + if (!isSame(v1, v2)) { + result += w + join(v1 ,v2) + } + } + console.log(result) +} + + +async function main() { + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [v, e] = (await readline()).split(" ").map(Number) + const edges = [] + for (let i = 0 ; i < e ; i++) { + edges.push((await readline()).split(" ").map(Number)) + } + kruskal(v, edges) +} + + +main() +``` + ### TypeScript ### PhP diff --git a/problems/kamacoder/0053.寻宝-prim.md b/problems/kamacoder/0053.寻宝-prim.md index c71624b5..a8dad4cb 100644 --- a/problems/kamacoder/0053.寻宝-prim.md +++ b/problems/kamacoder/0053.寻宝-prim.md @@ -693,6 +693,55 @@ if __name__ == "__main__": ### Rust ### Javascript +```js +function prim(v, edges) { + const grid = Array.from({ length: v + 1 }, () => new Array(v + 1).fill(10001)); // Fixed grid initialization + const minDist = new Array(v + 1).fill(10001) + const isInTree = new Array(v + 1).fill(false) + // 建構鄰接矩陣 + for(const [v1, v2, w] of edges) { + grid[v1][v2] = w + grid[v2][v1] = w + } + // prim 演算法 + for (let i = 1 ; i < v ; i++) { + let cur = -1 + let tempMinDist = Number.MAX_VALUE + // 1. 尋找距離生成樹最近的節點 + for (let j = 1 ; j < v + 1 ; j++) { + if (!isInTree[j] && minDist[j] < tempMinDist) { + tempMinDist = minDist[j] + cur = j + } + } + // 2. 將節點放入生成樹 + isInTree[cur] = true + // 3. 更新非生成樹節點與生成樹的最短距離 + for (let j = 1 ; j < v + 1 ; j++) { + if (!isInTree[j] && grid[cur][j] < minDist[j]) { + minDist[j] = grid[cur][j] + } + } + } + console.log(minDist.slice(2).reduce((acc, cur) => acc + cur, 0)) +} + + +async function main() { + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [v, e] = (await readline()).split(" ").map(Number) + const edges = [] + for (let i = 0 ; i < e ; i++) { + edges.push((await readline()).split(" ").map(Number)) + } + prim(v, edges) +} + + +main() +``` ### TypeScript diff --git a/problems/kamacoder/0094.城市间货物运输I-SPFA.md b/problems/kamacoder/0094.城市间货物运输I-SPFA.md index b3f42bf8..9ba92599 100644 --- a/problems/kamacoder/0094.城市间货物运输I-SPFA.md +++ b/problems/kamacoder/0094.城市间货物运输I-SPFA.md @@ -464,6 +464,60 @@ if __name__ == "__main__": ### Javascript +```js +async function main() { + // 輸入 + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [n, m] = (await readline()).split(" ").map(Number) + const grid = {} + for (let i = 0 ; i < m ; i++) { + const [src, desc, w] = (await readline()).split(" ").map(Number) + if (grid.hasOwnProperty(src)) { + grid[src].push([desc, w]) + } else { + grid[src] = [[desc, w]] + } + } + const minDist = Array.from({length: n + 1}, () => Number.MAX_VALUE) + + // 起始點 + minDist[1] = 0 + + const q = [1] + const visited = Array.from({length: n + 1}, () => false) + + while (q.length) { + const src = q.shift() + const neighbors = grid[src] + visited[src] = false + if (neighbors) { + for (const [desc, w] of neighbors) { + if (minDist[src] !== Number.MAX_VALUE + && minDist[src] + w < minDist[desc]) { + minDist[desc] = minDist[src] + w + if (!visited[desc]) { + q.push(desc) + visited[desc] = true + } + + } + } + } + } + + // 輸出 + if (minDist[n] === Number.MAX_VALUE) { + console.log('unconnected') + } else { + console.log(minDist[n]) + } +} + +main() +``` + ### TypeScript ### PhP diff --git a/problems/kamacoder/0094.城市间货物运输I.md b/problems/kamacoder/0094.城市间货物运输I.md index 3737fe01..9021e0fe 100644 --- a/problems/kamacoder/0094.城市间货物运输I.md +++ b/problems/kamacoder/0094.城市间货物运输I.md @@ -485,6 +485,45 @@ if __name__ == "__main__": ### Javascript +```js +async function main() { + // 輸入 + const rl = require('readline').createInterface({ input: process.stdin }) + const iter = rl[Symbol.asyncIterator]() + const readline = async () => (await iter.next()).value + const [n, m] = (await readline()).split(" ").map(Number) + const edges = [] + for (let i = 0 ; i < m ; i++) { + edges.push((await readline()).split(" ").map(Number)) + } + const minDist = Array.from({length: n + 1}, () => Number.MAX_VALUE) + // 起始點 + minDist[1] = 0 + + for (let i = 1 ; i < n ; i++) { + let update = false + for (const [src, desc, w] of edges) { + if (minDist[src] !== Number.MAX_VALUE && minDist[src] + w < minDist[desc]) { + minDist[desc] = minDist[src] + w + update = true + } + } + if (!update) { + break; + } + } + + // 輸出 + if (minDist[n] === Number.MAX_VALUE) { + console.log('unconnected') + } else { + console.log(minDist[n]) + } +} + +main() +``` + ### TypeScript ### PhP diff --git a/problems/kamacoder/0096.城市间货物运输III.md b/problems/kamacoder/0096.城市间货物运输III.md index dacd23d1..567a1d87 100644 --- a/problems/kamacoder/0096.城市间货物运输III.md +++ b/problems/kamacoder/0096.城市间货物运输III.md @@ -703,6 +703,42 @@ public class Main { ``` ### Python +```python +def main(): + # 輸入 + n, m = map(int, input().split()) + edges = list() + for _ in range(m): + edges.append(list(map(int, input().split() ))) + + start, end, k = map(int, input().split()) + min_dist = [float('inf') for _ in range(n + 1)] + min_dist[start] = 0 + + # 只能經過k個城市,所以從起始點到中間有(k + 1)個邊連接 + # 需要鬆弛(k + 1)次 + + for _ in range(k + 1): + update = False + min_dist_copy = min_dist.copy() + for src, desc, w in edges: + if (min_dist_copy[src] != float('inf') and + min_dist_copy[src] + w < min_dist[desc]): + min_dist[desc] = min_dist_copy[src] + w + update = True + if not update: + break + # 輸出 + if min_dist[end] == float('inf'): + print('unreachable') + else: + print(min_dist[end]) + + + +if __name__ == "__main__": + main() +``` ### Go diff --git a/problems/kamacoder/0099.岛屿的数量广搜.md b/problems/kamacoder/0099.岛屿的数量广搜.md index 30475753..9d31c922 100644 --- a/problems/kamacoder/0099.岛屿的数量广搜.md +++ b/problems/kamacoder/0099.岛屿的数量广搜.md @@ -499,6 +499,55 @@ main(); ### Swift ### Scala +```scala +import scala.collection.mutable.Queue +import util.control.Breaks._ + +// Dev on LeetCode: https://leetcode.cn/problems/number-of-islands/description/ +object Solution { + def numIslands(grid: Array[Array[Char]]): Int = { + val row = grid.length + val col = grid(0).length + val dir = List((-1,0), (0,-1), (1,0), (0,1)) // 四个方向 + var visited = Array.fill(row)(Array.fill(col)(false)) + var counter = 0 + var que = Queue.empty[Tuple2[Int, Int]] + + (0 until row).map{ r => + (0 until col).map{ c => + breakable { + if (!visited(r)(c) && grid(r)(c) == '1') { + que.enqueue((r, c)) + visited(r)(c) // 只要加入队列,立刻标记 + } else break // 不是岛屿不进入queue,也不记录 + + while (!que.isEmpty) { + val cur = que.head + que.dequeue() + val x = cur(0) + val y = cur(1) + dir.map{ d => + val nextX = x + d(0) + val nextY = y + d(1) + breakable { + // 越界就跳过 + if (nextX < 0 || nextX >= row || nextY < 0 || nextY >= col) break + if (!visited(nextX)(nextY) && grid(nextX)(nextY) == '1') { + visited(nextX)(nextY) = true // 只要加入队列,立刻标记 + que.enqueue((nextX, nextY)) + } + } + } + } + counter = counter + 1 // 找完一个岛屿后记录一下 + } + } + } + + counter + } +} +``` ### C# diff --git a/problems/kamacoder/0099.岛屿的数量深搜.md b/problems/kamacoder/0099.岛屿的数量深搜.md index 6ac7ba3b..da1c0739 100644 --- a/problems/kamacoder/0099.岛屿的数量深搜.md +++ b/problems/kamacoder/0099.岛屿的数量深搜.md @@ -412,6 +412,46 @@ const dfs = (graph, visited, x, y) => { ### Swift ### Scala +```scala +import util.control.Breaks._ + +object Solution { + val dir = List((-1,0), (0,-1), (1,0), (0,1)) // 四个方向 + + def dfs(grid: Array[Array[Char]], visited: Array[Array[Boolean]], row: Int, col: Int): Unit = { + (0 until 4).map { x => + val nextR = row + dir(x)(0) + val nextC = col + dir(x)(1) + breakable { + if(nextR < 0 || nextR >= grid.length || nextC < 0 || nextC >= grid(0).length) break + if (!visited(nextR)(nextC) && grid(nextR)(nextC) == '1') { + visited(nextR)(nextC) = true // 经过就记录 + dfs(grid, visited, nextR, nextC) + } + } + } + } + + def numIslands(grid: Array[Array[Char]]): Int = { + val row = grid.length + val col = grid(0).length + var visited = Array.fill(row)(Array.fill(col)(false)) + var counter = 0 + + (0 until row).map{ r => + (0 until col).map{ c => + if (!visited(r)(c) && grid(r)(c) == '1') { + visited(r)(c) = true // 经过就记录 + dfs(grid, visited, r, c) + counter += 1 + } + } + } + + counter + } +} +``` ### C# diff --git a/problems/kamacoder/0100.岛屿的最大面积.md b/problems/kamacoder/0100.岛屿的最大面积.md index 51bfc57f..06319ee3 100644 --- a/problems/kamacoder/0100.岛屿的最大面积.md +++ b/problems/kamacoder/0100.岛屿的最大面积.md @@ -223,7 +223,121 @@ public: ## 其他语言版本 ### Java - +DFS +```java +//这里的实现为主函数处理每个岛屿的第一块陆地 方式 +//所以是主函数直接置count为1,剩余的交给dfs来做。 +import java.util.*; +public class Main{ + static int[][] dir = {{0,-1}, {1,0}, {0,1}, {-1, 0}};//四个方向 + static int count = 0; + public static void dfs(boolean[][] visited, int x, int y, int[][] grid){ + for(int i = 0; i < 4; i++){ + int nextX = x + dir[i][0]; + int nextY = y + dir[i][1]; + if(nextX < 0 || nextY < 0 || nextY >= grid[0].length || nextX >= grid.length){ + continue; + } + if(!visited[nextX][nextY] && grid[nextX][nextY] == 1){ + count++; + visited[nextX][nextY] = true; + dfs(visited, nextX, nextY, grid); + } + } + } + public static void main(String[] args){ + Scanner in = new Scanner(System.in); + int n = in.nextInt(); + int m = in.nextInt(); + int[][] grid = new int[n][m]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + grid[i][j] = in.nextInt(); + } + } + + int result = 0; + boolean[][] visited = new boolean[n][m]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + if(!visited[i][j] && grid[i][j] == 1){ + visited[i][j] = true; + count = 1; + dfs(visited, i, j, grid); + //dfs遍历完了一座岛屿,就比较count和result,保留最大的 + result = Math.max(result, count); + } + } + } + System.out.println(result); + } +} +``` +BFS +```java +import java.util.*; +public class Main{ + static int[][] dir = {{0,-1}, {1,0}, {0,1}, {-1, 0}};//下右上左的顺序 + static int count = 0; + public static void bfs(boolean[][] visited, int x, int y, int[][] grid){ + Queue queue = new LinkedList(); + queue.add(new pair(x,y)); + count = 1; //该岛屿的第一块陆地被visit了 + + //对这个岛屿的所有都入队,除非上下左右都没有未访问的陆地 + while(!queue.isEmpty()){ + int curX = queue.peek().x; + int curY = queue.poll().y; + //对每块陆地都进行上下左右的入队和计算(遍历),自然就是按广度优先了 + for(int i = 0; i < 4; i++){ + int nextX = curX + dir[i][0]; + int nextY = curY + dir[i][1]; + if(nextX < 0 || nextY < 0 || nextX >= grid.length || nextY >= grid[0].length){ + continue; + } + if(!visited[nextX][nextY] && grid[nextX][nextY] == 1){ + count++; + queue.add(new pair(nextX, nextY)); + visited[nextX][nextY] = true; + } + } + } + } + + static class pair{ + int x; + int y; + pair(int x, int y){ + this.x = x; + this.y = y; + } + } + + public static void main(String[] args){ + Scanner in = new Scanner(System.in); + int n = in.nextInt(); + int m = in.nextInt(); + int[][] grid = new int[n][m]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + grid[i][j] = in.nextInt(); + } + } + int result = 0; + boolean[][] visited = new boolean[n][m]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < m; j++){ + if(!visited[i][j] && grid[i][j] == 1){ + visited[i][j] = true; + bfs(visited, i, j, grid); + result = Math.max(result, count); + } + } + } + System.out.println(result); + } +} +``` ### Python DFS @@ -389,6 +503,144 @@ func main() { ### Rust +DFS + +``` rust +use std::io; +use std::cmp; + +// 定义四个方向 +const DIRECTIONS: [(i32, i32); 4] = [(0, 1), (1, 0), (-1, 0), (0, -1)]; + +fn dfs(grid: &Vec>, visited: &mut Vec>, x: usize, y: usize, count: &mut i32) { + if visited[x][y] || grid[x][y] == 0 { + return; // 终止条件:已访问或者遇到海水 + } + visited[x][y] = true; // 标记已访问 + *count += 1; + + for &(dx, dy) in DIRECTIONS.iter() { + let new_x = x as i32 + dx; + let new_y = y as i32 + dy; + + // 检查边界条件 + if new_x >= 0 && new_x < grid.len() as i32 && new_y >= 0 && new_y < grid[0].len() as i32 { + dfs(grid, visited, new_x as usize, new_y as usize, count); + } + } +} + +fn main() { + let mut input = String::new(); + + // 读取 n 和 m + io::stdin().read_line(&mut input); + let dims: Vec = input.trim().split_whitespace().map(|s| s.parse().unwrap()).collect(); + let (n, m) = (dims[0], dims[1]); + + // 读取 grid + let mut grid = vec![]; + for _ in 0..n { + input.clear(); + io::stdin().read_line(&mut input); + let row: Vec = input.trim().split_whitespace().map(|s| s.parse().unwrap()).collect(); + grid.push(row); + } + + // 初始化访问记录 + let mut visited = vec![vec![false; m]; n]; + let mut result = 0; + + // 遍历所有格子 + for i in 0..n { + for j in 0..m { + if !visited[i][j] && grid[i][j] == 1 { + let mut count = 0; + dfs(&grid, &mut visited, i, j, &mut count); + result = cmp::max(result, count); + } + } + } + + // 输出结果 + println!("{}", result); +} + +``` +BFS +```rust +use std::io; +use std::collections::VecDeque; + +// 定义四个方向 +const DIRECTIONS: [(i32, i32); 4] = [(0, 1), (1, 0), (-1, 0), (0, -1)]; + +fn bfs(grid: &Vec>, visited: &mut Vec>, x: usize, y: usize) -> i32 { + let mut count = 0; + let mut queue = VecDeque::new(); + queue.push_back((x, y)); + visited[x][y] = true; // 标记已访问 + + while let Some((cur_x, cur_y)) = queue.pop_front() { + count += 1; // 增加计数 + + for &(dx, dy) in DIRECTIONS.iter() { + let new_x = cur_x as i32 + dx; + let new_y = cur_y as i32 + dy; + + // 检查边界条件 + if new_x >= 0 && new_x < grid.len() as i32 && new_y >= 0 && new_y < grid[0].len() as i32 { + let new_x_usize = new_x as usize; + let new_y_usize = new_y as usize; + + // 如果未访问且是陆地,加入队列 + if !visited[new_x_usize][new_y_usize] && grid[new_x_usize][new_y_usize] == 1 { + visited[new_x_usize][new_y_usize] = true; // 标记已访问 + queue.push_back((new_x_usize, new_y_usize)); + } + } + } + } + + count +} + +fn main() { + let mut input = String::new(); + + // 读取 n 和 m + io::stdin().read_line(&mut input).expect("Failed to read line"); + let dims: Vec = input.trim().split_whitespace().map(|s| s.parse().unwrap()).collect(); + let (n, m) = (dims[0], dims[1]); + + // 读取 grid + let mut grid = vec![]; + for _ in 0..n { + input.clear(); + io::stdin().read_line(&mut input).expect("Failed to read line"); + let row: Vec = input.trim().split_whitespace().map(|s| s.parse().unwrap()).collect(); + grid.push(row); + } + + // 初始化访问记录 + let mut visited = vec![vec![false; m]; n]; + let mut result = 0; + + // 遍历所有格子 + for i in 0..n { + for j in 0..m { + if !visited[i][j] && grid[i][j] == 1 { + let count = bfs(&grid, &mut visited, i, j); + result = result.max(count); + } + } + } + + // 输出结果 + println!("{}", result); +} + +``` ### Javascript