已开启
分治算法实现归并排序-湖北工业大学-殷渝-徐承志 #148
、创建于 2025年6月24日
分治算法实现归并排序-湖北工业大学-殷渝-徐承志 #148
已开启
共 87 个文件变更+85-15680
| @@ -0,0 +1,56 @@ | |||
| 1 | + | ||
| 2 | +func mergeSort(arr: [Int64]): [Int64] { | ||
| 3 | + // 递归终止条件:单个元素直接返回 | ||
| 4 | + if arr.size <= 1 { | ||
| 5 | + return arr | ||
| 6 | + } | ||
| 7 | + | ||
| 8 | + // 分解步骤:二分数组 | ||
| 9 | + let mid = arr.size / 2 | ||
| 10 | + let left = arr[0:mid] // 左子数组切片 | ||
| 11 | + let right = arr[mid:] // 右子数组切片 | ||
| 12 | + | ||
| 13 | + // 递归求解子问题 | ||
| 14 | + let sortedLeft = mergeSort(left) | ||
| 15 | + let sortedRight = mergeSort(right) | ||
| 16 | + | ||
| 17 | + // 合并步骤:合并有序数组 | ||
| 18 | + return merge(sortedLeft, sortedRight) | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +// 合并两个有序数组 | ||
| 22 | +func merge(left: [Int64], right: [Int64]): [Int64] { | ||
| 23 | + var result: [Int64] = [] | ||
| 24 | + var i = 0, j = 0 | ||
| 25 | + | ||
| 26 | + // 双指针遍历有序数组合并 | ||
| 27 | + while i < left.size && j < right.size { | ||
| 28 | + if left[i] < right[j] { | ||
| 29 | + result.append(left[i]) | ||
| 30 | + i += 1 | ||
| 31 | + } else { | ||
| 32 | + result.append(right[j]) | ||
| 33 | + j += 1 | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + // 添加剩余元素 | ||
| 38 | + while i < left.size { | ||
| 39 | + result.append(left[i]) | ||
| 40 | + i += 1 | ||
| 41 | + } | ||
| 42 | + while j < right.size { | ||
| 43 | + result.append(right[j]) | ||
| 44 | + j += 1 | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + return result | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +// 主函数 | ||
| 51 | +main() { | ||
| 52 | + let data = [38, 27, 43, 3, 9, 82, 10] | ||
| 53 | + let sorted = mergeSort(data) | ||
| 54 | + println("排序结果: ${sorted}") | ||
| 55 | + // 输出: [3, 9, 10, 27, 38, 43, 82] | ||
| 56 | +} | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | +算法框架 | ||
| 2 | + | ||
| 3 | +递归终止条件:数组长度 ≤ 1 时直接返回 | ||
| 4 | +分解: | ||
| 5 | +mid=⌊n/2⌋ | ||
| 6 | + | ||
| 7 | +,数组拆分为左右子区间 | ||
| 8 | +治理:递归调用 mergeSort 处理子问题 | ||
| 9 | +合并:merge() 函数实现有序数组的线性合并 | ||
| 10 | +时间复杂度分析 | ||
| 11 | + | ||
| 12 | +分解复杂度: | ||
| 13 | +O(1) | ||
| 14 | + | ||
| 15 | +(切片操作) | ||
| 16 | +子问题数量:每次递归产生 2 个子问题(二叉树) | ||
| 17 | +合并复杂度: | ||
| 18 | +O(n) | ||
| 19 | + | ||
| 20 | +(单次合并操作) | ||
| 21 | +总时间复杂度: | ||
| 22 | +T(n)=2T(n/2)+O(n)=O(nlogn) | ||
| 23 | + | ||
| 24 | +仓颉语言特性应用 | ||
| 25 | + | ||
| 26 | +切片操作:arr[0:mid] 语法实现高效数组分割 | ||
| 27 | +类型安全:[Int64] 显式声明数组类型 | ||
| 28 | +递归优化:编译器自动优化尾递归调用1 | ||
| 29 | +泛型支持:可修改为 <T: Comparable> 实现泛型排序 | ||
| @@ -1,7 +0,0 @@ | |||
| 1 | -# Exercises | ||
| 2 | - | ||
| 3 | -本仓收集仓颉算法题解程序。 | ||
| 4 | - | ||
| 5 | -提交要求: | ||
| 6 | - - 每道编程题放在不同的子目录下,目录以题目命名,优先使用中文。 | ||
| 7 | - - 在子目录下创建 `readme.md` 文件,其中填写题目描述,还可以包含解题指导等内容。 | ||
| @@ -1,25 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | -import std.math.* | ||
| 6 | - | ||
| 7 | -main(): Int32 { | ||
| 8 | - while(let Some(ln) <- Console.stdIn.readln()) { | ||
| 9 | - var params = ln.split(" ") | ||
| 10 | - if(params.size >= 1) { | ||
| 11 | - var x: Int32 = Int32.parse(params[0]) | ||
| 12 | - var n: Int32 = 0 | ||
| 13 | - while(x != 0) { | ||
| 14 | - n = n * 10 + x % 10 | ||
| 15 | - x = x / 10 | ||
| 16 | - } | ||
| 17 | - if(n > 2147483647 || n < -2147483648) { | ||
| 18 | - println(0) | ||
| 19 | - } else { | ||
| 20 | - println(n) | ||
| 21 | - } | ||
| 22 | - } | ||
| 23 | - } | ||
| 24 | - return 0 | ||
| 25 | -} | ||
| @@ -1,42 +0,0 @@ | |||
| 1 | -# 7. 整数反转(中等) | ||
| 2 | - | ||
| 3 | -给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。 | ||
| 4 | - | ||
| 5 | -如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。 | ||
| 6 | - | ||
| 7 | -假设环境不允许存储 64 位整数(有符号或无符号)。 | ||
| 8 | - | ||
| 9 | - | ||
| 10 | -示例 1: | ||
| 11 | -输入:x = 123 | ||
| 12 | -输出:321 | ||
| 13 | - | ||
| 14 | -示例 2: | ||
| 15 | -输入:x = -123 | ||
| 16 | -输出:-321 | ||
| 17 | - | ||
| 18 | -示例 3: | ||
| 19 | -输入:x = 120 | ||
| 20 | -输出:21 | ||
| 21 | - | ||
| 22 | -示例 4: | ||
| 23 | -输入:x = 0 | ||
| 24 | -输出:0 | ||
| 25 | - | ||
| 26 | - | ||
| 27 | -提示: | ||
| 28 | --2^31 <= x <= 2^31 - 1 | ||
| 29 | - | ||
| 30 | -## Java实现 | ||
| 31 | -```java | ||
| 32 | -class Solution { | ||
| 33 | - public int reverse(int x) { | ||
| 34 | - long n = 0; | ||
| 35 | - while(x != 0) { | ||
| 36 | - n = n * 10 + x % 10; | ||
| 37 | - x = x / 10; | ||
| 38 | - } | ||
| 39 | - return (int) (n > 2147483647 || n < -2147483648 ? 0 : n); | ||
| 40 | - } | ||
| 41 | -} | ||
| 42 | -``` | ||
| @@ -1,35 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * 第一题,两数之和 | ||
| 3 | - * https://leetcode.cn/problems/two-sum/ | ||
| 4 | -解题思路: | ||
| 5 | - 使用HashMap 存放信息,K:数字本身,V:数字下标 | ||
| 6 | - 遍历数组,每次用target - 当前数字 得到一个数,在map中查询是否有这个K | ||
| 7 | - 如果查到:返回当前数字的下标和查到的下标 | ||
| 8 | - 否则:将当前数字作为K,当前下标作为V,放进map中 | ||
| 9 | - | ||
| 10 | -时间、空间复杂度: | ||
| 11 | - O(n) O(n) | ||
| 12 | - | ||
| 13 | -涉及仓颉语法: | ||
| 14 | - 区间值:0..x | ||
| 15 | - HashMap的基本使用 | ||
| 16 | - if-let 判断枚举语法 | ||
| 17 | - */ | ||
| 18 | -package cangjieLeetcode.n1_n100 | ||
| 19 | -import std.collection.HashMap | ||
| 20 | -class SolutionN1 { | ||
| 21 | - func twoSum(nums: Array<Int64>, target: Int64): Array<Int64> { | ||
| 22 | - let m = HashMap<Int64,Int64>() | ||
| 23 | - let ans = [-1,-1] | ||
| 24 | - for(i in 0..nums.size) { | ||
| 25 | - if (let Some(x) <- m.get(target - nums[i])) { | ||
| 26 | - ans[0] = i | ||
| 27 | - ans[1] = x | ||
| 28 | - return ans | ||
| 29 | - } else { | ||
| 30 | - m.put(nums[i],i) | ||
| 31 | - } | ||
| 32 | - } | ||
| 33 | - ans | ||
| 34 | - } | ||
| 35 | -} | ||
| @@ -1,16 +0,0 @@ | |||
| 1 | - | ||
| 2 | -## 第一题,两数之和 | ||
| 3 | - * 题目描述: https://leetcode.cn/problems/two-sum/ | ||
| 4 | -## 解题思路: | ||
| 5 | - 使用HashMap存放信息,K:数字本身,V:数字下标 | ||
| 6 | - 遍历数组,每次用target - 当前数字 得到一个数,在map中查询是否有这个K | ||
| 7 | - 如果查到:返回当前数字的下标和查到的下标 | ||
| 8 | - 否则:将当前数字作为K,当前下标作为V,放进map中 | ||
| 9 | - | ||
| 10 | -## 时间、空间复杂度: | ||
| 11 | - O(n) O(n) | ||
| 12 | - | ||
| 13 | -## 仓颉语法: | ||
| 14 | -* 区间值:0..x | ||
| 15 | -* HashMap的基本使用 | ||
| 16 | -* if-let 判断枚举语法 | ||
| @@ -1,41 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * 第2题,两数相加 | ||
| 3 | - * https://leetcode.cn/problems/add-two-numbers/description/ | ||
| 4 | -解题思路: | ||
| 5 | - 迭代两个List,只要满足 l1不为None、l2不为None、进位不为0 其中条件之一,就一直迭代 | ||
| 6 | - 每次迭代,sum = l1的值(如果不为空) + l2的值(如果不为空)+ 进位 | ||
| 7 | - | ||
| 8 | -时间、空间复杂度: | ||
| 9 | - O(n) O(1) | ||
| 10 | - | ||
| 11 | -仓颉语法: | ||
| 12 | - Option类型的isSome()和isNone()语法 | ||
| 13 | - Option类型的??语法:a??b, 等同于:if(a.isNone()){b} else {T} | ||
| 14 | - */ | ||
| 15 | -package cangjieLeetcode.n1_n100 | ||
| 16 | -import cangjieLeetcode.common.ListNode | ||
| 17 | -class SolutionN2 { | ||
| 18 | - func addTwoNumbers(l1: ?ListNode, l2: ?ListNode): ?ListNode { | ||
| 19 | - var t = ListNode(0) | ||
| 20 | - var cur = t | ||
| 21 | - var l1cur = l1 | ||
| 22 | - var l2cur = l2 | ||
| 23 | - var carry = 0 | ||
| 24 | - while (l1cur.isSome() || l2cur.isSome() || carry != 0) { | ||
| 25 | - var sum = carry | ||
| 26 | - if(let Some(node) <- l1cur) { | ||
| 27 | - sum += node.val | ||
| 28 | - l1cur = node.next | ||
| 29 | - } | ||
| 30 | - if(let Some(node) <- l2cur) { | ||
| 31 | - sum += node.val | ||
| 32 | - l2cur = node.next | ||
| 33 | - } | ||
| 34 | - carry = sum / 10 | ||
| 35 | - cur.next = Some(ListNode(sum % 10)) | ||
| 36 | - cur = cur.next??ListNode(0) | ||
| 37 | - } | ||
| 38 | - t.next | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | -} | ||
| @@ -1,13 +0,0 @@ | |||
| 1 | - | ||
| 2 | -## 第2题,两数相加 | ||
| 3 | - * 题目描述:https://leetcode.cn/problems/add-two-numbers/description/ | ||
| 4 | -## 解题思路: | ||
| 5 | - 迭代两个List,只要满足 l1不为None、l2不为None、进位不为0 中条件之一,就一直迭代 | ||
| 6 | - 每次迭代,sum = l1的值(如果不为空) + l2的值(如果不为空)+ 进位 | ||
| 7 | - | ||
| 8 | -## 时间、空间复杂度: | ||
| 9 | - O(n) O(1) | ||
| 10 | - | ||
| 11 | -## 仓颉语法: | ||
| 12 | - Option类型的isSome()和isNone()语法 | ||
| 13 | - Option类型的??语法:a??b, 等同于:if(a.isNone()){b} else {T} | ||
| @@ -1,43 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | -3. 无重复字符的最长子串 | ||
| 3 | -https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/ | ||
| 4 | - | ||
| 5 | -解题思路: | ||
| 6 | - 滑动窗口,利用s中的每个byte作为右指针,每次查询window中是否已存在右指针指向的byte | ||
| 7 | - 如果存在,滑动左指针,并且滑过的路径全部设为false,直到滑倒右指针为止 | ||
| 8 | - 如果不存在,右指针会自然递增,左指针不变,右-左+1 更新ans的值(如果比原来的ans大) | ||
| 9 | -时间、空间复杂度: | ||
| 10 | - O(n) O(1) | ||
| 11 | -仓颉语法: | ||
| 12 | - 迭代器iterator()和enumerate()的基本使用,enumerate() 除了会包含T,还会包含迭代的次数(这里对应下标) | ||
| 13 | - 扩展语法的基本使用,本例为Int64扩展了max方法 | ||
| 14 | - 类型转换,UInt8 转换为 Int64 语法为 :Int64(x) | ||
| 15 | - */ | ||
| 16 | -package cangjieLeetcode.n1_n100 | ||
| 17 | -import std.collection.* | ||
| 18 | -class SolutionN3 { | ||
| 19 | - func lengthOfLongestSubstring(s: String): Int64 { | ||
| 20 | - var left = 0 | ||
| 21 | - var ans = 0 | ||
| 22 | - var window = Array<Bool>(128,{b=>false}) | ||
| 23 | - | ||
| 24 | - for ((right,c) in s.iterator().enumerate()) { | ||
| 25 | - let i = Int64(c) | ||
| 26 | - while(window[i]) { | ||
| 27 | - window[Int64(s[left])] = false | ||
| 28 | - left += 1 | ||
| 29 | - } | ||
| 30 | - window[i] = true | ||
| 31 | - ans = ans.max(right - left + 1) | ||
| 32 | - } | ||
| 33 | - ans | ||
| 34 | - } | ||
| 35 | -} | ||
| 36 | -extend Int64 { | ||
| 37 | - public func max(other:Int64):Int64 { | ||
| 38 | - if (this > other){ | ||
| 39 | - return this | ||
| 40 | - } | ||
| 41 | - return other | ||
| 42 | - } | ||
| 43 | -} | ||
| @@ -1,14 +0,0 @@ | |||
| 1 | -## 3. 无重复字符的最长子串 | ||
| 2 | -* 题目描述:https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/ | ||
| 3 | - | ||
| 4 | -## 解题思路: | ||
| 5 | - 滑动窗口,利用s中的每个byte作为右指针,每次查询window中是否已存在右指针指向的byte | ||
| 6 | - 如果存在,滑动左指针,并且滑过的路径全部设为false,直到滑倒右指针为止 | ||
| 7 | - 如果不存在,右指针会自然递增,左指针不变,右-左+1 更新ans的值(如果比原来的ans大) | ||
| 8 | -## 时间、空间复杂度: | ||
| 9 | - O(n) O(1) | ||
| 10 | -## 仓颉语法: | ||
| 11 | - 迭代器iterator()和enumerate()的基本使用, | ||
| 12 | - enumerate() 除了会包含T,还会包含迭代的次数(这里对应下标) | ||
| 13 | - 扩展语法的基本使用,本例为Int64扩展了max方法 | ||
| 14 | - 类型转换,UInt8 转换为 Int64 语法为 :Int64(x) | ||
| @@ -1,24 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | - | ||
| 6 | -main(): Int64 { | ||
| 7 | - while (let Some(line) <- Console.stdIn.readln()) { | ||
| 8 | - let params = line.split(" ") | ||
| 9 | - if(params.size == 0) { | ||
| 10 | - break; | ||
| 11 | - } else { | ||
| 12 | - let n: Int64 = Int64.parse(params[0]) | ||
| 13 | - let m: Int64 | ||
| 14 | - if(n%2 == 0) { | ||
| 15 | - m = n/2*(n+1) | ||
| 16 | - } else { | ||
| 17 | - m = (n+1)/2*n | ||
| 18 | - } | ||
| 19 | - Console.stdOut.writeln(m) | ||
| 20 | - Console.stdOut.writeln("") | ||
| 21 | - } | ||
| 22 | - } | ||
| 23 | - return 0 | ||
| 24 | -} | ||
| @@ -1,43 +0,0 @@ | |||
| 1 | -# HDU1001 | ||
| 2 | -Sum Problem | ||
| 3 | - | ||
| 4 | -## 题目地址 | ||
| 5 | -https://acm.hdu.edu.cn/showproblem.php?pid=1001 | ||
| 6 | - | ||
| 7 | -## 解题思路 | ||
| 8 | -这一题,当使用数学求和公式时,要考虑算n*(n+1)可能溢出的特殊情况 | ||
| 9 | - | ||
| 10 | -题目提示“You may assume the result will be in the range of 32 bit signed integer” | ||
| 11 | - | ||
| 12 | -可以认为求和结果是32位有符号整数,虽然n*(n+1)/2一定是32位有符号整数范围内,但n*(n+1)却未必是 | ||
| 13 | - | ||
| 14 | -所以应该想办法让除法先做,然后再做乘法,避免溢出 | ||
| 15 | - | ||
| 16 | -做除法的时候还应注意整除的问题,整数相加肯定还是整数 | ||
| 17 | - | ||
| 18 | -所以需要先判断是否能被2整除,避免丢失精度 | ||
| 19 | - | ||
| 20 | -## 【CPP实现】 | ||
| 21 | -```cpp | ||
| 22 | -// | ||
| 23 | -// Created by yezeyu on 2022/11/16. | ||
| 24 | -// | ||
| 25 | -// 不要小看这一题,当使用数学求和公式时,要考虑算n*(n+1)可能溢出的特殊情况 | ||
| 26 | -// 题目提示“You may assume the result will be in the range of 32 bit signed integer” | ||
| 27 | -// 可以认为求和结果是32位有符号整数,虽然n*(n+1)/2一定是32位有符号整数范围内,但n*(n+1)却未必是 | ||
| 28 | -// 所以应该想办法让除法先做,然后再做乘法,避免溢出 | ||
| 29 | -// 做除法的时候还应注意整除的问题,整数相加肯定还是整数 | ||
| 30 | -// 所以需要先判断是否能被2整除,避免丢失精度 | ||
| 31 | - | ||
| 32 | -#include <iostream> | ||
| 33 | - | ||
| 34 | -using namespace std; | ||
| 35 | - | ||
| 36 | -int main() { | ||
| 37 | - int n; | ||
| 38 | - while(~scanf("%d", &n) && n) { | ||
| 39 | - cout << (n%2 ? (n+1)/2*n : n/2*(n+1)) << endl << endl; | ||
| 40 | - } | ||
| 41 | - return 0; | ||
| 42 | -} | ||
| 43 | -``` | ||
| @@ -1,68 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | -import std.math.* | ||
| 6 | - | ||
| 7 | -main(): Int64 { | ||
| 8 | - if(let Some(ln) <- Console.stdIn.readln()) { | ||
| 9 | - var params = ln.split(" ") | ||
| 10 | - if(params.size >= 0) { | ||
| 11 | - let round = Int64.parse(params[0]) | ||
| 12 | - for(kase in 1..=round) { | ||
| 13 | - // 将两个大整数存入两个字符数组 | ||
| 14 | - // 先输入的会存在数组的低位,如数12345,a[0]=1,a[1]=2,...,a[4]=5 | ||
| 15 | - if(let Some(ln) <- Console.stdIn.readln()) { | ||
| 16 | - params = ln.split(" ") | ||
| 17 | - if(params.size >= 2) { | ||
| 18 | - let a: Array<String> = params[0].split("") | ||
| 19 | - let b: Array<String> = params[1].split("") | ||
| 20 | - let maxlen = max(a.size, b.size) | ||
| 21 | - let minlen = max(a.size, b.size) | ||
| 22 | - let r: Array<Int8> = Array<Int8>(maxlen + 1, item: 0) | ||
| 23 | - for(i in a.size-1 ..= 0:-1) { | ||
| 24 | - r[a.size - 1 - i] = Int8.parse(a[i]) // 被加数数组A倒序存入结果数组R,因为加法计算要从低位向高位计算 | ||
| 25 | - } | ||
| 26 | - var rLen = maxlen | ||
| 27 | - var carry:Int8 = 0 // 记录进位,初始为0 | ||
| 28 | - for(i in b.size-1 ..= 0:-1) { | ||
| 29 | - let sum = Int8.parse(b[i]) + r[b.size - 1 - i] + carry // 加数数组倒序按元素钰结果数组R对应位置元素相加 | ||
| 30 | - carry = sum / 10 | ||
| 31 | - r[b.size - 1 - i] = sum % 10 | ||
| 32 | - } | ||
| 33 | - // 被加数a与加数b长度不同的情况下,若有进位需继续计算 | ||
| 34 | - for(i in 0 ..= maxlen + 1 - minlen) { | ||
| 35 | - if(carry <= 0) { | ||
| 36 | - break | ||
| 37 | - } | ||
| 38 | - let sum = r[b.size + i] + carry | ||
| 39 | - carry = sum / 10 | ||
| 40 | - r[b.size + i] = sum % 10 | ||
| 41 | - if(b.size + i + 1 > rLen) { | ||
| 42 | - rLen = b.size + i + 1 | ||
| 43 | - } | ||
| 44 | - } | ||
| 45 | - // 按格式输出 | ||
| 46 | - println("Case ${kase}:") | ||
| 47 | - for(i in 0..a.size) { | ||
| 48 | - print(a[i]); | ||
| 49 | - } | ||
| 50 | - print(" + ") | ||
| 51 | - for(i in 0..b.size) { | ||
| 52 | - print(b[i]); | ||
| 53 | - } | ||
| 54 | - print(" = ") | ||
| 55 | - for(i in (rLen-1)..=0:-1) { | ||
| 56 | - print(r[i]); | ||
| 57 | - } | ||
| 58 | - println() | ||
| 59 | - if(kase < round) { | ||
| 60 | - println() | ||
| 61 | - } | ||
| 62 | - } | ||
| 63 | - } | ||
| 64 | - } | ||
| 65 | - } | ||
| 66 | - } | ||
| 67 | - return 0 | ||
| 68 | -} | ||
| @@ -1,117 +0,0 @@ | |||
| 1 | -# A + B Problem II | ||
| 2 | -Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) | ||
| 3 | - | ||
| 4 | -Total Submission(s): 589047 Accepted Submission(s): 112312 | ||
| 5 | - | ||
| 6 | - | ||
| 7 | -## Problem Description | ||
| 8 | -I have a very simple problem for you. Given two integers A and B, your job is to calculate the Sum of A + B. | ||
| 9 | - | ||
| 10 | - | ||
| 11 | -## Input | ||
| 12 | -The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line consists of two positive integers, A and B. Notice that the integers are very large, that means you should not process them by using 32-bit integer. You may assume the length of each integer will not exceed 1000. | ||
| 13 | - | ||
| 14 | - | ||
| 15 | -## Output | ||
| 16 | -For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line is the an equation "A + B = Sum", Sum means the result of A + B. Note there are some spaces int the equation. Output a blank line between two test cases. | ||
| 17 | - | ||
| 18 | - | ||
| 19 | -## Sample Input | ||
| 20 | -2 | ||
| 21 | -1 2 | ||
| 22 | -112233445566778899 998877665544332211 | ||
| 23 | - | ||
| 24 | - | ||
| 25 | -## Sample Output | ||
| 26 | -Case 1: | ||
| 27 | -1 + 2 = 3 | ||
| 28 | - | ||
| 29 | -Case 2: | ||
| 30 | -112233445566778899 + 998877665544332211 = 1111111111111111110 | ||
| 31 | - | ||
| 32 | - | ||
| 33 | -## Author | ||
| 34 | -Ignatius.L | ||
| 35 | - | ||
| 36 | -## 【CPP实现】 | ||
| 37 | -```cpp | ||
| 38 | -// Created by yezeyu on 2022/11/16. | ||
| 39 | -// HDU1002 | ||
| 40 | -// 超大整数加法运算 | ||
| 41 | -// 使用字符数组存储超大整数,再模拟加法计算 | ||
| 42 | - | ||
| 43 | -#include <iostream> | ||
| 44 | -#include <cstring> | ||
| 45 | - | ||
| 46 | -using namespace std; | ||
| 47 | - | ||
| 48 | -char a[1005], b[1005], r[1010]; | ||
| 49 | - | ||
| 50 | -// 出现较多数据时,使用scanf和printf的效率会更高 | ||
| 51 | -int main() { | ||
| 52 | - int round; | ||
| 53 | - scanf("%d", &round); | ||
| 54 | - for(int kase = 1; kase <= round; kase++) { | ||
| 55 | - // 使用scanf将两个大整数存入两个字符数组 | ||
| 56 | - // 先输入的会存在数组的低位,如数12345,a[0]=1,a[1]=2,...,a[4]=5 | ||
| 57 | - scanf("%s%s", a, b); | ||
| 58 | - // 清空结果数组,重置成全0 | ||
| 59 | - memset(r, 0, sizeof(r)); | ||
| 60 | - // 定义变量存储数值真实长度 | ||
| 61 | - int aLen, bLen, rLen; | ||
| 62 | - aLen = strlen(a); | ||
| 63 | - bLen = strlen(b); | ||
| 64 | - rLen = aLen; | ||
| 65 | - // 由于先输入的数字字符(数值的高位)会存在数组的低位,而计算需要进位,会增加数组长度, | ||
| 66 | - // 所以需要把其中一个大数的原数组“倒装”置入结果数组 | ||
| 67 | - for(int i = aLen - 1; i >= 0; i--) { | ||
| 68 | - r[aLen - 1 - i ] = a[i] - '0'; // 数字字符减字符0可以获得真实整数数值 | ||
| 69 | - } | ||
| 70 | - if(bLen > rLen) { | ||
| 71 | - rLen = bLen; | ||
| 72 | - } | ||
| 73 | - int carry = 0; // 记录进位,初始为0 | ||
| 74 | - for(int i = bLen - 1; i >= 0; i--) { | ||
| 75 | - r[bLen - 1 - i] += b[i] - '0' + carry; // 需要加上进位 | ||
| 76 | - carry = r[bLen - 1 - i] / 10; // 10进制,也可以使用变量 | ||
| 77 | - r[bLen - 1 - i] %= 10; | ||
| 78 | - } | ||
| 79 | - // a与b长度不同,若有进位则继续计算 | ||
| 80 | - for(int i = 0; carry > 0; i++) { | ||
| 81 | - r[bLen + i] += carry; | ||
| 82 | - carry = r[bLen + i] / 10; | ||
| 83 | - r[bLen + i] %= 10; | ||
| 84 | - // 判断是否需要更新结果数值长度 | ||
| 85 | - if(bLen + i + 1 > rLen) { | ||
| 86 | - rLen = bLen + i + 1; | ||
| 87 | - } | ||
| 88 | - } | ||
| 89 | - // 去除前导0 | ||
| 90 | - for(int i = rLen - 1; i >= 0; i--) { | ||
| 91 | - if(r[i]) { | ||
| 92 | - rLen = i + 1; | ||
| 93 | - break; | ||
| 94 | - } | ||
| 95 | - } | ||
| 96 | - | ||
| 97 | - // 按格式输出 | ||
| 98 | - printf("Case %d:\n", kase); | ||
| 99 | - for(int i = 0; i < aLen; i++) { | ||
| 100 | - printf("%c", a[i]); | ||
| 101 | - } | ||
| 102 | - printf(" + "); | ||
| 103 | - for(int i = 0; i < bLen; i++) { | ||
| 104 | - printf("%c", b[i]); | ||
| 105 | - } | ||
| 106 | - printf(" = "); | ||
| 107 | - for(int i = rLen - 1; i >= 0; i--) { | ||
| 108 | - printf("%c", r[i] + '0'); | ||
| 109 | - } | ||
| 110 | - printf("\n"); | ||
| 111 | - if(kase != round) { | ||
| 112 | - printf("\n"); | ||
| 113 | - } | ||
| 114 | - } | ||
| 115 | - return 0; | ||
| 116 | -} | ||
| 117 | -``` | ||
| @@ -1,85 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | -import std.math.* | ||
| 6 | - | ||
| 7 | -func swap(arr: Array<Int64>, n:Int64, m:Int64): Bool { | ||
| 8 | - // 交换数组中的两个元素 | ||
| 9 | - let temp:Int64 = arr[n] | ||
| 10 | - arr[n] = arr[m] | ||
| 11 | - arr[m] = temp | ||
| 12 | - return true; | ||
| 13 | -} | ||
| 14 | - | ||
| 15 | -func reverse(arr: Array<Int64>, first:Int64, last:Int64): Bool { | ||
| 16 | - // 反转数组中的一段元素 | ||
| 17 | - let temp: Array<Int64> = Array<Int64>(last-first+1, item:0) | ||
| 18 | - var tempIndex = 0 | ||
| 19 | - for(i in first..=last) { | ||
| 20 | - temp[tempIndex] = arr[i] | ||
| 21 | - tempIndex++ | ||
| 22 | - } | ||
| 23 | - temp.reverse() | ||
| 24 | - tempIndex = 0 | ||
| 25 | - for(i in first..=last) { | ||
| 26 | - arr[i] = temp[tempIndex] | ||
| 27 | - tempIndex++ | ||
| 28 | - } | ||
| 29 | - return true; | ||
| 30 | -} | ||
| 31 | - | ||
| 32 | -func next_permutation(arr: Array<Int64>): Bool { | ||
| 33 | - if(arr.size < 2) { | ||
| 34 | - // 只有一个元素或没有元素,直接返回false | ||
| 35 | - return false | ||
| 36 | - } else { | ||
| 37 | - let first = 0 | ||
| 38 | - let last = arr.size - 1 | ||
| 39 | - var i1 = last | ||
| 40 | - while(true) { | ||
| 41 | - // 从后向前查找第一个相邻的升序对 | ||
| 42 | - var i2 = i1 | ||
| 43 | - i1 -= 1 | ||
| 44 | - if(arr[i1] < arr[i2]) { | ||
| 45 | - // 如果前一个元素小于后一个元素 | ||
| 46 | - var j = last | ||
| 47 | - while(!(arr[i1] < arr[j])) { | ||
| 48 | - j -= 1 | ||
| 49 | - } | ||
| 50 | - swap(arr, i1, j) | ||
| 51 | - reverse(arr, i2, last) | ||
| 52 | - return true | ||
| 53 | - } | ||
| 54 | - if(i1 == first) { | ||
| 55 | - // 进行至最前面了,全部逆袭重排 | ||
| 56 | - reverse(arr, first, last) | ||
| 57 | - return false | ||
| 58 | - } | ||
| 59 | - } | ||
| 60 | - } | ||
| 61 | - return false | ||
| 62 | -} | ||
| 63 | - | ||
| 64 | -main(): Int64 { | ||
| 65 | - while(let Some(ln) <- Console.stdIn.readln()) { | ||
| 66 | - var params = ln.split(" ") | ||
| 67 | - if(params.size >= 2) { | ||
| 68 | - let n: Int64 = Int64.parse(params[0]) | ||
| 69 | - let m: Int64 = Int64.parse(params[1]) | ||
| 70 | - let a: Array<Int> = Array<Int64>(n, item:0) | ||
| 71 | - for(i in 0..n) { | ||
| 72 | - a[i] = i+1; | ||
| 73 | - } | ||
| 74 | - var b = 1 | ||
| 75 | - while(b != m && next_permutation(a)) { | ||
| 76 | - b++; | ||
| 77 | - } | ||
| 78 | - for(i in 0..n-1) { | ||
| 79 | - print("${a[i]} ") | ||
| 80 | - } | ||
| 81 | - println(a[n-1]) | ||
| 82 | - } | ||
| 83 | - } | ||
| 84 | - return 0 | ||
| 85 | -} | ||
| @@ -1,76 +0,0 @@ | |||
| 1 | -# Ignatius and the Princess II | ||
| 2 | -Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) | ||
| 3 | - | ||
| 4 | -Total Submission(s): 20753 Accepted Submission(s): 11850 | ||
| 5 | - | ||
| 6 | - | ||
| 7 | -## Problem Description | ||
| 8 | -Now our hero finds the door to the BEelzebub feng5166. He opens the door and finds feng5166 is about to kill our pretty Princess. But now the BEelzebub has to beat our hero first. feng5166 says, "I have three question for you, if you can work them out, I will release the Princess, or you will be my dinner, too." Ignatius says confidently, "OK, at last, I will save the Princess." | ||
| 9 | - | ||
| 10 | -"Now I will show you the first problem." feng5166 says, "Given a sequence of number 1 to N, we define that 1,2,3...N-1,N is the smallest sequence among all the sequence which can be composed with number 1 to N(each number can be and should be use only once in this problem). So it's easy to see the second smallest sequence is 1,2,3...N,N-1. Now I will give you two numbers, N and M. You should tell me the Mth smallest sequence which is composed with number 1 to N. It's easy, isn't is? Hahahahaha......" | ||
| 11 | -Can you help Ignatius to solve this problem? | ||
| 12 | - | ||
| 13 | - | ||
| 14 | -## Input | ||
| 15 | -The input contains several test cases. Each test case consists of two numbers, N and M(1<=N<=1000, 1<=M<=10000). You may assume that there is always a sequence satisfied the BEelzebub's demand. The input is terminated by the end of file. | ||
| 16 | - | ||
| 17 | - | ||
| 18 | -## Output | ||
| 19 | -For each test case, you only have to output the sequence satisfied the BEelzebub's demand. When output a sequence, you should print a space between two numbers, but do not output any spaces after the last number. | ||
| 20 | - | ||
| 21 | - | ||
| 22 | -## Sample Input | ||
| 23 | -6 4 | ||
| 24 | -11 8 | ||
| 25 | - | ||
| 26 | - | ||
| 27 | -## Sample Output | ||
| 28 | -1 2 3 5 6 4 | ||
| 29 | -1 2 3 4 5 6 7 9 8 11 10 | ||
| 30 | - | ||
| 31 | - | ||
| 32 | -## Author | ||
| 33 | -Ignatius.L | ||
| 34 | - | ||
| 35 | -## 【CPP实现】 | ||
| 36 | -```cpp | ||
| 37 | -// | ||
| 38 | -// Created by yezeyu on 2022/11/18. | ||
| 39 | -// HDU1027 | ||
| 40 | - | ||
| 41 | -#include <iostream> | ||
| 42 | -#include <algorithm> | ||
| 43 | -#include <cstring> | ||
| 44 | - | ||
| 45 | -#define maxN 1000 | ||
| 46 | - | ||
| 47 | -using namespace std; | ||
| 48 | - | ||
| 49 | -int a[maxN]; | ||
| 50 | - | ||
| 51 | -int main() { | ||
| 52 | - int n, m; | ||
| 53 | - while (cin >> n >> m) { | ||
| 54 | - // memset(a, 0, sizeof(a)); // 不需要memset了,因为数组长度是清楚的 | ||
| 55 | - for(int i = 1; i <= n; i++) { | ||
| 56 | - a[i] = i; | ||
| 57 | - } | ||
| 58 | - // 不需要排序了,因为赋值时已经按最小字典序排好了 | ||
| 59 | - int b = 1; | ||
| 60 | - do { | ||
| 61 | - | ||
| 62 | - } while (b != m && b++ && next_permutation(a+1, a+n+1)); | ||
| 63 | - // next_permutation的意思是下一个排列,与其相对的是prev_permutation | ||
| 64 | - // 基本定义如下: | ||
| 65 | - // next_permutaion(起始地址,末尾地址+1) | ||
| 66 | - // next_permutaion(起始地址,末尾地址+1,自定义排序) | ||
| 67 | - // 可以使用默认的升序排序,也可以使用自定义的排序方法 | ||
| 68 | - for(int i = 1; i < n; i++) { | ||
| 69 | - cout << a[i] << " "; // 最后一个元素后不需要空格 | ||
| 70 | - } | ||
| 71 | - cout << a[n] << endl; | ||
| 72 | - } | ||
| 73 | - return 0; | ||
| 74 | -} | ||
| 75 | -// 补充参考:https://blog.csdn.net/love20165104027/article/details/79809291 | ||
| 76 | -``` | ||
| @@ -1,3 +0,0 @@ | |||
| 1 | -本目录将收录我在学习《算法竞赛入门到进阶》一书时所遇例题的仓颉写法。 | ||
| 2 | - | ||
| 3 | - | ||
| @@ -1,3 +0,0 @@ | |||
| 1 | -本目录将收录我在学习《算法竞赛入门经典》一书时所遇例题的仓颉写法。 | ||
| 2 | - | ||
| 3 | - | ||
| @@ -1,24 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | -import std.math.* | ||
| 6 | -import std.format.* | ||
| 7 | - | ||
| 8 | - | ||
| 9 | -main(): Int64 { | ||
| 10 | - if(let Some(ln) <- Console.stdIn.readln()) { | ||
| 11 | - let params = ln.split(" ") | ||
| 12 | - if(params.size >= 2) { | ||
| 13 | - // 注意精度的选择,这里至少使用Float32类型,对应C++的double型,否则结果与答案不一致 | ||
| 14 | - let pi: Float32 = acos(-1.0); | ||
| 15 | - let r: Float32 = Float32.parse(params[0]) | ||
| 16 | - let h: Float32 = Float32.parse(params[1]) | ||
| 17 | - let area: Float32 = 2.0 * pi * r * r + 2.0 * pi * r * h | ||
| 18 | - // 注意题目中结果的精度 | ||
| 19 | - // 对于浮点数表示小数点后的有效数字位数,如果不指定,那么则打印六位小数,如果小于数值本身有效数字的长度,那就四舍五入,如果大于就补全,补全的不一定是 0 | ||
| 20 | - Console.stdOut.writeln(area.format(".3")) | ||
| 21 | - } | ||
| 22 | - } | ||
| 23 | - return 0 | ||
| 24 | -} | ||
| @@ -1,38 +0,0 @@ | |||
| 1 | -# 例题1-1 圆柱体的表面积 | ||
| 2 | -输入底面半径r和高h,输出圆柱体的表面积,保留3位小数,格式见样例。 | ||
| 3 | - | ||
| 4 | -样例输入: | ||
| 5 | -3.5 9 | ||
| 6 | - | ||
| 7 | -样例输出: | ||
| 8 | -Area = 274.889 | ||
| 9 | - | ||
| 10 | -## 【分析】 | ||
| 11 | -圆柱体的表面积由3部分组成:上底面积、下底面积和侧面积。由于上下底面积相等,完整的公式可以写成:表面积=底面积×2+侧面积。根据几何知识,底面积=πr2,侧面积=2πrh | ||
| 12 | - | ||
| 13 | -这是本书中第一个完整的“竞赛题目”,因为和正规比赛一样,题目中包含着输入输出格式规定,还有样例数据。大多数的算法竞赛包含如下一些相同的“游戏规则”。 | ||
| 14 | - | ||
| 15 | -首先,选手程序的执行是自动完成的,没有人工干预。不要在用户输入之前打印提示信息(例如“Please input n:”),这不仅不会为程序赢得更高的“界面友好分”,反而会让程序丢掉大量的(甚至所有的)分数——这些提示信息会被当作输出数据的一部分。例如,刚才的程序如果加上了“友好提示”,输出信息将变成: | ||
| 16 | - | ||
| 17 | -``` | ||
| 18 | -Please input n: | ||
| 19 | -Area = 274.889 | ||
| 20 | -``` | ||
| 21 | - | ||
| 22 | -比标准答案多了整整一行! | ||
| 23 | - | ||
| 24 | -其次,不要让程序“按任意键退出”,因为不会有人来“按任意键”的。不少早期的C语言教材会建议在程序的最后添加这样一条语句来“观察输出结果”,但注意千万不要在算法竞赛中这样做。 | ||
| 25 | - | ||
| 26 | -## 【C实现】 | ||
| 27 | -```c | ||
| 28 | -#include <stdio.h> | ||
| 29 | -#include <math.h> | ||
| 30 | - | ||
| 31 | -int main() { | ||
| 32 | - const double pi = acos(-1.0); | ||
| 33 | - double r = 0.0, h = 0.0; | ||
| 34 | - scanf("%lf%lf", &r, &h); | ||
| 35 | - printf("Area = %.3f", 2 * pi * r * r + 2 * pi * r * h); | ||
| 36 | - return 0; | ||
| 37 | -} | ||
| 38 | -``` | ||
| @@ -1,35 +0,0 @@ | |||
| 1 | -import std.console.* | ||
| 2 | -import std.convert.* | ||
| 3 | - | ||
| 4 | -// n 代表“柱子上的金片”, source = "A"(源柱), auxiliary = "B"(辅助柱), target = "C"(目标柱) | ||
| 5 | -func hanoi(n: UInt8, source: String , auxiliary: String, target: String): Int64 { | ||
| 6 | - if(n == 0 ) { | ||
| 7 | - println("no disk") | ||
| 8 | - } | ||
| 9 | - if (n == 1) { | ||
| 10 | - println("Move disk 1 from ${source} to ${target}") | ||
| 11 | - return 1 | ||
| 12 | - } | ||
| 13 | - var i = 1 | ||
| 14 | - i += hanoi(n - 1, source, target, auxiliary) | ||
| 15 | - println("Move disk ${n} from ${source} to ${target}") | ||
| 16 | - i += hanoi(n - 1, auxiliary, source, target) | ||
| 17 | - return i | ||
| 18 | -} | ||
| 19 | - | ||
| 20 | -main (args: Array<String>): Int64 { | ||
| 21 | - Console.stdOut.write("请输入一个范围在[0-256)的正整数: ") | ||
| 22 | - if (let Some(line) <- Console.stdIn.readln()) { | ||
| 23 | - try { | ||
| 24 | - let n = UInt8.parse(line) | ||
| 25 | - let count = hanoi(n, "A", "B", "C") | ||
| 26 | - println("\n移动次数: ${count}") | ||
| 27 | - } catch (e: IllegalArgumentException) { | ||
| 28 | - println("请输入范围在[0-256)的正整数") | ||
| 29 | - } catch (e: StackOverflowError) { | ||
| 30 | - // e.printStackTrace() | ||
| 31 | - println("An exception has occurred:StackOverflowError, 请输入较小的n") | ||
| 32 | - } | ||
| 33 | - } | ||
| 34 | - return 0 | ||
| 35 | -} | ||
| @@ -1,29 +0,0 @@ | |||
| 1 | -# 【汉诺塔(Tower of Hanoi)】 | ||
| 2 | - | ||
| 3 | -## 背景由来 | ||
| 4 | - 在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。 | ||
| 5 | - | ||
| 6 | - 不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n)。显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。 | ||
| 7 | - | ||
| 8 | - n=64时,假如每秒钟移动一次,共需多长时间呢? | ||
| 9 | - | ||
| 10 | - --以上描述摘自百度百科-- | ||
| 11 | -## 输入格式 | ||
| 12 | - | ||
| 13 | -一个范围在[0-256)的正整数 $n$ 代表“柱子上大小不同的金片个数”,"A", "B", "C" 分别代表三根柱子。 | ||
| 14 | -### 样例输入 | ||
| 15 | -``` | ||
| 16 | -3 | ||
| 17 | -``` | ||
| 18 | -### 样例输出 | ||
| 19 | -``` | ||
| 20 | -Move disk 1 from A to C | ||
| 21 | -Move disk 2 from A to B | ||
| 22 | -Move disk 1 from C to B | ||
| 23 | -Move disk 3 from A to C | ||
| 24 | -Move disk 1 from B to A | ||
| 25 | -Move disk 2 from B to C | ||
| 26 | -Move disk 1 from A to C | ||
| 27 | - | ||
| 28 | -移动次数: 7 | ||
| 29 | -``` | ||
| @@ -1,19 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | -import std.math.* | ||
| 6 | - | ||
| 7 | - | ||
| 8 | -main(): Int64 { | ||
| 9 | - while (let Some(line) <- Console.stdIn.readln()) { | ||
| 10 | - let params = line.split(" ") | ||
| 11 | - if(params.size == 0 || Int.parse(params[0]) <= 0) { | ||
| 12 | - Console.stdOut.writeln("Invalid input") | ||
| 13 | - } else { | ||
| 14 | - let k: Float64 = Float64.parse(params[0]) | ||
| 15 | - Console.stdOut.writeln(Int64(floor(exp(k-0.5772156649) + 0.5))) | ||
| 16 | - } | ||
| 17 | - } | ||
| 18 | - return 0 | ||
| 19 | -} | ||
| @@ -1,39 +0,0 @@ | |||
| 1 | -# [NOIP2002 普及组] 级数求和 | ||
| 2 | - | ||
| 3 | -## 题目描述 | ||
| 4 | - | ||
| 5 | -已知:$S_n= 1+\dfrac{1}{2}+\dfrac{1}{3}+…+\dfrac{1}{n}$。显然对于任意一个整数 $k$,当 $n$ 足够大的时候,$S_n>k$。 | ||
| 6 | - | ||
| 7 | -现给出一个整数 $k$,要求计算出一个最小的 $n$,使得 $S_n>k$。 | ||
| 8 | - | ||
| 9 | -## 输入格式 | ||
| 10 | - | ||
| 11 | -一个正整数 $k$。 | ||
| 12 | - | ||
| 13 | -## 输出格式 | ||
| 14 | - | ||
| 15 | -一个正整数 $n$。 | ||
| 16 | - | ||
| 17 | -## 样例 #1 | ||
| 18 | - | ||
| 19 | -### 样例输入 #1 | ||
| 20 | - | ||
| 21 | -``` | ||
| 22 | -1 | ||
| 23 | -``` | ||
| 24 | - | ||
| 25 | -### 样例输出 #1 | ||
| 26 | - | ||
| 27 | -``` | ||
| 28 | -2 | ||
| 29 | -``` | ||
| 30 | - | ||
| 31 | -## 提示 | ||
| 32 | - | ||
| 33 | -**【数据范围】** | ||
| 34 | - | ||
| 35 | -对于 $100\%$ 的数据,$1\le k \le 15$。 | ||
| 36 | - | ||
| 37 | -**【题目来源】** | ||
| 38 | - | ||
| 39 | -NOIP 2002 普及组第一题 | ||
| @@ -1,28 +0,0 @@ | |||
| 1 | -import std.core.* | ||
| 2 | -import std.console.* | ||
| 3 | -import std.collection.* | ||
| 4 | -import std.convert.* | ||
| 5 | - | ||
| 6 | -var height: Int64 = 0; | ||
| 7 | - | ||
| 8 | -func parseResult(s: String): Int64 { | ||
| 9 | - let appleHeight = Int.parse(s); | ||
| 10 | - if (appleHeight > height) { | ||
| 11 | - return 0 | ||
| 12 | - } | ||
| 13 | - return 1 | ||
| 14 | -} | ||
| 15 | - | ||
| 16 | -main(): Int64 { | ||
| 17 | - if (let Some(firstLine) <- Console.stdIn.readln()) { | ||
| 18 | - if (let Some(secondLine) <- Console.stdIn.readln()) { | ||
| 19 | - height = Int.parse(secondLine.split(" ")[0]) + 30 | ||
| 20 | - let resultMap = map(parseResult)(firstLine.split(" ")) | ||
| 21 | - let sum = reduce({a: Int64, b: Int64 => a + b})(resultMap) | ||
| 22 | - if (let Some(r) <- sum) { | ||
| 23 | - Console.stdOut.writeln(r) | ||
| 24 | - } | ||
| 25 | - } | ||
| 26 | - } | ||
| 27 | - return 0 | ||
| 28 | -} | ||
| @@ -1,37 +0,0 @@ | |||
| 1 | -# [NOIP2005 普及组] 陶陶摘苹果 | ||
| 2 | - | ||
| 3 | -## 题目描述 | ||
| 4 | - | ||
| 5 | -陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 $10$ 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 $30$ 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 | ||
| 6 | - | ||
| 7 | - | ||
| 8 | -现在已知 $10$ 个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。 | ||
| 9 | - | ||
| 10 | -## 输入格式 | ||
| 11 | - | ||
| 12 | -输入包括两行数据。第一行包含 $10$ 个 $100$ 到 $200$ 之间(包括 $100$ 和 $200$)的整数(以厘米为单位)分别表示 $10$ 个苹果到地面的高度,两个相邻的整数之间用一个空格隔开。第二行只包括一个 $100$ 到 $120$ 之间(包含 $100$ 和 $120$)的整数(以厘米为单位),表示陶陶把手伸直的时候能够达到的最大高度。 | ||
| 13 | - | ||
| 14 | -## 输出格式 | ||
| 15 | - | ||
| 16 | -输出包括一行,这一行只包含一个整数,表示陶陶能够摘到的苹果的数目。 | ||
| 17 | - | ||
| 18 | -## 样例 #1 | ||
| 19 | - | ||
| 20 | -### 样例输入 #1 | ||
| 21 | - | ||
| 22 | -``` | ||
| 23 | -100 200 150 140 129 134 167 198 200 111 | ||
| 24 | -110 | ||
| 25 | -``` | ||
| 26 | - | ||
| 27 | -### 样例输出 #1 | ||
| 28 | - | ||
| 29 | -``` | ||
| 30 | -5 | ||
| 31 | -``` | ||
| 32 | - | ||
| 33 | -## 提示 | ||
| 34 | - | ||
| 35 | -**【题目来源】** | ||
| 36 | - | ||
| 37 | -NOIP 2005 普及组第一题 | ||
| @@ -1,365 +0,0 @@ | |||
| 1 | -package cangJieTest | ||
| 2 | - | ||
| 3 | - | ||
| 4 | -import std.math.* | ||
| 5 | -import std.reflect.* | ||
| 6 | -import std.collection.* | ||
| 7 | - | ||
| 8 | - | ||
| 9 | -let features = HashMap<String, (String, String)>([("头发",("长", "短")), ("声音",("粗", "细"))]) | ||
| 10 | - | ||
| 11 | - | ||
| 12 | -/** 决策树节点类 | ||
| 13 | -*/ | ||
| 14 | -class decisionTreeNode{ | ||
| 15 | - var node_class:String="" | ||
| 16 | - var valueSize=0 | ||
| 17 | - var node_value_array_list=ArrayList<String>() | ||
| 18 | - var nextNode=ArrayList<?decisionTreeNode>() | ||
| 19 | - | ||
| 20 | - public func traverse(): Int { | ||
| 21 | - print("${node_class} ${valueSize} ") | ||
| 22 | - for(i in 0..valueSize){ | ||
| 23 | - print("${node_value_array_list[i]} ") | ||
| 24 | - if(! nextNode[i].isNone()){ | ||
| 25 | - nextNode[i].getOrThrow().traverse() | ||
| 26 | - } | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - println() | ||
| 30 | - return 0 | ||
| 31 | - } | ||
| 32 | -} | ||
| 33 | - | ||
| 34 | - | ||
| 35 | -/** 创造数据集 | ||
| 36 | - | ||
| 37 | -返回数据集和特征列表 | ||
| 38 | -*/ | ||
| 39 | -func createDataSet1() { | ||
| 40 | - | ||
| 41 | - let dataSet = ArrayList<ArrayList<String>>([ArrayList(['长', '粗', '男']), | ||
| 42 | - ArrayList(['短', '粗', '男']), | ||
| 43 | - ArrayList(['短', '粗', '男']), | ||
| 44 | - ArrayList(['长', '细', '女']), | ||
| 45 | - ArrayList(['短', '细', '女']), | ||
| 46 | - ArrayList(['短', '粗', '女']), | ||
| 47 | - ArrayList(['长', '粗', '女']), | ||
| 48 | - ArrayList(['长', '粗', '女'])]) | ||
| 49 | - // println(TypeInfo.of(dataSet).name) | ||
| 50 | - | ||
| 51 | - let featuresList = ArrayList(['头发', '声音']) // 两个特征 | ||
| 52 | - // println(TypeInfo.of(featuresList).name) | ||
| 53 | - | ||
| 54 | - // println("dataset: ${dataSet}") | ||
| 55 | - // println("labels: ${featuresList}") | ||
| 56 | - return (dataSet, featuresList) | ||
| 57 | -} | ||
| 58 | - | ||
| 59 | - | ||
| 60 | -/** 计算数据集的熵(entropy) | ||
| 61 | - | ||
| 62 | -*/ | ||
| 63 | -func calcShannonEnt(dataSet: ArrayList< ArrayList<String> >): Float64 { | ||
| 64 | - let numEntries = dataSet.size // 数据条数 | ||
| 65 | - | ||
| 66 | - // 统计有多少个类以及每个类的数量 | ||
| 67 | - var featureValueCounts = HashMap<String, Int64>() | ||
| 68 | - for (featVector in dataSet) { | ||
| 69 | - let currentLabel=featVector[dataSet[0].size-1] // 每行数据的最后一个字(类别) | ||
| 70 | - if (! featureValueCounts.contains(currentLabel)) { | ||
| 71 | - featureValueCounts[currentLabel]=0 | ||
| 72 | - } | ||
| 73 | - featureValueCounts[currentLabel]+=1 | ||
| 74 | - } | ||
| 75 | - // println("featureValueCounts: ${featureValueCounts}") | ||
| 76 | - | ||
| 77 | - var shannonEntropy:Float64=0.0 | ||
| 78 | - for ((key,val) in featureValueCounts){ | ||
| 79 | - var prob = Float64(val)/Float64(numEntries) // 计算单个类的熵值 | ||
| 80 | - // println("prob:${prob}") | ||
| 81 | - | ||
| 82 | - shannonEntropy-=prob*logBase(prob, 2.0) // 累加每个类的熵值 | ||
| 83 | - } | ||
| 84 | - | ||
| 85 | - // println("shannonEntropy: ${shannonEntropy}") | ||
| 86 | - // println("\n") | ||
| 87 | - return shannonEntropy | ||
| 88 | -} | ||
| 89 | - | ||
| 90 | - | ||
| 91 | -/** 按某个特征的值切分数据集 | ||
| 92 | - | ||
| 93 | -*/ | ||
| 94 | -func splitDataSet(dataSet:ArrayList<ArrayList<String>>, axis:Int64, value:String) { | ||
| 95 | - var retDataSet=ArrayList<ArrayList<String>>() | ||
| 96 | - // println("retDataSet: ${retDataSet}") | ||
| 97 | - | ||
| 98 | - for (featVec in dataSet) { | ||
| 99 | - if (featVec[axis] == value){ | ||
| 100 | - let tmp=featVec.clone() //复制产生新的数据,不能改变原有dataset的值。 | ||
| 101 | - tmp.remove(axis) | ||
| 102 | - retDataSet.append(tmp) | ||
| 103 | - } | ||
| 104 | - } | ||
| 105 | - | ||
| 106 | - // println("axis: ${axis}") | ||
| 107 | - // println("retDataSet: ${retDataSet}") | ||
| 108 | - // println() | ||
| 109 | - return retDataSet | ||
| 110 | -} | ||
| 111 | - | ||
| 112 | - | ||
| 113 | -/** 选择最优的分类特征 | ||
| 114 | - | ||
| 115 | -*/ | ||
| 116 | -func chooseBestFeatureForSplit(dataSet:ArrayList<ArrayList<String>>):Int64 { | ||
| 117 | - let numFeatures = dataSet[0].size - 1 | ||
| 118 | - // println("numFeatures: ${numFeatures}") | ||
| 119 | - | ||
| 120 | - let baseEntropy = calcShannonEnt(dataSet) // 原始的熵 | ||
| 121 | - var bestInfoGain = 0.0 // 最好的信息增益 | ||
| 122 | - var bestFeature = -1 // 最好的特性 | ||
| 123 | - | ||
| 124 | - for (i in 0..numFeatures){ | ||
| 125 | - var featureValueList = ArrayList<String>() | ||
| 126 | - // [example[i] for example in dataSet] | ||
| 127 | - for (example in dataSet) { | ||
| 128 | - featureValueList.append(example[i]) | ||
| 129 | - } | ||
| 130 | - | ||
| 131 | - let uniqueVals = HashSet(featureValueList) | ||
| 132 | - // println("uniqueVals: ${uniqueVals}") | ||
| 133 | - | ||
| 134 | - | ||
| 135 | - var newEntropy = 0.0 | ||
| 136 | - for (value in uniqueVals) { | ||
| 137 | - var subDataSet = splitDataSet(dataSet, i, value) | ||
| 138 | - var prob = Float64(subDataSet.size) / Float64(dataSet.size) | ||
| 139 | - newEntropy += prob * calcShannonEnt(subDataSet) // 按特征分类后的熵 | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - var infoGain = baseEntropy - newEntropy // 原始熵与按特征分类后的熵的差值,即信息增益。 | ||
| 143 | - | ||
| 144 | - if (infoGain > bestInfoGain) { // 若按某特征划分后,熵值减少的最大,则次特征为最优分类特征。 | ||
| 145 | - bestInfoGain = infoGain | ||
| 146 | - bestFeature = i | ||
| 147 | - } | ||
| 148 | - } | ||
| 149 | - | ||
| 150 | - // print("bestFeature: ${bestFeature}" ) | ||
| 151 | - // print("\n") | ||
| 152 | - return bestFeature | ||
| 153 | -} | ||
| 154 | - | ||
| 155 | - | ||
| 156 | -/**元素比较函数。 | ||
| 157 | - | ||
| 158 | -对两个元组的比较。 | ||
| 159 | -用于本程序中后面的列表类型排序函数中的回调函数。 | ||
| 160 | -*/ | ||
| 161 | -func comparator_00(value0:(String, Int64), value1:(String, Int64)): Ordering { | ||
| 162 | - if (value0[1] < value1[1]) { | ||
| 163 | - return Ordering.LT | ||
| 164 | - } else if (value0[1] == value1[1]) { | ||
| 165 | - return Ordering.EQ | ||
| 166 | - } else { | ||
| 167 | - return Ordering.GT | ||
| 168 | - } | ||
| 169 | -} | ||
| 170 | - | ||
| 171 | - | ||
| 172 | -/** 通过对分类后的类别按数量进行排序得到占多数的多数派的类别。 | ||
| 173 | - | ||
| 174 | -比如: | ||
| 175 | -classList: ArrayList<String>(['男', '男', '女']) | ||
| 176 | -classCountMap: {'男': 2, '女': 1} | ||
| 177 | -sortedClassCount: [('男', 2), ('女', 1)] | ||
| 178 | -sortedClassCount[0][0]: 男,即拥有最大数值的那个类别 | ||
| 179 | -*/ | ||
| 180 | -func getMajorityClassByCount(classList:ArrayList<String>) { | ||
| 181 | - // println("classList: ${classList}") | ||
| 182 | - | ||
| 183 | - var classCountMap=HashMap<String, Int64>() | ||
| 184 | - for (cls in classList){ | ||
| 185 | - if (! classCountMap.contains(cls)){ | ||
| 186 | - classCountMap[cls]=0 | ||
| 187 | - } | ||
| 188 | - classCountMap[cls]+=1 | ||
| 189 | - } | ||
| 190 | - // println("classCountMap: ${classCountMap}") | ||
| 191 | - | ||
| 192 | - | ||
| 193 | - var array00:Array<(String, Int64)> = classCountMap.toArray() | ||
| 194 | - | ||
| 195 | - var sortedClassCount=ArrayList(array00) | ||
| 196 | - sortedClassCount.sortBy(stable:false, comparator:comparator_00) | ||
| 197 | - sortedClassCount.reverse() | ||
| 198 | - | ||
| 199 | - // println("sortedClassCount[0][0]: ${sortedClassCount[0][0]}") | ||
| 200 | - // print("\n") | ||
| 201 | - return sortedClassCount[0][0] | ||
| 202 | -} | ||
| 203 | - | ||
| 204 | - | ||
| 205 | -/** 创建一棵决策树 | ||
| 206 | - | ||
| 207 | -例:{'声音': {'粗': {'头发': {'短': '男', '长': '女'}}, '细': '女'}} | ||
| 208 | -*/ | ||
| 209 | -func createDecisionTree_ID3(dataSet:ArrayList<ArrayList<String>>, featuresList:ArrayList<String>): decisionTreeNode { | ||
| 210 | - println("dataSet: ${dataSet}") | ||
| 211 | - println("featuresList: ${featuresList}") | ||
| 212 | - println() | ||
| 213 | - | ||
| 214 | - // 0.创建一个空决策树 | ||
| 215 | - var myTree = decisionTreeNode() | ||
| 216 | - | ||
| 217 | - // 1.从数据集得到最后一列的类别的列表,即获取数据集中最后一列的数值构成一个列表,是由“男”、“女”组成的列表。 | ||
| 218 | - var classList=ArrayList<String>() | ||
| 219 | - for (record in dataSet) { | ||
| 220 | - classList.append(record[record.size-1]) | ||
| 221 | - } | ||
| 222 | - | ||
| 223 | - // 2.如果只有一种类别,则函数直接返回该类型,作为决策值。如“男“。 | ||
| 224 | - var count=0 | ||
| 225 | - let class00=classList[0] | ||
| 226 | - | ||
| 227 | - for(i in classList){ | ||
| 228 | - if (i==classList[0]){ | ||
| 229 | - count++ | ||
| 230 | - } | ||
| 231 | - } | ||
| 232 | - | ||
| 233 | - if (count == classList.size){ | ||
| 234 | - myTree.node_class="性别" | ||
| 235 | - myTree.valueSize++ | ||
| 236 | - myTree.node_value_array_list.append(classList[0]) | ||
| 237 | - myTree.nextNode.append(None) | ||
| 238 | - return myTree | ||
| 239 | - } | ||
| 240 | - | ||
| 241 | - // 3.如果只有类别没有特征值,则直接统计各类别的数值,返回数值最大的那个类别,作为默认决策值。如[('男', 2), ('女', 1)]决策为“男”。 | ||
| 242 | - if (dataSet[0].size == 1) { | ||
| 243 | - myTree.node_class="性别" | ||
| 244 | - myTree.valueSize++ | ||
| 245 | - myTree.node_value_array_list.append(getMajorityClassByCount(classList)) | ||
| 246 | - myTree.nextNode.append(None) | ||
| 247 | - return myTree | ||
| 248 | - } | ||
| 249 | - | ||
| 250 | - // 4.选择最优特征 | ||
| 251 | - let bestFeature:Int64 = chooseBestFeatureForSplit(dataSet) | ||
| 252 | - let bestFeatureName = featuresList[bestFeature] | ||
| 253 | - println("bestFeatureName: ${bestFeatureName}") | ||
| 254 | - | ||
| 255 | - // 5.获得数据集中的当前最好特性对应的所有取值而后精简为集合 | ||
| 256 | - var featureValues = ArrayList<String>() | ||
| 257 | - for (record in dataSet){ | ||
| 258 | - featureValues.append(record[bestFeature]) | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - var featureValuesSet = HashSet(featureValues) | ||
| 262 | - // println("uniqueValues: ${featureValuesSet}") | ||
| 263 | - | ||
| 264 | - featuresList.remove(bestFeature) | ||
| 265 | - | ||
| 266 | - // 6.递归建立整棵决策树 | ||
| 267 | - for (value in featureValuesSet) { | ||
| 268 | - println("value: ${value}") | ||
| 269 | - | ||
| 270 | - var subDataSet = splitDataSet(dataSet, bestFeature, value) | ||
| 271 | - println("subDataSet: ${subDataSet}") | ||
| 272 | - | ||
| 273 | - var subFeaturesList = featuresList[..] | ||
| 274 | - println("subFeaturesList: ${subFeaturesList}") | ||
| 275 | - println("\n") | ||
| 276 | - | ||
| 277 | - myTree.node_class = bestFeatureName | ||
| 278 | - myTree.valueSize++ | ||
| 279 | - myTree.node_value_array_list.append(value) | ||
| 280 | - | ||
| 281 | - myTree.nextNode.append(createDecisionTree_ID3(subDataSet, subFeaturesList)) | ||
| 282 | - | ||
| 283 | - } | ||
| 284 | - | ||
| 285 | - print("\n") | ||
| 286 | - | ||
| 287 | - return myTree | ||
| 288 | -} | ||
| 289 | - | ||
| 290 | - | ||
| 291 | -/** 输入数据,通过决策树决策一个结果 | ||
| 292 | - | ||
| 293 | -参数: | ||
| 294 | -data = ArrayList(["粗","长"]) | ||
| 295 | -feature = ArrayList(["声音", "头发"]) | ||
| 296 | -decisionTree,一个类,结果的json展示: {'声音': {'细': '女', '粗': {'头发': {'短': '男', '长': '女'}}}} | ||
| 297 | -*/ | ||
| 298 | -func decideByDecisionTree(data:ArrayList<String>, features:ArrayList<String>, decisionTree:decisionTreeNode): String { | ||
| 299 | - | ||
| 300 | - if (data.size < features.size){ | ||
| 301 | - println("错误:输入的数据的元素数目少于决策树的特征数目,异常退出。") | ||
| 302 | - return "Error:。" | ||
| 303 | - } | ||
| 304 | - | ||
| 305 | - if(! ["粗","细"].contains(data[0])){ | ||
| 306 | - println("Error:输入的数据不是标准数据。") | ||
| 307 | - return "Error" | ||
| 308 | - } | ||
| 309 | - | ||
| 310 | - if(! ["长","短"].contains(data[1])){ | ||
| 311 | - println("Error:输入的数据不是标准数据。") | ||
| 312 | - return "Error" | ||
| 313 | - } | ||
| 314 | - | ||
| 315 | - var tmpTree = decisionTree | ||
| 316 | - | ||
| 317 | - for(i in 0..data.size){ | ||
| 318 | - for(j in 0..tmpTree.valueSize){ | ||
| 319 | - if(data[i] == tmpTree.node_value_array_list[j]) { | ||
| 320 | - tmpTree = tmpTree.nextNode[j].getOrThrow() | ||
| 321 | - break | ||
| 322 | - } | ||
| 323 | - } | ||
| 324 | - } | ||
| 325 | - | ||
| 326 | - println("决策结果:${tmpTree.node_value_array_list[0]}") | ||
| 327 | - return tmpTree.node_value_array_list[0] | ||
| 328 | -} | ||
| 329 | - | ||
| 330 | - | ||
| 331 | -/** main function | ||
| 332 | - | ||
| 333 | -*/ | ||
| 334 | -main(): Int8 { | ||
| 335 | - | ||
| 336 | - // 0.创造示例数据 | ||
| 337 | - let tmpTuple = createDataSet1() | ||
| 338 | - let dataSet = tmpTuple[0] | ||
| 339 | - let featuresList = tmpTuple[1] | ||
| 340 | - | ||
| 341 | - // 1. test 熵函数 | ||
| 342 | - calcShannonEnt(dataSet) | ||
| 343 | - | ||
| 344 | - // 2. test 测试切分数据集函数 | ||
| 345 | - var subDataset=splitDataSet(dataSet, 1, "粗") | ||
| 346 | - | ||
| 347 | - // 3. test 最好属性函数 | ||
| 348 | - chooseBestFeatureForSplit(dataSet) | ||
| 349 | - | ||
| 350 | - // 4. test 主类别函数 | ||
| 351 | - getMajorityClassByCount(ArrayList<String>(['男', '男', '女'])) | ||
| 352 | - | ||
| 353 | - // 5. 由以上数据集推导出决策树模型并遍历 | ||
| 354 | - var decisionTree00 = createDecisionTree_ID3(dataSet, featuresList) | ||
| 355 | - println(TypeInfo.of(decisionTree00).name) | ||
| 356 | - decisionTree00.traverse() | ||
| 357 | - | ||
| 358 | - // 6. 利用以上决策树模型对输入的数据进行判断决策 | ||
| 359 | - var data = ArrayList(["粗","短"]) | ||
| 360 | - var features_00 = ArrayList(["声音","头发"]) | ||
| 361 | - decideByDecisionTree(data, features_00, decisionTree00) | ||
| 362 | - | ||
| 363 | - return 0 | ||
| 364 | -} | ||
| 365 | - | ||
| @@ -1,93 +0,0 @@ | |||
| 1 | -# 1.决策树算法之ID3算法 | ||
| 2 | - | ||
| 3 | -## 1.1 案例需求 | ||
| 4 | - | ||
| 5 | -训练一个决策树分类器,输入声音和头发,分类器能给出这个人是男还是女。 | ||
| 6 | - | ||
| 7 | -所用的训练数据集如下,这个数据一共有8个样本,每个样本有2个属性,分别为头发和声音,第三列为性别标签,表示“男”或“女”。 | ||
| 8 | - | ||
| 9 | -**数据集。** 总共有8位同学,男生3位,女生5位 | ||
| 10 | - | ||
| 11 | -头发 声音 性别 | ||
| 12 | - | ||
| 13 | -长 粗 男 | ||
| 14 | - | ||
| 15 | -短 粗 男 | ||
| 16 | - | ||
| 17 | -短 粗 男 | ||
| 18 | - | ||
| 19 | -长 细 女 | ||
| 20 | - | ||
| 21 | -短 细 女 | ||
| 22 | - | ||
| 23 | -短 粗 女 | ||
| 24 | - | ||
| 25 | -长 粗 女 | ||
| 26 | - | ||
| 27 | -长 粗 女 | ||
| 28 | - | ||
| 29 | -## 1.2 算法原理分析 | ||
| 30 | - | ||
| 31 | -决策树基于“是非”二值逻辑进行分枝。 | ||
| 32 | - | ||
| 33 | -本例决策树的任务是找到头发、声音将其样本两两分类,自顶向下构建决策树。 | ||
| 34 | - | ||
| 35 | -**这里列出两种方案:** | ||
| 36 | - | ||
| 37 | -①先根据头发判断,若判断不出,再根据声音判断,如下图: | ||
| 38 | - | ||
| 39 | - | ||
| 40 | -一个简单、直观的决策树就出来了。头发长、声音粗就是男生;头发长、声音细就是女生;头发短、声音粗是男生;头发短、声音细是女生。 | ||
| 41 | - | ||
| 42 | -② 先根据声音判断,然后再根据头发来判断,决策树如下: | ||
| 43 | - | ||
| 44 | - | ||
| 45 | -**问题**:方案①和方案②哪个的决策树好些?计算机做决策树的时候,面对多个特征,该如何选哪个特征为最佳多得划分特征? | ||
| 46 | - | ||
| 47 | -**最佳多得划分特征**。 是通过计算信息增益来确定的。在数据集中,我们通过计算每个特征的数据集划分前后的信息变化量,即信息增益,来判断哪个特征是最好的划分方式。 | ||
| 48 | - | ||
| 49 | -**信息增益。** 是指数据集在划分前后信息的变化量,它反映了通过某个特征进行划分后,数据集的有序化程度提高的程度。通过遍历数据集中的每个特征,计算其信息增益,选择信息增益最大的特征作为最佳划分特征,这样可以使得划分后的数据集更加有序,从而提高分类的准确性。 | ||
| 50 | - | ||
| 51 | -例如,在一个具体的数据集中,有两个特征和一种分类。 | ||
| 52 | -通过计算每个特征的信息增益,可以确定哪个特征能够最大程度地减少数据集的无序性,从而选择该特征作为最佳划分特征。这种方法在机器学习和数据分析中非常常见,尤其是在构建决策树等模型时,选择最佳划分特征是提高模型性能的关键步骤之一。 | ||
| 53 | - | ||
| 54 | -**选取最佳特征划分数据集。** | ||
| 55 | - | ||
| 56 | -划分数据集的大原则是:将无序的数据变得更加有序。 | ||
| 57 | -我们可以使用多种方法划分数据集,但是每种方法都有各自的优缺点。于是我们这么想,如果我们能测量数据的复杂度,对比按不同特征分类后的数据复杂度,若按某一特征分类后复杂度减少的更多,那么这个特征即为最佳分类特征。为此,Claude Shannon定义了熵和信息增益,用熵来表示信息的复杂度,熵越大,则信息越复杂。信息增益表示两个信息熵的差值。 | ||
| 58 | - | ||
| 59 | -**以上面的数据集的选取最佳特征为例:** | ||
| 60 | - | ||
| 61 | -(1)首先计算未分类前的熵。 | ||
| 62 | -熵(总)= -3/8log2(3/8)-5/8log2(5/8)=0.9544 | ||
| 63 | - | ||
| 64 | -(2)接着分别计算方案①和方案②分类后信息熵。 | ||
| 65 | - | ||
| 66 | -a.方案①首先按头发分类,分类后的结果为:长头发中有1男3女。短头发中有2男2女。 | ||
| 67 | - | ||
| 68 | -熵(长发)= -1/4log2(1/4)-3/4log2(3/4)=0.8113 | ||
| 69 | - | ||
| 70 | -熵(短发)= -2/4log2(2/4)-2/4log2(2/4)=1 | ||
| 71 | - | ||
| 72 | -熵(方案①)= 4/8*0.8113+4/8*1=0.9057 (4/8为长头发有4人,短头发有4人) | ||
| 73 | - | ||
| 74 | -信息增益(方案①)= 熵(总)- 熵(方案①)= 0.9544 - 0.9057 = 0.0487 | ||
| 75 | - | ||
| 76 | -b.同理,按方案②的方法,首先按声音特征来分,分类后的结果为:声音粗中有3男3女。声音细中有0男2女。 | ||
| 77 | - | ||
| 78 | -熵(声音粗)= -3/6log2(3/6)-3/6log2(3/6)=1 | ||
| 79 | - | ||
| 80 | -熵(声音细)= -2/2log2(2/2)=0 | ||
| 81 | - | ||
| 82 | -熵(方案②)= 6/8*1+2/8*0=0.75 (6/8为声音粗有6人,2/8为声音细有2人) | ||
| 83 | - | ||
| 84 | -信息增益(方案②)= 熵(总)- 熵(方案②)= 0.9544 - 0.75 = 0.2087 | ||
| 85 | - | ||
| 86 | -(3)按照方案②的方法,先按声音特征分类,信息增益更大,区分样本的能力更强,更具有代表性。 | ||
| 87 | - | ||
| 88 | -以上就是决策树ID3算法的核心思想。 | ||
| 89 | - | ||
| 90 | -<br> | ||
| 91 | - | ||
| 92 | -**最终得到的决策树如下图:** | ||
| 93 | - | ||
| @@ -1,34 +0,0 @@ | |||
| 1 | -### 基本概念: | ||
| 2 | -1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。 | ||
| 3 | - | ||
| 4 | -2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。 | ||
| 5 | - | ||
| 6 | -### 最小堆定义: | ||
| 7 | -1、堆是一颗完全二叉树; | ||
| 8 | - | ||
| 9 | -2、堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。 | ||
| 10 | - | ||
| 11 | -3、堆中每个结点的子树都是堆树。 | ||
| 12 | - | ||
| 13 | -### 使用方法 | ||
| 14 | -1. 编译 | ||
| 15 | -```bash | ||
| 16 | -cjc min_heap --test | ||
| 17 | -``` | ||
| 18 | - | ||
| 19 | -2. 运行 | ||
| 20 | -```bash | ||
| 21 | -./main | ||
| 22 | -``` | ||
| 23 | - | ||
| 24 | -3. 输出结果`PASSED: 1, SKIPPED: 0, ERROR: 0`则说明成功,下面是运行日志。 | ||
| 25 | -```bash | ||
| 26 | --------------------------------------------------------------------------------------------------- | ||
| 27 | -TP: minheap, time elapsed: 66522 ns, RESULT: | ||
| 28 | - TCS: TestA, time elapsed: 55343 ns, RESULT: | ||
| 29 | - [ PASSED ] CASE: test_min_heap (22824 ns) | ||
| 30 | -Summary: TOTAL: 1 | ||
| 31 | - PASSED: 1, SKIPPED: 0, ERROR: 0 | ||
| 32 | - FAILED: 0 | ||
| 33 | --------------------------------------------------------------------------------------------------- | ||
| 34 | -``` | ||
| @@ -1,113 +0,0 @@ | |||
| 1 | -package minheap | ||
| 2 | - | ||
| 3 | -import std.core.Comparable | ||
| 4 | -import std.core.Collection | ||
| 5 | -import std.collection.ArrayList | ||
| 6 | -// for test | ||
| 7 | -import std.unittest.assertEqual | ||
| 8 | -import std.unittest.Configuration | ||
| 9 | -import std.unittest.TestClass | ||
| 10 | -import std.unittest.TestPackage | ||
| 11 | -import std.unittest.TestSuite | ||
| 12 | -import std.unittest.UnitTestCase | ||
| 13 | -import std.unittest.testmacro.Test | ||
| 14 | -import std.unittest.testmacro.TestCase | ||
| 15 | - | ||
| 16 | -public struct BinaryHeap<T> where T <: Comparable<T> { | ||
| 17 | - public var data: ArrayList<T>; | ||
| 18 | - | ||
| 19 | - public init() { | ||
| 20 | - this.data = ArrayList<T>(); | ||
| 21 | - } | ||
| 22 | - | ||
| 23 | - public init(capacity!: Int64) { | ||
| 24 | - this.data = ArrayList<T>(capacity); | ||
| 25 | - } | ||
| 26 | - | ||
| 27 | - public static func with_capacity(capacity: Int64): BinaryHeap<T> { | ||
| 28 | - return BinaryHeap<T>(capacity: capacity); | ||
| 29 | - } | ||
| 30 | - | ||
| 31 | - public mut func appendAll(elements: Collection<T>) { | ||
| 32 | - for (element in elements) { | ||
| 33 | - this.push(element); | ||
| 34 | - } | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - public mut func push(item: T) { | ||
| 38 | - this.data.append(item); | ||
| 39 | - this.shift_up(this.data.size - 1); | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | - public mut func pop(): Option<T> { | ||
| 43 | - // pop first value and return | ||
| 44 | - if (this.data.size == 0) { | ||
| 45 | - return None; | ||
| 46 | - } | ||
| 47 | - // swap 0 with last_index | ||
| 48 | - let last_index = this.data.size - 1; | ||
| 49 | - if (last_index > 0) { | ||
| 50 | - unsafe{this.data.getRawArray().swap(0, last_index)}; | ||
| 51 | - } | ||
| 52 | - // pop head value(already swaped into last_index) | ||
| 53 | - let first_value = this.data[last_index]; | ||
| 54 | - this.data.remove(last_index); | ||
| 55 | - this.shift_down(0); | ||
| 56 | - return first_value; | ||
| 57 | - } | ||
| 58 | - | ||
| 59 | - public func len(): Int64 { | ||
| 60 | - return this.data.size; | ||
| 61 | - } | ||
| 62 | - | ||
| 63 | - public func is_empty(): Bool { | ||
| 64 | - return this.data.isEmpty(); | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - private mut func shift_up(index: Int64): Unit { | ||
| 68 | - if (index == 0) { | ||
| 69 | - return ; | ||
| 70 | - } | ||
| 71 | - // get parent index | ||
| 72 | - let parent_index: Int64 = (index - 1) / 2; | ||
| 73 | - if (this.data[index] < this.data[parent_index]) { | ||
| 74 | - // swap index with parent_index | ||
| 75 | - unsafe{this.data.getRawArray().swap(index, parent_index)}; | ||
| 76 | - this.shift_up(parent_index); | ||
| 77 | - return ; | ||
| 78 | - } | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - private mut func shift_down(index: Int64): Unit { | ||
| 82 | - let left_child_index = index * 2 + 1; | ||
| 83 | - let right_child_index = index * 2 + 2; | ||
| 84 | - // get smallest_index | ||
| 85 | - var smallest_index = index; | ||
| 86 | - if (left_child_index < this.data.size && this.data[left_child_index] < this.data[smallest_index]) { | ||
| 87 | - smallest_index = left_child_index; | ||
| 88 | - } | ||
| 89 | - if (right_child_index < this.data.size && this.data[right_child_index] < this.data[smallest_index]) { | ||
| 90 | - smallest_index = right_child_index; | ||
| 91 | - } | ||
| 92 | - if (smallest_index != index) { | ||
| 93 | - unsafe{this.data.getRawArray().swap(index, smallest_index)}; | ||
| 94 | - this.shift_down(smallest_index) | ||
| 95 | - } | ||
| 96 | - } | ||
| 97 | - | ||
| 98 | -} | ||
| 99 | - | ||
| 100 | -@Test | ||
| 101 | -public class TestA { | ||
| 102 | - @TestCase | ||
| 103 | - public func test_min_heap(): Unit { | ||
| 104 | - var heap = BinaryHeap<Int32>.with_capacity(10); | ||
| 105 | - heap.appendAll([3, 1, 4, 1, 5]); | ||
| 106 | - assertEqual("", "", heap.pop().getOrThrow(), 1); | ||
| 107 | - assertEqual("", "", heap.pop().getOrThrow(), 1); | ||
| 108 | - assertEqual("", "", heap.pop().getOrThrow(), 3); | ||
| 109 | - assertEqual("", "", heap.pop().getOrThrow(), 4); | ||
| 110 | - assertEqual("", "", heap.pop().getOrThrow(), 5); | ||
| 111 | - assertEqual("", "", heap.pop().isNone(), true); | ||
| 112 | - } | ||
| 113 | -} | ||
| @@ -1,13 +0,0 @@ | |||
| 1 | -**/target/ | ||
| 2 | -**/.cache/ | ||
| 3 | -**/build-script-cache/ | ||
| 4 | -**/html/ | ||
| 5 | -**/.vscode/ | ||
| 6 | -**/output/ | ||
| 7 | -**/html/ | ||
| 8 | -**/output/ | ||
| 9 | -main | ||
| 10 | -*.bchir2 | ||
| 11 | -*.cjo | ||
| 12 | -*.macrocall | ||
| 13 | -*.dylib | ||
| @@ -1,201 +0,0 @@ | |||
| 1 | - Apache License | ||
| 2 | - Version 2.0, January 2004 | ||
| 3 | - http://www.apache.org/licenses/ | ||
| 4 | - | ||
| 5 | - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
| 6 | - | ||
| 7 | - 1. Definitions. | ||
| 8 | - | ||
| 9 | - "License" shall mean the terms and conditions for use, reproduction, | ||
| 10 | - and distribution as defined by Sections 1 through 9 of this document. | ||
| 11 | - | ||
| 12 | - "Licensor" shall mean the copyright owner or entity authorized by | ||
| 13 | - the copyright owner that is granting the License. | ||
| 14 | - | ||
| 15 | - "Legal Entity" shall mean the union of the acting entity and all | ||
| 16 | - other entities that control, are controlled by, or are under common | ||
| 17 | - control with that entity. For the purposes of this definition, | ||
| 18 | - "control" means (i) the power, direct or indirect, to cause the | ||
| 19 | - direction or management of such entity, whether by contract or | ||
| 20 | - otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
| 21 | - outstanding shares, or (iii) beneficial ownership of such entity. | ||
| 22 | - | ||
| 23 | - "You" (or "Your") shall mean an individual or Legal Entity | ||
| 24 | - exercising permissions granted by this License. | ||
| 25 | - | ||
| 26 | - "Source" form shall mean the preferred form for making modifications, | ||
| 27 | - including but not limited to software source code, documentation | ||
| 28 | - source, and configuration files. | ||
| 29 | - | ||
| 30 | - "Object" form shall mean any form resulting from mechanical | ||
| 31 | - transformation or translation of a Source form, including but | ||
| 32 | - not limited to compiled object code, generated documentation, | ||
| 33 | - and conversions to other media types. | ||
| 34 | - | ||
| 35 | - "Work" shall mean the work of authorship, whether in Source or | ||
| 36 | - Object form, made available under the License, as indicated by a | ||
| 37 | - copyright notice that is included in or attached to the work | ||
| 38 | - (an example is provided in the Appendix below). | ||
| 39 | - | ||
| 40 | - "Derivative Works" shall mean any work, whether in Source or Object | ||
| 41 | - form, that is based on (or derived from) the Work and for which the | ||
| 42 | - editorial revisions, annotations, elaborations, or other modifications | ||
| 43 | - represent, as a whole, an original work of authorship. For the purposes | ||
| 44 | - of this License, Derivative Works shall not include works that remain | ||
| 45 | - separable from, or merely link (or bind by name) to the interfaces of, | ||
| 46 | - the Work and Derivative Works thereof. | ||
| 47 | - | ||
| 48 | - "Contribution" shall mean any work of authorship, including | ||
| 49 | - the original version of the Work and any modifications or additions | ||
| 50 | - to that Work or Derivative Works thereof, that is intentionally | ||
| 51 | - submitted to Licensor for inclusion in the Work by the copyright owner | ||
| 52 | - or by an individual or Legal Entity authorized to submit on behalf of | ||
| 53 | - the copyright owner. For the purposes of this definition, "submitted" | ||
| 54 | - means any form of electronic, verbal, or written communication sent | ||
| 55 | - to the Licensor or its representatives, including but not limited to | ||
| 56 | - communication on electronic mailing lists, source code control systems, | ||
| 57 | - and issue tracking systems that are managed by, or on behalf of, the | ||
| 58 | - Licensor for the purpose of discussing and improving the Work, but | ||
| 59 | - excluding communication that is conspicuously marked or otherwise | ||
| 60 | - designated in writing by the copyright owner as "Not a Contribution." | ||
| 61 | - | ||
| 62 | - "Contributor" shall mean Licensor and any individual or Legal Entity | ||
| 63 | - on behalf of whom a Contribution has been received by Licensor and | ||
| 64 | - subsequently incorporated within the Work. | ||
| 65 | - | ||
| 66 | - 2. Grant of Copyright License. Subject to the terms and conditions of | ||
| 67 | - this License, each Contributor hereby grants to You a perpetual, | ||
| 68 | - worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| 69 | - copyright license to reproduce, prepare Derivative Works of, | ||
| 70 | - publicly display, publicly perform, sublicense, and distribute the | ||
| 71 | - Work and such Derivative Works in Source or Object form. | ||
| 72 | - | ||
| 73 | - 3. Grant of Patent License. Subject to the terms and conditions of | ||
| 74 | - this License, each Contributor hereby grants to You a perpetual, | ||
| 75 | - worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| 76 | - (except as stated in this section) patent license to make, have made, | ||
| 77 | - use, offer to sell, sell, import, and otherwise transfer the Work, | ||
| 78 | - where such license applies only to those patent claims licensable | ||
| 79 | - by such Contributor that are necessarily infringed by their | ||
| 80 | - Contribution(s) alone or by combination of their Contribution(s) | ||
| 81 | - with the Work to which such Contribution(s) was submitted. If You | ||
| 82 | - institute patent litigation against any entity (including a | ||
| 83 | - cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
| 84 | - or a Contribution incorporated within the Work constitutes direct | ||
| 85 | - or contributory patent infringement, then any patent licenses | ||
| 86 | - granted to You under this License for that Work shall terminate | ||
| 87 | - as of the date such litigation is filed. | ||
| 88 | - | ||
| 89 | - 4. Redistribution. You may reproduce and distribute copies of the | ||
| 90 | - Work or Derivative Works thereof in any medium, with or without | ||
| 91 | - modifications, and in Source or Object form, provided that You | ||
| 92 | - meet the following conditions: | ||
| 93 | - | ||
| 94 | - (a) You must give any other recipients of the Work or | ||
| 95 | - Derivative Works a copy of this License; and | ||
| 96 | - | ||
| 97 | - (b) You must cause any modified files to carry prominent notices | ||
| 98 | - stating that You changed the files; and | ||
| 99 | - | ||
| 100 | - (c) You must retain, in the Source form of any Derivative Works | ||
| 101 | - that You distribute, all copyright, patent, trademark, and | ||
| 102 | - attribution notices from the Source form of the Work, | ||
| 103 | - excluding those notices that do not pertain to any part of | ||
| 104 | - the Derivative Works; and | ||
| 105 | - | ||
| 106 | - (d) If the Work includes a "NOTICE" text file as part of its | ||
| 107 | - distribution, then any Derivative Works that You distribute must | ||
| 108 | - include a readable copy of the attribution notices contained | ||
| 109 | - within such NOTICE file, excluding those notices that do not | ||
| 110 | - pertain to any part of the Derivative Works, in at least one | ||
| 111 | - of the following places: within a NOTICE text file distributed | ||
| 112 | - as part of the Derivative Works; within the Source form or | ||
| 113 | - documentation, if provided along with the Derivative Works; or, | ||
| 114 | - within a display generated by the Derivative Works, if and | ||
| 115 | - wherever such third-party notices normally appear. The contents | ||
| 116 | - of the NOTICE file are for informational purposes only and | ||
| 117 | - do not modify the License. You may add Your own attribution | ||
| 118 | - notices within Derivative Works that You distribute, alongside | ||
| 119 | - or as an addendum to the NOTICE text from the Work, provided | ||
| 120 | - that such additional attribution notices cannot be construed | ||
| 121 | - as modifying the License. | ||
| 122 | - | ||
| 123 | - You may add Your own copyright statement to Your modifications and | ||
| 124 | - may provide additional or different license terms and conditions | ||
| 125 | - for use, reproduction, or distribution of Your modifications, or | ||
| 126 | - for any such Derivative Works as a whole, provided Your use, | ||
| 127 | - reproduction, and distribution of the Work otherwise complies with | ||
| 128 | - the conditions stated in this License. | ||
| 129 | - | ||
| 130 | - 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
| 131 | - any Contribution intentionally submitted for inclusion in the Work | ||
| 132 | - by You to the Licensor shall be under the terms and conditions of | ||
| 133 | - this License, without any additional terms or conditions. | ||
| 134 | - Notwithstanding the above, nothing herein shall supersede or modify | ||
| 135 | - the terms of any separate license agreement you may have executed | ||
| 136 | - with Licensor regarding such Contributions. | ||
| 137 | - | ||
| 138 | - 6. Trademarks. This License does not grant permission to use the trade | ||
| 139 | - names, trademarks, service marks, or product names of the Licensor, | ||
| 140 | - except as required for reasonable and customary use in describing the | ||
| 141 | - origin of the Work and reproducing the content of the NOTICE file. | ||
| 142 | - | ||
| 143 | - 7. Disclaimer of Warranty. Unless required by applicable law or | ||
| 144 | - agreed to in writing, Licensor provides the Work (and each | ||
| 145 | - Contributor provides its Contributions) on an "AS IS" BASIS, | ||
| 146 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
| 147 | - implied, including, without limitation, any warranties or conditions | ||
| 148 | - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
| 149 | - PARTICULAR PURPOSE. You are solely responsible for determining the | ||
| 150 | - appropriateness of using or redistributing the Work and assume any | ||
| 151 | - risks associated with Your exercise of permissions under this License. | ||
| 152 | - | ||
| 153 | - 8. Limitation of Liability. In no event and under no legal theory, | ||
| 154 | - whether in tort (including negligence), contract, or otherwise, | ||
| 155 | - unless required by applicable law (such as deliberate and grossly | ||
| 156 | - negligent acts) or agreed to in writing, shall any Contributor be | ||
| 157 | - liable to You for damages, including any direct, indirect, special, | ||
| 158 | - incidental, or consequential damages of any character arising as a | ||
| 159 | - result of this License or out of the use or inability to use the | ||
| 160 | - Work (including but not limited to damages for loss of goodwill, | ||
| 161 | - work stoppage, computer failure or malfunction, or any and all | ||
| 162 | - other commercial damages or losses), even if such Contributor | ||
| 163 | - has been advised of the possibility of such damages. | ||
| 164 | - | ||
| 165 | - 9. Accepting Warranty or Additional Liability. While redistributing | ||
| 166 | - the Work or Derivative Works thereof, You may choose to offer, | ||
| 167 | - and charge a fee for, acceptance of support, warranty, indemnity, | ||
| 168 | - or other liability obligations and/or rights consistent with this | ||
| 169 | - License. However, in accepting such obligations, You may act only | ||
| 170 | - on Your own behalf and on Your sole responsibility, not on behalf | ||
| 171 | - of any other Contributor, and only if You agree to indemnify, | ||
| 172 | - defend, and hold each Contributor harmless for any liability | ||
| 173 | - incurred by, or claims asserted against, such Contributor by reason | ||
| 174 | - of your accepting any such warranty or additional liability. | ||
| 175 | - | ||
| 176 | - END OF TERMS AND CONDITIONS | ||
| 177 | - | ||
| 178 | - APPENDIX: How to apply the Apache License to your work. | ||
| 179 | - | ||
| 180 | - To apply the Apache License to your work, attach the following | ||
| 181 | - boilerplate notice, with the fields enclosed by brackets "{}" | ||
| 182 | - replaced with your own identifying information. (Don't include | ||
| 183 | - the brackets!) The text should be enclosed in the appropriate | ||
| 184 | - comment syntax for the file format. We also recommend that a | ||
| 185 | - file or class name and description of purpose be included on the | ||
| 186 | - same "printed page" as the copyright notice for easier | ||
| 187 | - identification within third-party archives. | ||
| 188 | - | ||
| 189 | - Copyright {yyyy} {name of copyright owner} | ||
| 190 | - | ||
| 191 | - Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 192 | - you may not use this file except in compliance with the License. | ||
| 193 | - You may obtain a copy of the License at | ||
| 194 | - | ||
| 195 | - http://www.apache.org/licenses/LICENSE-2.0 | ||
| 196 | - | ||
| 197 | - Unless required by applicable law or agreed to in writing, software | ||
| 198 | - distributed under the License is distributed on an "AS IS" BASIS, | ||
| 199 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 200 | - See the License for the specific language governing permissions and | ||
| 201 | - limitations under the License. | ||
| @@ -1,19 +0,0 @@ | |||
| 1 | -# 使用仓颉实现的一些算法 | ||
| 2 | - | ||
| 3 | -使用仓颉语言实现的一些算法。包括leetcode和算法指南上的实现等 | ||
| 4 | - | ||
| 5 | -## leetcode | ||
| 6 | - | ||
| 7 | -leetcode上的一些算法。每个思想解题思路对应一个文件,里面有若干算法可供参考 | ||
| 8 | - | ||
| 9 | -## structures | ||
| 10 | - | ||
| 11 | -算法中使用的一些类型和结构定义 | ||
| 12 | - | ||
| 13 | -## swordoffer | ||
| 14 | - | ||
| 15 | -剑指Offer中的一些算法 | ||
| 16 | - | ||
| 17 | -## 参考来源 | ||
| 18 | - | ||
| 19 | -1. https://www.cyc2018.xyz | ||
| @@ -1 +0,0 @@ | |||
| 1 | -!G.NAM.01 | ||
| @@ -1,7 +0,0 @@ | |||
| 1 | -version = 0 | ||
| 2 | - | ||
| 3 | -[requires] | ||
| 4 | - cj_curry = {branch = "main", commitId = "314beb19220a38a92965a321e7030b758ee8e5b2", git = "https://gitcode.com/unravel/cj_curry_macro.git", output-type = "static"} | ||
| 5 | - cj_verbose = {branch = "main", commitId = "ee736f3839e27583a27582ff44f10d19092f76c4", git = "https://gitcode.com/unravel/cj_verbose_macro.git", output-type = "static"} | ||
| 6 | - cjextensions = {branch = "main", commitId = "0ace1c4621075b24e9db4475a3b423c3b871e46d", git = "https://gitcode.com/unravel/cjextensions.git", output-type = "static"} | ||
| 7 | - usablemacros = {branch = "main", commitId = "addd787b9dadc9bcb296d40a4afb5fde6b782826", git = "https://gitcode.com/unravel/cj_macros.git", output-type = "static"} | ||
| @@ -1,15 +0,0 @@ | |||
| 1 | -[dependencies] | ||
| 2 | - cjextensions = { git='https://gitcode.com/unravel/cjextensions.git', output-type='static', branch='main' } | ||
| 3 | - usablemacros = { git='https://gitcode.com/unravel/cj_macros.git', output-type='static', branch = 'main' } | ||
| 4 | - | ||
| 5 | -[package] | ||
| 6 | - cjc-version = "0.57.3" | ||
| 7 | - compile-option = "-Woff unused -O0" | ||
| 8 | - description = "leetcode和剑指offer的一些算法实现" | ||
| 9 | - link-option = "" | ||
| 10 | - name = "cj_algorithm" | ||
| 11 | - output-type = "executable" | ||
| 12 | - src-dir = "" | ||
| 13 | - target-dir = "" | ||
| 14 | - version = "1.0.0" | ||
| 15 | - package-configuration = {} | ||
| @@ -1,289 +0,0 @@ | |||
| 1 | -// cjlint-ignore -start !G.OTH.03 !G.NAM.02 !G.FUN.01 | ||
| 2 | -/** | ||
| 3 | - * @author unravel | ||
| 4 | - * @see https://gitcode.com/unravel/cj_algorithm/overview | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -package cj_algorithm.leetcode | ||
| 8 | - | ||
| 9 | -import usablemacros.verbose.{dprintMultline, dprint} | ||
| 10 | -import std.collection.{HashMap, ArrayList} | ||
| 11 | -import std.sort.stableSort | ||
| 12 | -import std.math.sqrt | ||
| 13 | -import std.convert.Parsable | ||
| 14 | -import cj_algorithm.structures.{Stack, ListNode, TreeNode, null} | ||
| 15 | - | ||
| 16 | -public class OtherAlgorithm { | ||
| 17 | - // MARK: 将十六进制转换成十进制 | ||
| 18 | - func convertTo10(hexStr: String): Int { | ||
| 19 | - let charIntMap = HashMap<Rune, Int>( | ||
| 20 | - [ | ||
| 21 | - (r'0', 0), | ||
| 22 | - (r'1', 1), | ||
| 23 | - (r'2', 2), | ||
| 24 | - (r'3', 3), | ||
| 25 | - (r'4', 4), | ||
| 26 | - (r'5', 5), | ||
| 27 | - (r'6', 6), | ||
| 28 | - (r'7', 7), | ||
| 29 | - (r'8', 8), | ||
| 30 | - (r'9', 9), | ||
| 31 | - (r'a', 10), | ||
| 32 | - (r'b', 11), | ||
| 33 | - (r'c', 12), | ||
| 34 | - (r'd', 13), | ||
| 35 | - (r'e', 14), | ||
| 36 | - (r'f', 15) | ||
| 37 | - ] | ||
| 38 | - ) | ||
| 39 | - | ||
| 40 | - let chars = hexStr.toAsciiLower().toRuneArray() | ||
| 41 | - var res = 0 | ||
| 42 | - for ((ind, ch) in chars.iterator().enumerate()) { | ||
| 43 | - if (ind == 0) { | ||
| 44 | - res = charIntMap[ch] | ||
| 45 | - } else { | ||
| 46 | - res = res * 16 + charIntMap[ch] | ||
| 47 | - } | ||
| 48 | - } | ||
| 49 | - return res | ||
| 50 | - } | ||
| 51 | - | ||
| 52 | - // MARK: 有15个瓶子,其中最多有一瓶有毒,现在有四只老鼠,喝了有毒的水之后,第二天就会死。 | ||
| 53 | - // 如何在第二天就可以判断出哪个瓶子有毒? | ||
| 54 | - // http://cloverkim.com/mouse-drug-water.html | ||
| 55 | - // 核心思想:第n个老鼠喝二进制表示的第n位上为1的瓶子里的水 | ||
| 56 | - // mices为老鼠的存活状态 0011 | ||
| 57 | - func bottles15(mices: Array<Int>): Int { | ||
| 58 | - let miceNum: Int = 4 | ||
| 59 | - if (mices.size != miceNum) { | ||
| 60 | - return 0 | ||
| 61 | - } | ||
| 62 | - | ||
| 63 | - var drug: Int = 0 | ||
| 64 | - for (i in 0..miceNum) { | ||
| 65 | - drug |= (mices[i] << (miceNum - i - 1)) | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - // if (drug == 0) { | ||
| 69 | - // println("无毒") | ||
| 70 | - // } else { | ||
| 71 | - // println("有毒的瓶子是第 ${drug} 瓶") | ||
| 72 | - // } | ||
| 73 | - return drug | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - // MARK: 一个乱序数组,求符合 一个 比它前面的数都大,比它后面的数都小的数 组成的子数组 | ||
| 77 | - // 核心思想:使用栈先进后出的特性,判断要进的数比栈顶的大就直接进,比栈顶的数小就一直弹栈到比它大为止 | ||
| 78 | - // https://leetcode.cn/circle/discuss/fBUtcu/ | ||
| 79 | - func printMinMaxArray(array: Array<Int>): Array<Int> { | ||
| 80 | - let stack = Stack<Int>() | ||
| 81 | - if (!array.isEmpty()) { | ||
| 82 | - var maxValue = array[0] | ||
| 83 | - stack.push(maxValue) | ||
| 84 | - | ||
| 85 | - for (value in array) { | ||
| 86 | - if (maxValue <= value) { | ||
| 87 | - maxValue = value | ||
| 88 | - stack.push(value) | ||
| 89 | - } else { | ||
| 90 | - while (let Some(topValue) <- stack.top) { | ||
| 91 | - if (topValue > value) { | ||
| 92 | - stack.pop() | ||
| 93 | - } else { | ||
| 94 | - break | ||
| 95 | - } | ||
| 96 | - } | ||
| 97 | - } | ||
| 98 | - } | ||
| 99 | - } | ||
| 100 | - stack.toArray() | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - // MARK: 构建并打印倒三角形 | ||
| 104 | - // 核心思想:找规律,先竖着找行之间的规律,再横着找列之间的规律 | ||
| 105 | - func printInvertedtriangle(numLines: Int) { | ||
| 106 | - let count = numLines + 1 | ||
| 107 | - let ret = Array<Array<Int>>(count) {_ => Array<Int>(count, repeat: 0)} | ||
| 108 | - | ||
| 109 | - ret[0][0] = 1 | ||
| 110 | - for (i in 0..numLines) { | ||
| 111 | - // 两行之间相差的数 | ||
| 112 | - let rowdet = i + 1 | ||
| 113 | - // 竖着的,每一行比上一行 大row+1 | ||
| 114 | - ret[i + 1][0] = ret[i][0] + rowdet | ||
| 115 | - print("${ret[i][0]} ") | ||
| 116 | - | ||
| 117 | - // 剩多少列 | ||
| 118 | - let colNum = numLines - i - 1 | ||
| 119 | - | ||
| 120 | - for (j in 0..colNum) { | ||
| 121 | - // 两列之间相差的数 | ||
| 122 | - let coldet = j + 1 | ||
| 123 | - ret[i][j + 1] = ret[i][j] + rowdet + coldet | ||
| 124 | - print("${ret[i][j + 1]} ") | ||
| 125 | - } | ||
| 126 | - print("\n") | ||
| 127 | - } | ||
| 128 | - } | ||
| 129 | - | ||
| 130 | - /** | ||
| 131 | - 给定两个数组 nums1 和 nums2 ,返回 它们的 交集 | ||
| 132 | - 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 | ||
| 133 | - */ | ||
| 134 | - // MARK: 给定两个排好序的数组A,B,请写一个函数,从中找到它们的公共元素 | ||
| 135 | - // https://leetcode.cn/problems/intersection-of-two-arrays/description/ | ||
| 136 | - // 核心思想:使用双指针同时遍历数组 | ||
| 137 | - func findCommon(arrA: Array<Int>, arrB: Array<Int>): Array<Int> { | ||
| 138 | - if (arrA.isEmpty() || arrB.isEmpty()) { | ||
| 139 | - return [] | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - let aCount = arrA.size | ||
| 143 | - let bCount = arrB.size | ||
| 144 | - var indexA = 0 | ||
| 145 | - var indexB = 0 | ||
| 146 | - | ||
| 147 | - let ret = ArrayList<Int>() | ||
| 148 | - while (indexA < aCount && indexB < bCount) { | ||
| 149 | - let valA = arrA[indexA] | ||
| 150 | - let valB = arrB[indexB] | ||
| 151 | - | ||
| 152 | - if (valA < valB) { | ||
| 153 | - indexA += 1 | ||
| 154 | - } else if (valA == valB) { | ||
| 155 | - indexA += 1 | ||
| 156 | - indexB += 1 | ||
| 157 | - ret.append(valA) | ||
| 158 | - } else { | ||
| 159 | - indexB += 1 | ||
| 160 | - } | ||
| 161 | - } | ||
| 162 | - return ret.toArray() | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - // MARK: 斐波那契 | ||
| 166 | - func fiboacci(n: Int): Int { | ||
| 167 | - if (n <= 1) { | ||
| 168 | - return n | ||
| 169 | - } | ||
| 170 | - return fiboacci(n - 1) + fiboacci(n - 2) | ||
| 171 | - } | ||
| 172 | - | ||
| 173 | - // MARK: 是否是一个质数(只能被1和自身整除) | ||
| 174 | - func isPrime(n: Int64): Bool { | ||
| 175 | - let sqrtIntValue = Int64(sqrt(Float64(n))) | ||
| 176 | - for (i in 2..=sqrtIntValue) { | ||
| 177 | - if (n % i == 0) { | ||
| 178 | - return false | ||
| 179 | - } | ||
| 180 | - } | ||
| 181 | - return true | ||
| 182 | - } | ||
| 183 | - | ||
| 184 | - /** | ||
| 185 | - 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 | ||
| 186 | - | ||
| 187 | - 你应当 保留 两个分区中每个节点的初始相对位置。 | ||
| 188 | - */ | ||
| 189 | - // MARK: 分割链表 | ||
| 190 | - // https://leetcode.cn/problems/partition-list/description/ | ||
| 191 | - // 例:1->5->3->2->4->2,给定x = 3。则我们要返回1->2->2->5->3->4 | ||
| 192 | - // 思想:先处理左边(比 x 小的节点),然后再处理右边(比 x 大的节点),最后再把左右两边拼起来 | ||
| 193 | - func partition( | ||
| 194 | - head: ?ListNode, | ||
| 195 | - x: Int | ||
| 196 | - ): ?ListNode { | ||
| 197 | - // 引入Dummy节点 | ||
| 198 | - let prevDummy = ListNode(val: Int.Min) | ||
| 199 | - let postDummy = ListNode(val: Int.Min) | ||
| 200 | - var prev = prevDummy | ||
| 201 | - var post = postDummy | ||
| 202 | - | ||
| 203 | - var node = head | ||
| 204 | - // 用尾插法处理左边和右边 | ||
| 205 | - while (let Some(nodeValue) <- node) { | ||
| 206 | - if (nodeValue.val < x) { | ||
| 207 | - prev.next = node | ||
| 208 | - prev = nodeValue | ||
| 209 | - } else { | ||
| 210 | - post.next = node | ||
| 211 | - post = nodeValue | ||
| 212 | - } | ||
| 213 | - node = nodeValue.next | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | - // 防止构成环。防止它最后一个节点是中间节点 | ||
| 217 | - post.next = None | ||
| 218 | - // 左右拼接 | ||
| 219 | - prev.next = postDummy.next | ||
| 220 | - return prevDummy.next | ||
| 221 | - } | ||
| 222 | - | ||
| 223 | - /* | ||
| 224 | - 给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。 | ||
| 225 | - | ||
| 226 | - 有效 二叉搜索树定义如下: | ||
| 227 | - | ||
| 228 | - 节点的左 | ||
| 229 | - 子树 | ||
| 230 | - 只包含 小于 当前节点的数。 | ||
| 231 | - 节点的右子树只包含 大于 当前节点的数。 | ||
| 232 | - 所有左子树和右子树自身必须也是二叉搜索树。 | ||
| 233 | - */ | ||
| 234 | - // MARK: 判断一颗二叉树是否为二叉查找树 | ||
| 235 | - // https://leetcode.cn/problems/validate-binary-search-tree/description/ | ||
| 236 | - func isValidBST(root: ?TreeNode): Bool { | ||
| 237 | - func helper( | ||
| 238 | - node: ?TreeNode, | ||
| 239 | - min: Int, | ||
| 240 | - max: Int | ||
| 241 | - ): Bool { | ||
| 242 | - if (let Some(nodeV) <- node) { | ||
| 243 | - // 所有右子节点都必须大于根节点 | ||
| 244 | - if (nodeV.val <= min) { | ||
| 245 | - return false | ||
| 246 | - } | ||
| 247 | - // 所有左子节点都必须小于根节点 | ||
| 248 | - if (nodeV.val >= max) { | ||
| 249 | - return false | ||
| 250 | - } | ||
| 251 | - return helper(nodeV.left, min, nodeV.val) && helper(nodeV.right, nodeV.val, max) | ||
| 252 | - } else { | ||
| 253 | - return true | ||
| 254 | - } | ||
| 255 | - } | ||
| 256 | - | ||
| 257 | - return helper(root, Int.Min, Int.Max) | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | - public func verify() { | ||
| 261 | - @dprintMultline( | ||
| 262 | - // 253 | ||
| 263 | - convertTo10('fd') | ||
| 264 | - // 3 | ||
| 265 | - bottles15([0, 0, 1, 1]) | ||
| 266 | - // [8, 9] | ||
| 267 | - printMinMaxArray([2, 3, 1, 8, 9, 20, 12]) | ||
| 268 | - /** | ||
| 269 | - 1 3 6 10 | ||
| 270 | - 2 5 9 | ||
| 271 | - 4 8 | ||
| 272 | - 7 | ||
| 273 | - */ | ||
| 274 | - printInvertedtriangle(4) | ||
| 275 | - // [2, 20, 23, 58, 120] | ||
| 276 | - findCommon([1, 2, 20, 22, 23, 58, 63, 120, 346, 263], [2, 3, 3, 20, 21, 23, 46, 58, 120, 983]) | ||
| 277 | - // 3 | ||
| 278 | - fiboacci(4) | ||
| 279 | - // true | ||
| 280 | - isPrime(293) | ||
| 281 | - // 1->2->2->5->3->4 | ||
| 282 | - partition(ListNode('1->5->3->2->4->2'), 3) | ||
| 283 | - // true | ||
| 284 | - isValidBST(TreeNode([2, 1, 3])) | ||
| 285 | - ) | ||
| 286 | - } | ||
| 287 | -} | ||
| 288 | - | ||
| 289 | -// cjlint-ignore -end | ||
| @@ -1,255 +0,0 @@ | |||
| 1 | -// cjlint-ignore -start !G.OTH.03 !G.NAM.02 !G.NAM.03 !G.FUN.01 | ||
| 2 | -/** | ||
| 3 | - * @author unravel | ||
| 4 | - * @see https://gitcode.com/unravel/cj_algorithm/overview | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -package cj_algorithm.leetcode | ||
| 8 | - | ||
| 9 | -import usablemacros.verbose.dprintMultline | ||
| 10 | - | ||
| 11 | -// 思想之二分查找 | ||
| 12 | -public class ThinkHalfFind { | ||
| 13 | - /** | ||
| 14 | - 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 | ||
| 15 | - */ | ||
| 16 | - // MARK: 在已排序数组中 二分查找key | ||
| 17 | - // https://leetcode.cn/problems/binary-search/description/ | ||
| 18 | - func binarySearch(nums: Array<Int>, key: Int): (Bool, Int) { | ||
| 19 | - var l = 0 | ||
| 20 | - var h = nums.size - 1 | ||
| 21 | - while (l <= h) { | ||
| 22 | - let m = l + (h - l) / 2 | ||
| 23 | - if (nums[m] == key) { | ||
| 24 | - return (true, m) | ||
| 25 | - } else if (nums[m] > key) { | ||
| 26 | - h = m - 1 | ||
| 27 | - } else { | ||
| 28 | - l = m + 1 | ||
| 29 | - } | ||
| 30 | - } | ||
| 31 | - return (false, -1) | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | - // MARK: 在一个有重复元素的数组中查找 key 的最左位置 | ||
| 35 | - func binarySearch2(nums: Array<Int>, key: Int): Int { | ||
| 36 | - var l = 0 | ||
| 37 | - var h = nums.size - 1 | ||
| 38 | - // 在 h 的赋值表达式为 h = m 的情况下,如果循环条件为 l <= h,那么会出现循环无法退出的情况,因此循环条件只能是 l < h | ||
| 39 | - while (l < h) { | ||
| 40 | - let m = l + (h - l) / 2 | ||
| 41 | - if (nums[m] >= key) { | ||
| 42 | - // 在 nums[m] >= key 的情况下,可以推导出最左 key 位于 [l, m] 区间中,这是一个闭区间。 | ||
| 43 | - // h 的赋值表达式为 h = m,因为 m 位置也可能是解 | ||
| 44 | - h = m | ||
| 45 | - } else { | ||
| 46 | - l = m + 1 | ||
| 47 | - } | ||
| 48 | - } | ||
| 49 | - | ||
| 50 | - if (nums[l] == key) { | ||
| 51 | - l | ||
| 52 | - } else { | ||
| 53 | - -1 | ||
| 54 | - } | ||
| 55 | - } | ||
| 56 | - | ||
| 57 | - /** | ||
| 58 | - 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。 | ||
| 59 | - | ||
| 60 | - 由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。 | ||
| 61 | - | ||
| 62 | - 注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。 | ||
| 63 | - */ | ||
| 64 | - // MARK: 求开方 | ||
| 65 | - // https://leetcode-cn.com/problems/sqrtx/description/ | ||
| 66 | - func mySqrt(x: Int): Int { | ||
| 67 | - if (x <= 1) { | ||
| 68 | - return x | ||
| 69 | - } | ||
| 70 | - // 对于 x = 8,它的开方是 2.82842...,最后应该返回 2 而不是 3。 | ||
| 71 | - // 在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l | ||
| 72 | - var l = 1 | ||
| 73 | - var h = x | ||
| 74 | - while (l <= h) { | ||
| 75 | - let mid = l + (h - l) / 2 | ||
| 76 | - let sqrt = x / mid | ||
| 77 | - | ||
| 78 | - if (mid == sqrt) { | ||
| 79 | - return mid | ||
| 80 | - } else if (mid > sqrt) { | ||
| 81 | - // 说明mid * sqrt < x,需要在左半区域寻找 | ||
| 82 | - h = mid - 1 | ||
| 83 | - } else { | ||
| 84 | - l = mid + 1 | ||
| 85 | - } | ||
| 86 | - } | ||
| 87 | - return h | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - /** | ||
| 91 | - 给你一个字符数组 letters,该数组按非递减顺序排序,以及一个字符 target。letters 里至少有两个不同的字符。 | ||
| 92 | - | ||
| 93 | - 返回 letters 中大于 target 的最小的字符。如果不存在这样的字符,则返回 letters 的第一个字符。 | ||
| 94 | - */ | ||
| 95 | - // MARK: 大于给定元素的最小元素 | ||
| 96 | - // https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/description/ | ||
| 97 | - // 给定一个有序的字符数组 letters 和一个字符 target,要求找出 letters 中大于 target 的最小字符,如果找不到就返回第 1 个字符 | ||
| 98 | - func nextGreatestLetter(letters: Array<Rune>, target: Rune): Rune { | ||
| 99 | - let n = letters.size | ||
| 100 | - var l = 0 | ||
| 101 | - var h = n - 1 | ||
| 102 | - while (l <= h) { | ||
| 103 | - let m = l + (h - l) / 2 | ||
| 104 | - // 如果中间的字母比target小,说明我们应该在右半区域寻找 | ||
| 105 | - if (letters[m] <= target) { | ||
| 106 | - l = m + 1 | ||
| 107 | - } else { | ||
| 108 | - h = m - 1 | ||
| 109 | - } | ||
| 110 | - } | ||
| 111 | - if (l < n) { | ||
| 112 | - letters[l] | ||
| 113 | - } else { | ||
| 114 | - letters[0] | ||
| 115 | - } | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - /** | ||
| 119 | - 给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。 | ||
| 120 | - | ||
| 121 | - 请你找出并返回只出现一次的那个数。 | ||
| 122 | - */ | ||
| 123 | - // MARK: 有序数组的 Single Element | ||
| 124 | - // https://leetcode-cn.com/problems/single-element-in-a-sorted-array/description/ | ||
| 125 | - // 一个有序数组只有一个数不出现两次,找出这个数 | ||
| 126 | - func singleNonDuplicate(nums: Array<Int>): Int { | ||
| 127 | - var l = 0 | ||
| 128 | - var h = nums.size - 1 | ||
| 129 | - while (l < h) { | ||
| 130 | - var m = l + (h - l) / 2 | ||
| 131 | - if (m % 2 == 1) { | ||
| 132 | - // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数 | ||
| 133 | - m -= 1 | ||
| 134 | - } | ||
| 135 | - if (nums[m] == nums[m + 1]) { | ||
| 136 | - // 说明左半区域是成双成对的 | ||
| 137 | - l = m + 2 | ||
| 138 | - } else { | ||
| 139 | - h = m | ||
| 140 | - } | ||
| 141 | - } | ||
| 142 | - return nums[l] | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - /** | ||
| 146 | - 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。 | ||
| 147 | - | ||
| 148 | - 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。 | ||
| 149 | - | ||
| 150 | - 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。 | ||
| 151 | - */ | ||
| 152 | - // MARK: 第一个错误的版本 | ||
| 153 | - // https://leetcode-cn.com/problems/first-bad-version/description/ | ||
| 154 | - // 给定一个元素 n 代表有 [1, 2, ..., n] 版本,在第 x 位置开始出现错误版本,导致后面的版本都错误。可以调用 isBadVersion(int x) 知道某个版本是否错误,要求找到第一个错误的版本 | ||
| 155 | - func firstBadVersion(n: Int): Int { | ||
| 156 | - let badBersionFrom = 4 | ||
| 157 | - func isBadVersion(n1: Int): Bool { | ||
| 158 | - if (n1 >= badBersionFrom) { | ||
| 159 | - return true | ||
| 160 | - } | ||
| 161 | - return false | ||
| 162 | - } | ||
| 163 | - | ||
| 164 | - var l = 1 | ||
| 165 | - var h = n | ||
| 166 | - while (l < h) { | ||
| 167 | - let mid = l + (h - l) / 2 | ||
| 168 | - if (isBadVersion(mid)) { | ||
| 169 | - h = mid | ||
| 170 | - } else { | ||
| 171 | - l = mid + 1 | ||
| 172 | - } | ||
| 173 | - } | ||
| 174 | - return l | ||
| 175 | - } | ||
| 176 | - | ||
| 177 | - /** | ||
| 178 | - 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到: | ||
| 179 | - 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2] | ||
| 180 | - 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7] | ||
| 181 | - 注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。 | ||
| 182 | - | ||
| 183 | - 给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。 | ||
| 184 | - */ | ||
| 185 | - // MARK: 旋转数组的最小数字 | ||
| 186 | - // https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/description/ | ||
| 187 | - public func findMin(nums: Array<Int>): Int { | ||
| 188 | - var l = 0 | ||
| 189 | - var h = nums.size - 1 | ||
| 190 | - while (l < h) { | ||
| 191 | - let m = l + (h - l) / 2 | ||
| 192 | - if (nums[m] <= nums[h]) { | ||
| 193 | - h = m | ||
| 194 | - } else { | ||
| 195 | - l = m + 1 | ||
| 196 | - } | ||
| 197 | - } | ||
| 198 | - return nums[l] | ||
| 199 | - } | ||
| 200 | - | ||
| 201 | - /** | ||
| 202 | - 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 | ||
| 203 | - | ||
| 204 | - 如果数组中不存在目标值 target,返回 [-1, -1]。 | ||
| 205 | - */ | ||
| 206 | - // MARK: 查找区间 | ||
| 207 | - // https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/ | ||
| 208 | - // 给定一个有序数组 nums 和一个目标 target,要求找到 target 在 nums 中的第一个位置和最后一个位置。 | ||
| 209 | - public func searchRange(nums: Array<Int>, target: Int): Array<Int> { | ||
| 210 | - func binarySearch(lower: Bool): Int { | ||
| 211 | - var left = 0 | ||
| 212 | - var right = nums.size - 1 | ||
| 213 | - var ans = nums.size | ||
| 214 | - while (left <= right) { | ||
| 215 | - let mid = left + (right - left) / 2 | ||
| 216 | - if (nums[mid] > target || (lower && nums[mid] >= target)) { | ||
| 217 | - right = mid - 1 | ||
| 218 | - ans = mid | ||
| 219 | - } else { | ||
| 220 | - left = mid + 1 | ||
| 221 | - } | ||
| 222 | - } | ||
| 223 | - return ans | ||
| 224 | - } | ||
| 225 | - | ||
| 226 | - let leftIdx = binarySearch(true) | ||
| 227 | - let rightIdx = binarySearch(false) - 1 | ||
| 228 | - if (leftIdx <= rightIdx && rightIdx < nums.size && nums[leftIdx] == target && nums[rightIdx] == target) { | ||
| 229 | - return [leftIdx, rightIdx] | ||
| 230 | - } else { | ||
| 231 | - return [-1, -1] | ||
| 232 | - } | ||
| 233 | - } | ||
| 234 | - | ||
| 235 | - public func verify() { | ||
| 236 | - @dprintMultline( | ||
| 237 | - // (true, 4) | ||
| 238 | - binarySearch([-1, 0, 3, 5, 9, 12], 9)[1] | ||
| 239 | - binarySearch2([-1, 0, 3, 5, 9, 12], 9) | ||
| 240 | - // 2 | ||
| 241 | - mySqrt(4) | ||
| 242 | - // "f" | ||
| 243 | - nextGreatestLetter([r"c", r"f", r"j"], r"c") | ||
| 244 | - // 10 | ||
| 245 | - singleNonDuplicate([3, 3, 7, 7, 10, 11, 11]) | ||
| 246 | - // 4 | ||
| 247 | - firstBadVersion(5) | ||
| 248 | - // 0 | ||
| 249 | - findMin([4, 5, 6, 7, 0, 1, 2]) | ||
| 250 | - // [3, 4] | ||
| 251 | - searchRange([5, 7, 7, 8, 8, 10], 8) | ||
| 252 | - ) | ||
| 253 | - } | ||
| 254 | -} | ||
| 255 | -// cjlint-ignore -end | ||
| @@ -1,102 +0,0 @@ | |||
| 1 | -// cjlint-ignore -start !G.OTH.03 !G.NAM.02 !G.NAM.03 !G.ERR.03 !G.FUN.01 | ||
| 2 | -/** | ||
| 3 | - * @author unravel | ||
| 4 | - * @see https://gitcode.com/unravel/cjalgorithm/overview | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -package cj_algorithm.leetcode | ||
| 8 | - | ||
| 9 | -import cjextensions.convert.Extension4Convert | ||
| 10 | -import usablemacros.verbose.dprintMultline | ||
| 11 | -import std.collection.ArrayList | ||
| 12 | -import cj_algorithm.structures.TreeNode | ||
| 13 | - | ||
| 14 | -public class ThinkDivideAndConquer { | ||
| 15 | - /** | ||
| 16 | - 给你一个由数字和运算符组成的字符串 expression ,按不同优先级组合数字和运算符,计算并返回所有可能组合的结果。你可以 按任意顺序 返回答案。 | ||
| 17 | - */ | ||
| 18 | - // MARK: 给表达式加括号 | ||
| 19 | - // https://leetcode-cn.com/problems/different-ways-to-add-parentheses/description/ | ||
| 20 | - func diffWaysToCompute(expression: String): Array<Int> { | ||
| 21 | - func diffWaysToComputeInner(chs: Array<Rune>): Array<Int> { | ||
| 22 | - let ways = ArrayList<Int>() | ||
| 23 | - for (i in 0..chs.size) { | ||
| 24 | - let c = chs[i] | ||
| 25 | - if (c == r"+" || c == r"-" || c == r"*") { | ||
| 26 | - // 计算左边运算式 | ||
| 27 | - chs[..i] | ||
| 28 | - let left = diffWaysToComputeInner(chs[..i]) | ||
| 29 | - // 计算右边运算式 | ||
| 30 | - let right = diffWaysToComputeInner(chs[(i + 1)..]) | ||
| 31 | - for (l in left) { | ||
| 32 | - for (r in right) { | ||
| 33 | - // 将左右运算式的结果进行遍历,添加到父运算式中 | ||
| 34 | - match (c) { | ||
| 35 | - case r"+" => ways.append(l + r) | ||
| 36 | - case r"-" => ways.append(l - r) | ||
| 37 | - case r"*" => ways.append(l * r) | ||
| 38 | - case _ => break | ||
| 39 | - } | ||
| 40 | - } | ||
| 41 | - } | ||
| 42 | - } | ||
| 43 | - } | ||
| 44 | - // 如果本次不包含任何运算,将值本身添加到结果数组中返回 | ||
| 45 | - if (ways.size == 0) { | ||
| 46 | - ways.append(String(chs).convertToInt64()) | ||
| 47 | - } | ||
| 48 | - return ways.toArray() | ||
| 49 | - } | ||
| 50 | - return diffWaysToComputeInner(expression.toRuneArray()) | ||
| 51 | - } | ||
| 52 | - | ||
| 53 | - /** | ||
| 54 | - 给你一个整数 n ,请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以按 任意顺序 返回答案。 | ||
| 55 | - */ | ||
| 56 | - // MARK: 不同的二叉搜索树 | ||
| 57 | - // https://leetcode-cn.com/problems/unique-binary-search-trees-ii/description/ | ||
| 58 | - // 给定一个数字 n,要求生成所有值为 1...n 的二叉搜索树 | ||
| 59 | - func generateTrees(n: Int): Array<?TreeNode> { | ||
| 60 | - func generateSubTrees( | ||
| 61 | - s: Int, | ||
| 62 | - e: Int | ||
| 63 | - ): Array<?TreeNode> { | ||
| 64 | - if (s > e) { | ||
| 65 | - // 返回nil这一步是必须的,代表着叶子节点的下一个几点 | ||
| 66 | - return [None] | ||
| 67 | - } | ||
| 68 | - let res = ArrayList<?TreeNode>() | ||
| 69 | - // 枚举可行根节点 | ||
| 70 | - for (i in s..=e) { | ||
| 71 | - // 获得所有可行的左子树集合 | ||
| 72 | - let leftSubTrees = generateSubTrees(s, i - 1) | ||
| 73 | - // 获得所有可行的右子树集合 | ||
| 74 | - let rightSubTrees = generateSubTrees(i + 1, e) | ||
| 75 | - // 从左子树集合中选出一棵左子树,从右子树集合中选出一棵右子树,拼接到根节点上 | ||
| 76 | - for (l in leftSubTrees) { | ||
| 77 | - for (r in rightSubTrees) { | ||
| 78 | - // 构建节点 | ||
| 79 | - let root = TreeNode(i) | ||
| 80 | - root.left = l | ||
| 81 | - root.right = r | ||
| 82 | - res.append(root) | ||
| 83 | - } | ||
| 84 | - } | ||
| 85 | - } | ||
| 86 | - return res.toArray() | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - return generateSubTrees(1, n) | ||
| 90 | - } | ||
| 91 | - | ||
| 92 | - public func verify() { | ||
| 93 | - @dprintMultline( | ||
| 94 | - // [-34,-14,-10,-10,10] | ||
| 95 | - diffWaysToCompute("2*3-4*5") | ||
| 96 | - // [[1,null,2,null,3],[1,null,3,2],[2,1,3],[3,1,null,null,2],[3,2,null,1]] | ||
| 97 | - generateTrees(3) | ||
| 98 | - ) | ||
| 99 | - } | ||
| 100 | -} | ||
| 101 | - | ||
| 102 | -// cjlint-ignore -end | ||
| @@ -1,280 +0,0 @@ | |||
| 1 | -// cjlint-ignore -start !G.OTH.03 !G.NAM.02 !G.NAM.03 !G.FUN.01 | ||
| 2 | -/** | ||
| 3 | - * @author unravel | ||
| 4 | - * @see https://gitcode.com/unravel/cj_algorithm/overview | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -package cj_algorithm.leetcode | ||
| 8 | - | ||
| 9 | -import std.math.sqrt | ||
| 10 | -import std.collection.HashSet | ||
| 11 | -import cj_algorithm.structures.ListNode | ||
| 12 | -import usablemacros.verbose.dprintMultline | ||
| 13 | - | ||
| 14 | -// 思想之双指针 | ||
| 15 | -public class ThinkDoublePointer { | ||
| 16 | - /** | ||
| 17 | - 给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。 | ||
| 18 | - | ||
| 19 | - 以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。 | ||
| 20 | - | ||
| 21 | - 你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。 | ||
| 22 | - | ||
| 23 | - 你所设计的解决方案必须只使用常量级的额外空间。 | ||
| 24 | - */ | ||
| 25 | - // MARK: 在有序数组中找出两个数,使它们的和为 target | ||
| 26 | - // 数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1) | ||
| 27 | - // https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/description/ | ||
| 28 | - // MARK: 和为S的两个数字 | ||
| 29 | - // https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/ | ||
| 30 | - // 思想:从两个方向往中间挤 | ||
| 31 | - func twoSum( | ||
| 32 | - numbers: Array<Int>, | ||
| 33 | - target: Int | ||
| 34 | - ): Array<Int> { | ||
| 35 | - if (numbers.size == 0) { | ||
| 36 | - return [-1, -1] | ||
| 37 | - } | ||
| 38 | - | ||
| 39 | - var i = 0 | ||
| 40 | - var j = numbers.size - 1 | ||
| 41 | - while (i < j) { | ||
| 42 | - let sum = numbers[i] + numbers[j] | ||
| 43 | - if (sum == target) { | ||
| 44 | - return [i, j] | ||
| 45 | - } else if (sum < target) { | ||
| 46 | - i += 1 | ||
| 47 | - } else { | ||
| 48 | - j -= 1 | ||
| 49 | - } | ||
| 50 | - } | ||
| 51 | - return [-1, -1] | ||
| 52 | - } | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | - 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。 | ||
| 56 | - */ | ||
| 57 | - // MARK: 判断一个非负整数是否为两个整数的平方和 | ||
| 58 | - // 因为最多只需要遍历一次 0~sqrt(target),所以时间复杂度为 O(sqrt(target))。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。 | ||
| 59 | - // https://leetcode-cn.com/problems/sum-of-square-numbers/description/ | ||
| 60 | - func judgeSquareSum(c: Int): Bool { | ||
| 61 | - if (c < 0) { | ||
| 62 | - return false | ||
| 63 | - } | ||
| 64 | - | ||
| 65 | - var i = 0 | ||
| 66 | - var j = Int64(sqrt(Float64(c))) | ||
| 67 | - while (i <= j) { | ||
| 68 | - let powSum = i * i + j * j | ||
| 69 | - if (powSum == c) { | ||
| 70 | - return true | ||
| 71 | - } else if (powSum > c) { | ||
| 72 | - j -= 1 | ||
| 73 | - } else { | ||
| 74 | - i += 1 | ||
| 75 | - } | ||
| 76 | - } | ||
| 77 | - return false | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - /** | ||
| 81 | - 给你一个字符串 s ,仅反转字符串中的所有元音字母,并返回结果字符串。 | ||
| 82 | - | ||
| 83 | - 元音字母包括 'a'、'e'、'i'、'o'、'u',且可能以大小写两种形式出现不止一次。 | ||
| 84 | - */ | ||
| 85 | - // MARK: 反转字符串中的元音字符 | ||
| 86 | - // https://leetcode.cn/problems/reverse-vowels-of-a-string/description/ | ||
| 87 | - /* | ||
| 88 | - 时间复杂度为 O(N):只需要遍历所有元素一次 | ||
| 89 | - 空间复杂度 O(1):只需要使用两个额外变量 | ||
| 90 | - */ | ||
| 91 | - func reverseVowels(s: String): String { | ||
| 92 | - if (s.isEmpty()) { | ||
| 93 | - return s | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | - let vowels = HashSet<Rune>([r"a", r"e", r"i", r"i", r"o", r"u", r"A", r"E", r"I", r"O", r"U"]) | ||
| 97 | - let result = s.toRuneArray() | ||
| 98 | - | ||
| 99 | - var i = 0 | ||
| 100 | - var j = result.size - 1 | ||
| 101 | - while (i <= j) { | ||
| 102 | - if (!vowels.contains(result[i])) { | ||
| 103 | - i += 1 | ||
| 104 | - continue | ||
| 105 | - } | ||
| 106 | - | ||
| 107 | - if (!vowels.contains(result[j])) { | ||
| 108 | - j -= 1 | ||
| 109 | - continue | ||
| 110 | - } | ||
| 111 | - result.swap(i, j) | ||
| 112 | - i += 1 | ||
| 113 | - j -= 1 | ||
| 114 | - } | ||
| 115 | - return String(result) | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - /** | ||
| 119 | - 给你一个字符串 s,最多 可以从中删除一个字符。 | ||
| 120 | - | ||
| 121 | - 请你判断 s 是否能成为回文字符串:如果能,返回 true ;否则,返回 false 。 | ||
| 122 | - */ | ||
| 123 | - // MARK: 可以删除一个字符,判断是否能构成回文字符串 | ||
| 124 | - // https://leetcode.cn/problems/valid-palindrome-ii/description/ | ||
| 125 | - func validPalindrome(s: String): Bool { | ||
| 126 | - let chars = s.toRuneArray() | ||
| 127 | - // 是否是回文串 | ||
| 128 | - func isPalindrome(i: Int, j: Int): Bool { | ||
| 129 | - var ii = i | ||
| 130 | - var jj = j | ||
| 131 | - while (ii < jj) { | ||
| 132 | - if (chars[ii] != chars[jj]) { | ||
| 133 | - return false | ||
| 134 | - } | ||
| 135 | - ii += 1 | ||
| 136 | - jj -= 1 | ||
| 137 | - } | ||
| 138 | - return true | ||
| 139 | - } | ||
| 140 | - | ||
| 141 | - var i = 0 | ||
| 142 | - var j = chars.size - 1 | ||
| 143 | - while (i < j) { | ||
| 144 | - if (chars[i] != chars[j]) { | ||
| 145 | - // 只需要判断剩余的子串是不是回文串 | ||
| 146 | - return isPalindrome(i, j - 1) || isPalindrome(i + 1, j) | ||
| 147 | - } | ||
| 148 | - i += 1 | ||
| 149 | - j -= 1 | ||
| 150 | - } | ||
| 151 | - return true | ||
| 152 | - } | ||
| 153 | - | ||
| 154 | - /** | ||
| 155 | - 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 | ||
| 156 | - | ||
| 157 | - 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 | ||
| 158 | - | ||
| 159 | - 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。 | ||
| 160 | - */ | ||
| 161 | - // MARK: 归并两个有序数组,把归并结果存到第一个数组上 | ||
| 162 | - // https://leetcode-cn.com/problems/merge-sorted-array/description/ | ||
| 163 | - func merge(nums1: Array<Int>, nums2: Array<Int>): Array<Int> { | ||
| 164 | - var index1 = nums1.size - nums2.size - 1 | ||
| 165 | - var index2 = nums2.size - 1 | ||
| 166 | - var indexMerge = nums1.size - 1 | ||
| 167 | - | ||
| 168 | - while (index2 >= 0) { | ||
| 169 | - if (index1 < 0) { | ||
| 170 | - // 把nums2依次加到末尾 | ||
| 171 | - nums1[indexMerge] = nums2[index2] | ||
| 172 | - index2 -= 1 | ||
| 173 | - } else if (nums1[index1] > nums2[index2]) { | ||
| 174 | - nums1[indexMerge] = nums1[index1] | ||
| 175 | - index1 -= 1 | ||
| 176 | - } else { | ||
| 177 | - nums1[indexMerge] = nums2[index2] | ||
| 178 | - index2 -= 1 | ||
| 179 | - } | ||
| 180 | - indexMerge -= 1 | ||
| 181 | - } | ||
| 182 | - nums1 | ||
| 183 | - } | ||
| 184 | - | ||
| 185 | - /** | ||
| 186 | - 给你一个链表的头节点 head ,判断链表中是否有环。 | ||
| 187 | - | ||
| 188 | - 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。 | ||
| 189 | - | ||
| 190 | - 如果链表中存在环 ,则返回 true 。 否则,返回 false 。 | ||
| 191 | - */ | ||
| 192 | - // MARK: 判断链表是否存在环 | ||
| 193 | - // https://leetcode-cn.com/problems/linked-list-cycle/description/ | ||
| 194 | - func hasCycle(head: ?ListNode): Bool { | ||
| 195 | - if (head.isNone()) { | ||
| 196 | - return false | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - let headNode = head.getOrThrow() | ||
| 200 | - var l1 = head | ||
| 201 | - var l2 = headNode.next | ||
| 202 | - while (let Some(l1Node) <- l1 && let Some(l2Node) <- l2) { | ||
| 203 | - if (let Some(l2NextNode) <- l2Node.next) { | ||
| 204 | - if (refEq(l1Node, l2Node)) { | ||
| 205 | - return true | ||
| 206 | - } | ||
| 207 | - l1 = l1Node.next | ||
| 208 | - l2 = l2NextNode.next | ||
| 209 | - } else { | ||
| 210 | - break | ||
| 211 | - } | ||
| 212 | - } | ||
| 213 | - return false | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | - /** | ||
| 217 | - 给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。 | ||
| 218 | - | ||
| 219 | - 如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。 | ||
| 220 | - */ | ||
| 221 | - // MARK: 最长子序列,删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串 | ||
| 222 | - // https://leetcode-cn.com/problems/longest-word-in-dictionary-through-deleting/ | ||
| 223 | - func findLongestWord(s: String, dictionary: Array<String>): String { | ||
| 224 | - let sChars = s.toRuneArray() | ||
| 225 | - let sCharsCount = sChars.size | ||
| 226 | - // 判断 target是否是由s中的字符构成的字符串 | ||
| 227 | - func isSubstrOfS(target: String): Bool { | ||
| 228 | - let targetChars = target.toRuneArray() | ||
| 229 | - let targetCharsCount = targetChars.size | ||
| 230 | - var i = 0 | ||
| 231 | - var j = 0 | ||
| 232 | - while (i < sCharsCount && j < targetCharsCount) { | ||
| 233 | - if (sChars[i] == targetChars[j]) { | ||
| 234 | - j += 1 | ||
| 235 | - } | ||
| 236 | - i += 1 | ||
| 237 | - } | ||
| 238 | - return j == targetChars.size | ||
| 239 | - } | ||
| 240 | - | ||
| 241 | - var longestWord = "" | ||
| 242 | - // 遍历 dictionary中的每一个target,判断target是否是s的子串 | ||
| 243 | - for (target in dictionary) { | ||
| 244 | - let l1 = longestWord.size | ||
| 245 | - let l2 = target.size | ||
| 246 | - if (l1 > l2 || (l1 == l2 && longestWord < target)) { | ||
| 247 | - continue | ||
| 248 | - } | ||
| 249 | - if (isSubstrOfS(target)) { | ||
| 250 | - longestWord = target | ||
| 251 | - } | ||
| 252 | - } | ||
| 253 | - return longestWord | ||
| 254 | - } | ||
| 255 | - | ||
| 256 | - public func verify() { | ||
| 257 | - let l1 = ListNode(val: 3) | ||
| 258 | - let l2 = ListNode([2, 0, 4]) | ||
| 259 | - l1.next = l2 | ||
| 260 | - l2.next = l1 | ||
| 261 | - | ||
| 262 | - @dprintMultline( | ||
| 263 | - // [0,1] | ||
| 264 | - twoSum([2, 7, 11, 15], 9) | ||
| 265 | - // true | ||
| 266 | - judgeSquareSum(5) | ||
| 267 | - // "AceCreIm" | ||
| 268 | - reverseVowels("IceCreAm") | ||
| 269 | - // true | ||
| 270 | - validPalindrome("abca") | ||
| 271 | - // [1,2,2,3,5,6] | ||
| 272 | - merge([1, 2, 3, 0, 0, 0], [2, 5, 6]) | ||
| 273 | - // true | ||
| 274 | - hasCycle(l1) | ||
| 275 | - // "apple" | ||
| 276 | - findLongestWord("abpcplea", ["ale", "apple", "monkey", "plea"]) | ||
| 277 | - ) | ||
| 278 | - } | ||
| 279 | -} | ||
| 280 | -// cjlint-ignore -end | ||
| @@ -1,208 +0,0 @@ | |||
| 1 | -// cjlint-ignore -start !G.OTH.03 !G.NAM.02 !G.NAM.03 !G.FUN.01 | ||
| 2 | -/** | ||
| 3 | - * @author unravel | ||
| 4 | - * @see https://gitcode.com/unravel/cjalgorithm/overview | ||
| 5 | - */ | ||
| 6 | - | ||
| 7 | -package cj_algorithm.leetcode | ||
| 8 | - | ||
| 9 | -import std.sort.unstableSort | ||
| 10 | -import std.collection.{HashMap, ArrayList} | ||
| 11 | -import usablemacros.verbose.dprintMultline | ||
| 12 | - | ||
| 13 | -// 思想之排序 | ||
| 14 | -public class ThinkSort { | ||
| 15 | - /** | ||
| 16 | - 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 | ||
| 17 | - | ||
| 18 | - 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 | ||
| 19 | - */ | ||
| 20 | - // MARK: 找到倒数第 k 个的元素 | ||
| 21 | - // https://leetcode-cn.com/problems/kth-largest-element-in-an-array/description/ | ||
| 22 | - func findKthLargest(nums: Array<Int>, k: Int): Int { | ||
| 23 | - unstableSort(nums) | ||
| 24 | - return nums[nums.size - k] | ||
| 25 | - } | ||
| 26 | - | ||
| 27 | - // 快速选择 :时间复杂度 O(N),空间复杂度 O(1) | ||
| 28 | - func findKthLargestQuickSort(nums: Array<Int>, k: Int): Int { | ||
| 29 | - if (k > nums.size || k <= 0) { | ||
| 30 | - return -1 | ||
| 31 | - } | ||
| 32 | - | ||
| 33 | - let kk = nums.size - k | ||
| 34 | - // 因为会进行排序,就用mNums接收 | ||
| 35 | - let mNums = nums | ||
| 36 | - | ||
| 37 | - /* 切分元素 */ | ||
| 38 | - func partition(l: Int, h: Int): Int { | ||
| 39 | - var i = l | ||
| 40 | - var j = h + 1 | ||
| 41 | - // 二路排序算法 | ||
| 42 | - while (true) { | ||
| 43 | - i += 1 | ||
| 44 | - // nums[l] 作为比较的基准点 | ||
| 45 | - // 找到小于nums[l]的值所在的最大下标 | ||
| 46 | - while (mNums[i] < mNums[l] && i < h) { | ||
| 47 | - i += 1 | ||
| 48 | - } | ||
| 49 | - j -= 1 | ||
| 50 | - // 找到大于nums[l]的值所在的最小下标 | ||
| 51 | - while (mNums[j] > mNums[l] && j > l) { | ||
| 52 | - j -= 1 | ||
| 53 | - } | ||
| 54 | - if (i >= j) { | ||
| 55 | - break | ||
| 56 | - } | ||
| 57 | - mNums.swap(i, j) | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | - mNums.swap(l, j) | ||
| 61 | - return j | ||
| 62 | - } | ||
| 63 | - | ||
| 64 | - func findKthSmallest(k: Int) { | ||
| 65 | - var l = 0 | ||
| 66 | - var h = mNums.size - 1 | ||
| 67 | - while (l < h) { | ||
| 68 | - // 查找切分点 | ||
| 69 | - let j = partition(l, h) | ||
| 70 | - if (j == k) { | ||
| 71 | - break | ||
| 72 | - } else if (j > k) { | ||
| 73 | - h = j - 1 | ||
| 74 | - } else { | ||
| 75 | - l = j + 1 | ||
| 76 | - } | ||
| 77 | - } | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - findKthSmallest(kk) | ||
| 81 | - return mNums[kk] | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - /** | ||
| 85 | - 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 | ||
| 86 | - */ | ||
| 87 | - // 桶排序 | ||
| 88 | - // MARK: 出现频率最多的 k 个元素 | ||
| 89 | - // https://leetcode-cn.com/problems/top-k-frequent-elements/description/ | ||
| 90 | - func topKFrequent(nums: Array<Int>, k: Int): Array<Int> { | ||
| 91 | - // 存储值和频率的对应表 | ||
| 92 | - let frequencyForNum = HashMap<Int, Int>(nums.size) {i: Int => (i, 0)} | ||
| 93 | - var maxFrequency = 0 | ||
| 94 | - for (num in nums) { | ||
| 95 | - frequencyForNum[num] += 1 | ||
| 96 | - maxFrequency = max(maxFrequency, frequencyForNum[num]) | ||
| 97 | - } | ||
| 98 | - | ||
| 99 | - //设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i | ||
| 100 | - // 下标代表频率,值代表原值 | ||
| 101 | - let len = maxFrequency + 1 | ||
| 102 | - let buckets = Array<ArrayList<Int>>(len) {i => ArrayList<Int>()} | ||
| 103 | - | ||
| 104 | - for ((value, frequency) in frequencyForNum) { | ||
| 105 | - buckets[frequency].append(value) | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - // 把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数 | ||
| 109 | - let topK = ArrayList<Int>() | ||
| 110 | - buckets.reverse() | ||
| 111 | - for (bucket in buckets) { | ||
| 112 | - if (topK.size >= k) { | ||
| 113 | - break | ||
| 114 | - } | ||
| 115 | - if (bucket.isEmpty()) { | ||
| 116 | - continue | ||
| 117 | - } | ||
| 118 | - let remindCount = k - topK.size | ||
| 119 | - if (bucket.size <= remindCount) { | ||
| 120 | - topK.appendAll(bucket) | ||
| 121 | - } else { | ||
| 122 | - topK.appendAll(bucket[0..remindCount]) | ||
| 123 | - } | ||
| 124 | - } | ||
| 125 | - return topK.toArray() | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | - /** | ||
| 129 | - 给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。 | ||
| 130 | - | ||
| 131 | - 返回 已排序的字符串 。如果有多个答案,返回其中任何一个。 | ||
| 132 | - */ | ||
| 133 | - // MARK: 按照字符出现次数对字符串排序 | ||
| 134 | - // https://leetcode-cn.com/problems/sort-characters-by-frequency/description/ | ||
| 135 | - func frequencySort(s: String): String { | ||
| 136 | - let sChars = s.toRuneArray() | ||
| 137 | - let frequencyForNum = HashMap<Rune, Int>(sChars.size) {i: Int => (sChars[i], 0)} | ||
| 138 | - // 存储每个字符出现的频率 | ||
| 139 | - for (c in sChars) { | ||
| 140 | - frequencyForNum[c] += 1 | ||
| 141 | - } | ||
| 142 | - // 翻转,第i个位置,存储出现i次的数据的数组 | ||
| 143 | - let len1 = sChars.size + 1 | ||
| 144 | - let buckets = Array<ArrayList<Rune>>(len1) {_ => ArrayList<Rune>()} | ||
| 145 | - | ||
| 146 | - for ((value, frequency) in frequencyForNum) { | ||
| 147 | - buckets[frequency].append(value) | ||
| 148 | - } | ||
| 149 | - | ||
| 150 | - let frequencyStr = ArrayList<Rune>() | ||
| 151 | - for (i in (buckets.size - 1)..=0 : -1) { | ||
| 152 | - let bucket = buckets[i] | ||
| 153 | - if (bucket.isEmpty()) { | ||
| 154 | - continue | ||
| 155 | - } | ||
| 156 | - for (ch in bucket) { | ||
| 157 | - for (_ in 0..i) { | ||
| 158 | - frequencyStr.append(ch) | ||
| 159 | - } | ||
| 160 | - } | ||
| 161 | - } | ||
| 162 | - return String(frequencyStr) | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - /** | ||
| 166 | - 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 | ||
| 167 | - | ||
| 168 | - 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 | ||
| 169 | - */ | ||
| 170 | - // MARK: 荷兰国旗问题 | ||
| 171 | - // https://leetcode-cn.com/problems/sort-colors/description/ | ||
| 172 | - // 在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素, | ||
| 173 | - // 而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色 | ||
| 174 | - func sortColors(nums: Array<Int>): Array<Int> { | ||
| 175 | - var zero = -1 | ||
| 176 | - var one = 0 | ||
| 177 | - var two = nums.size | ||
| 178 | - while (one < two) { | ||
| 179 | - if (nums[one] == 0) { | ||
| 180 | - zero += 1 | ||
| 181 | - nums.swap(zero, one) | ||
| 182 | - one += 1 | ||
| 183 | - } else if (nums[one] == 2) { | ||
| 184 | - two -= 1 | ||
| 185 | - nums.swap(two, one) | ||
| 186 | - } else { | ||
| 187 | - one += 1 | ||
| 188 | - } | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - nums | ||
| 192 | - } | ||
| 193 | - | ||
| 194 | - public func verify() { | ||
| 195 | - @dprintMultline( | ||
| 196 | - // 4 | ||
| 197 | - findKthLargest([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) | ||
| 198 | - findKthLargestQuickSort([3, 2, 3, 1, 2, 4, 5, 5, 6], 4) | ||
| 199 | - // [1,2] | ||
| 200 | - topKFrequent([1, 1, 1, 2, 2, 3], 2) | ||
| 201 | - // bbAa 或 bbaA | ||
| 202 | - frequencySort("Aabb") | ||
| 203 | - // [0,0,1,1,2,2] | ||
| 204 | - sortColors([2, 0, 2, 1, 1, 0]) | ||
| 205 | - ) | ||
| 206 | - } | ||
| 207 | -} | ||
| 208 | -// cjlint-ignore -end | ||