commit 8665c3f7b546d39cda6d81c64179559306c1a162 Author: lensfrex Date: Sat Apr 6 13:00:01 2024 +0800 feat: 第一次作业 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1acc3cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +*.iml +*.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +*.log + +/config.yml diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad7ccd3 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Lavos + +算法设计与分析例题/作业代码合集,使用go编写实现 + +## 目录结构 + +包代码目录格式为,`cap{x}`,对应的是第x章的例题和作业代码,其中每个例题/作业代码文件均命名为`ex{x}.go`以及`work{x}.go`。 + +如第三章例6,位于`cap3/ex6.go`中,第四章作业5,位于`cap4/ex5.go`中。 + +## 运行/测试 + +要运行测试,需要go开发环境。[Go installation instructions: https://go.dev/doc/install](https://go.dev/doc/install) + +完成后,运行 +```shell +go test lavos/cap3 +``` + +即可运行该章节下所有的例题和作业的test用例,例题和作业的test代码位于`example_test.go`和`work_test.go`中。 + +如果需要单独测试某一个题目,运行 +```shell +go test lavos/cap3 -v '-test.run' 'Ex6$' +go test lavos/cap3 -v '-test.run' 'Work1$' +``` +即可运行对应的题目,如示例中将会运行第三章例题6的test和作业1 + +## 其他细节 + +由于习惯原因,我并不是很喜欢在算法代码中进行具体的输出操作,或者直接将输出输入放置于算法代码中,而是使用了类似于leetcode中的核心代码模式,因此`ex{x}.go`和`work{x}.go`中的代码模式均为核心代码,不负责输出输入, +如有需要输出的算法,通过callback实现,传入能够实现输出的callback函数,在核心中调用callback来实现输出,核心代码不关心也不区负责输出输入,输出输入由test负责实现。 \ No newline at end of file diff --git a/cap3/ex1.go b/cap3/ex1.go new file mode 100644 index 0000000..4704af1 --- /dev/null +++ b/cap3/ex1.go @@ -0,0 +1,17 @@ +package cap3 + +// Ex1 例题1 +// 求1/1! - 1/3! + 1/5! - 1/7! + ... + (-1)^(n+1)/(2n-1)! +func Ex1(n int) float64 { + sum, fab := 0.0, uint64(1) + for i := 1; i <= n; i++ { + fab *= 2*uint64(i) + 1 + if i%2 == 0 { + sum += -1.0 / float64(fab) + } else { + sum += 1.0 / float64(fab) + } + } + + return sum +} diff --git a/cap3/ex10.go b/cap3/ex10.go new file mode 100644 index 0000000..1f22e4f --- /dev/null +++ b/cap3/ex10.go @@ -0,0 +1,29 @@ +package cap3 + +// Ex10 从0~n的整数中取r个不同的数做一组,求所有可能的组合 +func Ex10(n, r int, callback func(nums []int)) int { + if r > n { + return 0 + } + + count := 0 + combine := make([]int, r) + + var findCombine func(m, k int) + findCombine = func(n, k int) { + // 从n开始往下遍历组合 + for i := n; i >= k; i-- { + combine[k-1] = i + if k > 1 { + // k-1和n-1,继续深入遍历,处理子问题 + findCombine(i-1, k-1) + } else { + count++ + callback(combine) + } + } + } + + findCombine(n, r) + return count +} diff --git a/cap3/ex11.go b/cap3/ex11.go new file mode 100644 index 0000000..3d1750e --- /dev/null +++ b/cap3/ex11.go @@ -0,0 +1,23 @@ +package cap3 + +type Candidate int // 候选人 +type Vote int // 票数 + +// Ex11 投票统计,有调整,这里不使用数组存储结果,使用hashmap表存结果 +func Ex11(votes []Candidate, numOfCandidate int) map[Candidate]Vote { + result := map[Candidate]Vote{} + for _, candidate := range votes { + // 无效的投票 + if candidate <= 0 || int(candidate) > numOfCandidate { + continue + } + + if _, exists := result[candidate]; !exists { + result[candidate] = 1 + } else { + result[candidate]++ + } + } + + return result +} diff --git a/cap3/ex12.go b/cap3/ex12.go new file mode 100644 index 0000000..d0c51a6 --- /dev/null +++ b/cap3/ex12.go @@ -0,0 +1,21 @@ +package cap3 + +type HeightGrade int + +// Ex12 身高统计,这里也是用的hashmap存结果 +func Ex12(heights []int) []int { + result := make([]int, 8) + for _, height := range heights { + // 身高映射,上下限调整 + mapping := height/5 - 29 + if mapping < 0 { + mapping = 0 + } else if mapping > 7 { + mapping = 7 + } + + result[mapping]++ + } + + return result +} diff --git a/cap3/ex13.go b/cap3/ex13.go new file mode 100644 index 0000000..194d153 --- /dev/null +++ b/cap3/ex13.go @@ -0,0 +1,26 @@ +package cap3 + +type Subject int +type Student int + +func Ex14(subjectPassedStudentMap map[Subject]Student) []Student { + result := make([]Student, 0) + + // 学生及格科目数记录 + studentRecord := map[Student]int{} + for _, student := range subjectPassedStudentMap { + // 对当前学生及格科目计数 + if _, exists := studentRecord[student]; !exists { + studentRecord[student] = 1 + } else { + studentRecord[student]++ + } + + // 学生及格科目数已达到传入的科目数,即为全科及格 + if studentRecord[student] == len(subjectPassedStudentMap) { + result = append(result, student) + } + } + + return result +} diff --git a/cap3/ex14.go b/cap3/ex14.go new file mode 100644 index 0000000..f7877b6 --- /dev/null +++ b/cap3/ex14.go @@ -0,0 +1,25 @@ +package cap3 + +var _numberTexts = []string{ + "zero", "one", "two", "three", "four", + "five", "six", "seven", "eight", "nine", +} + +// Ex14NumInput 输入数字,输出英文 +func Ex14NumInput(num uint64, callback func(str string)) { + digits := make([]uint8, 0, 20) + for i := num; i >= 1; i /= 10 { + digits = append(digits, uint8(i%10)) + } + + for i := len(digits) - 1; i >= 0; i-- { + callback(_numberTexts[digits[i]]) + } +} + +// Ex14StrInput 输入数字字符串,输出英文 +func Ex14StrInput(num string, callback func(str string)) { + for _, char := range num { + callback(_numberTexts[char-48]) + } +} diff --git a/cap3/ex15.go b/cap3/ex15.go new file mode 100644 index 0000000..177329d --- /dev/null +++ b/cap3/ex15.go @@ -0,0 +1,24 @@ +package cap3 + +var ( + cashes = []int{50, 20, 10, 5, 2, 1} +) + +// Ex15 最少的纸币找零钱 +func Ex15(amount, paid int) map[int]int { + result := map[int]int{} + + // 还要找的钱 + change := paid - amount + for i, cash := range cashes { + // 当前面额需要找的张数,整除后结果为0说明当前面额不足以找零 + cashNum := change / cash + if cashNum != 0 { + result[i] = cashNum + // 当前面额找完后更新剩余未找余额 + change = change - cashNum*cash + } + } + + return result +} diff --git a/cap3/ex16.go b/cap3/ex16.go new file mode 100644 index 0000000..9404149 --- /dev/null +++ b/cap3/ex16.go @@ -0,0 +1,22 @@ +package cap3 + +// Ex16 求x,其平方为一个各位数字互不相同的九位数 +func Ex16(callback func(x, x2 int)) { +Next: + for x := 10000; x < 32000; x++ { + numCntMap := map[int]bool{} + + x2 := x * x + for i := x2; i >= 1; i /= 10 { + digit := i % 10 + // 当前数字已经出现过,直接跳过,找下一个x,如果没出现过,则做标记后看下一位数字情况 + if _, exists := numCntMap[digit]; exists { + continue Next + } + + numCntMap[digit] = true + } + + callback(x, x2) + } +} diff --git a/cap3/ex17.go b/cap3/ex17.go new file mode 100644 index 0000000..b9e7153 --- /dev/null +++ b/cap3/ex17.go @@ -0,0 +1,53 @@ +package cap3 + +// _node 链表节点,单向循环链表 +type _node struct { + Value int + Next *_node +} + +// 初始化长度为n的环 +func _initCircle(n int) *_node { + head := &_node{0, nil} + current := head + for i := 1; i < n; i++ { + newNode := &_node{i, nil} + current.Next = newNode + current = newNode + } + + // 头尾相连成环 + current.Next = head + return head +} + +// Ex17Normal 小朋友游戏(约瑟夫问题),一般解法(非数学解法),n: 总人数(节点数),k:出队报数 +func Ex17Normal(n, k int) int { + circle := _initCircle(n) + current := circle + for current.Next != current { + for i := 1; i < k-1; i++ { + current = current.Next + } + + // 报到k-1时移除下一个节点 + next := current.Next + current.Next = next.Next + current = current.Next + + // 避免内存泄露 + next.Next = nil + } + + return current.Value +} + +// Ex17Math 小朋友游戏(约瑟夫问题),公式递推解法,n: 总人数(节点数),k:出队报数 +func Ex17Math(n, k int) int { + idx := 0 + for i := 2; i <= n; i++ { + idx = (idx + k) % i + } + + return idx +} diff --git a/cap3/ex18.go b/cap3/ex18.go new file mode 100644 index 0000000..c24565e --- /dev/null +++ b/cap3/ex18.go @@ -0,0 +1,107 @@ +package cap3 + +import ( + "strings" +) + +// Ex18 超长整数乘法(都超长) +func Ex18(a, b string) string { + result := make([]uint8, len(a)+len(b)) + resultEndIdx := len(result) - 1 + for i := 0; i < len(b); i++ { + carry := uint8(0) + aEnd, bEnd, resultIdx := len(a)-1, len(b)-1, 0 + bDigit := b[bEnd-i] - 48 + for j := 0; j < len(a); j++ { + // 字符转数字 + aDigit := a[aEnd-j] - 48 + // 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位 + resultIdx = resultEndIdx - (i + j) + num := result[resultIdx] + aDigit*bDigit + carry + // 当前位为计算结果的个位,进位为十位上的数字 + result[resultIdx] = num % 10 + carry = num / 10 + } + + // 如果还有进位,则对下一个结果位添加进位,此时不会再有进位 + if carry != 0 { + resultIdx-- + result[resultIdx] += carry + } + } + + zeroPrefix := true + sb := strings.Builder{} + for _, num := range result { + if zeroPrefix && num == 0 { + continue + } else { + zeroPrefix = false + } + + sb.WriteByte(num + 48) + } + + return sb.String() +} + +// Ex18type2 超长整数乘法(都超长),a和b都是大端序的数字数组/切片 +func Ex18type2(a, b []uint8) []uint8 { + result := make([]uint8, len(a)+len(b)) + resultEndIdx := len(result) - 1 + for i := 0; i < len(b); i++ { + carry := uint8(0) + aEnd, bEnd, resultIdx := len(a)-1, len(b)-1, 0 + bDigit := b[bEnd-i] + for j := 0; j < len(a); j++ { + // 字符转数字 + aDigit := a[aEnd-j] + // 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位 + resultIdx = resultEndIdx - (i + j) + num := result[resultIdx] + aDigit*bDigit + carry + // 当前位为计算结果的个位,进位为十位上的数字 + result[resultIdx] = num % 10 + carry = num / 10 + } + + // 如果还有进位,则对下一个结果位添加进位,此时不会再有进位 + if carry != 0 { + resultIdx-- + result[resultIdx] += carry + } + } + + return result +} + +// Ex18type3 超长整数乘法(都超长),a是大端序的数字数组/切片,b为uint64 +func Ex18type3(a []uint8, b uint64) []uint8 { + result := make([]uint8, len(a)+22) + resultEndIdx := len(result) - 1 + bOffset := 0 + for i := b; i >= 1; i /= 10 { + carry := uint8(0) + bDigit := uint8(i % 10) + aEnd, resultIdx := len(a)-1, 0 + for j := 0; j < len(a); j++ { + // 字符转数字 + aDigit := a[aEnd-j] + // 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位 + resultIdx = resultEndIdx - (bOffset + j) + num := result[resultIdx] + aDigit*bDigit + carry + // 当前位为计算结果的个位,进位为十位上的数字 + result[resultIdx] = num % 10 + carry = num / 10 + } + + // 如果还有进位,则对下一个结果位添加进位,此时不会再有进位 + if carry != 0 { + resultIdx-- + result[resultIdx] += carry + } + + bOffset++ + } + + return result +} diff --git a/cap3/ex19.go b/cap3/ex19.go new file mode 100644 index 0000000..62f1e51 --- /dev/null +++ b/cap3/ex19.go @@ -0,0 +1,137 @@ +package cap3 + +import "strings" + +// Ex19 高精度阶乘 +func Ex19(n uint64) string { + result := []uint8{1} + for i := uint64(2); i <= n; i++ { + result = multiply(result, i) + } + + return DigitSlice2String(result) +} + +func multiply(a []uint8, b uint64) []uint8 { + result := make([]uint8, len(a)+22) + resultEndIdx := len(result) - 1 + bOffset := 0 + for i := b; i >= 1; i /= 10 { + carry := uint8(0) + bDigit := uint8(i % 10) + aEnd, resultIdx := len(a)-1, 0 + for j := 0; j < len(a); j++ { + // 字符转数字 + aDigit := a[aEnd-j] + // 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位 + resultIdx = resultEndIdx - (bOffset + j) + num := result[resultIdx] + aDigit*bDigit + carry + // 当前位为计算结果的个位,进位为十位上的数字 + result[resultIdx] = num % 10 + carry = num / 10 + } + + // 如果还有进位,则对下一个结果位添加进位,此时不会再有进位 + if carry != 0 { + resultIdx-- + result[resultIdx] += carry + } + + bOffset++ + } + + return result +} + +func multiply2(a []uint8, b uint64) []uint8 { + result := make([]uint8, len(a)+22) + resultEndIdx := len(result)*2 - 1 + bOffset := 0 + for i := b; i >= 1; i /= 10 { + carry := uint8(0) + bDigit := uint8(i % 10) + aEnd, resultIdx := len(a)*2-1, 0 + for j := 0; j < len(a); j++ { + // 字符转数字 + aDigit := uint8(0) + // 4,5 -> 37(0010,0101) + aIdx := aEnd - j + if aIdx%2 == 0 { + aDigit = a[aIdx/2] >> 4 + } else { + aDigit = a[aIdx/2] & 0b00001111 + } + + // 当前位计算结果 = 当前结果 + aDigit*bDigit + 进位 + resultIdx = resultEndIdx - (bOffset + j) + bitIdx := resultIdx / 2 + if resultIdx%2 == 0 { + num := (result[bitIdx] >> 4) + aDigit*bDigit + carry + // 清位 + // 4,5 -> 37(0010,0101) => 0,5 -> 5(0000,0101) + result[bitIdx] &= 0b00001111 + // 位赋值 + // 0,5 -> 5(0000,0101) => 5,5 -> 5(0101,0101) + // 5(0000,0101) << 4 => 80(0101,0000) + // 5(0000,0101) | 80(0101,0000) => 5,5 -> 5(0101,0101) + result[bitIdx] |= (num % 10) << 4 + carry = num / 10 + } else { + num := (result[bitIdx] & 0b00001111) + aDigit*bDigit + carry + // 清位 + // 4,5 -> 37(0010,0101) => 4,0 -> 32(0010,0000) + result[bitIdx] &= 0b11110000 + // 赋值 + // 4,0 -> 32(0010,0000) => 4,5 -> 37(0010,0101) + // 32(0010,0000) | 5(0000,0101) => 4,5 -> 37(0010,0101) + result[bitIdx] |= num % 10 + carry = num / 10 + } + } + + // 如果还有进位,则对下一个结果位添加进位,此时不会再有进位 + if carry != 0 { + resultIdx-- + bitIdx := resultIdx / 2 + if resultIdx%2 == 0 { + // 4,5 -> 37(0010,0101) => 5,5 -> 37(0101,0101) + // 4+1 -> 5(0000,0101) << 4 => 80(0101,0000) + // 5(0000,0101) | 80(0101,0000) => 5,5 -> 5(0101,0101) + result[bitIdx] &= 0b00001111 + num := (result[bitIdx] >> 4) + carry + result[bitIdx] |= num << 4 + } else { + result[bitIdx] &= 0b11110000 + num := (result[bitIdx] & 0b00001111) + carry + result[bitIdx] |= num + } + } + + bOffset++ + } + + return result +} + +// Ex19LowMem 低内存使用版,两位数字结果按位存在同一个uint8中,一个0~9数字只使用四字节,uint8可存两位,一个字节拆成两半用 +func Ex19LowMem(n uint64) string { + result := []uint8{1} + for i := uint64(2); i <= n; i++ { + result = multiply2(result, i) + } + + zeroPrefix := true + sb := strings.Builder{} + for _, num := range result { + if zeroPrefix && num == 0 { + continue + } else { + zeroPrefix = false + } + + sb.WriteByte((num >> 4) + 48) + sb.WriteByte((num & 0b00001111) + 48) + } + + return sb.String() +} diff --git a/cap3/ex2.go b/cap3/ex2.go new file mode 100644 index 0000000..d88227a --- /dev/null +++ b/cap3/ex2.go @@ -0,0 +1,26 @@ +package cap3 + +// Ex2 例2 求1000以内的完数,返回一个map,key为完数,value为其因数的数组 +func Ex2() map[int][]int { + result := map[int][]int{} + for i := range 1000 { + if i < 2 { + continue + } + + sum := 0 + factors := make([]int, 0, 20) + for j := 1; j <= i/2; j++ { + if i%j == 0 { + factors = append(factors, j) + sum += j + } + } + + if sum == i { + result[i] = factors + } + } + + return result +} diff --git a/cap3/ex3.go b/cap3/ex3.go new file mode 100644 index 0000000..a637dac --- /dev/null +++ b/cap3/ex3.go @@ -0,0 +1,29 @@ +package cap3 + +// Ex3 求鞍点数,行上最小,列上最大 +func Ex3(matrix [][]int) (int, int, int) { + rowMinIdx := map[int]int{} // key: 行索引, value: 最小值所在列 + colMaxIdx := map[int]int{} // key: 列索引, value: 最大值所在行 + for rowIdx, row := range matrix { + rowMinIdx[rowIdx] = 0 + for colIdx, currNum := range row { + if currNum < row[rowMinIdx[rowIdx]] { + rowMinIdx[rowIdx] = colIdx + } + + if _, has := colMaxIdx[colIdx]; !has { + colMaxIdx[colIdx] = rowIdx + } else if currNum > matrix[colMaxIdx[colIdx]][colIdx] { + colMaxIdx[colIdx] = rowIdx + } + } + } + + for rowIdx, colIdx := range rowMinIdx { + if rowIdx == colMaxIdx[colIdx] { + return rowIdx, colIdx, matrix[rowIdx][colIdx] + } + } + + return -1, -1, 0 +} diff --git a/cap3/ex4.go b/cap3/ex4.go new file mode 100644 index 0000000..17e9fed --- /dev/null +++ b/cap3/ex4.go @@ -0,0 +1 @@ +package cap3 diff --git a/cap3/ex5.go b/cap3/ex5.go new file mode 100644 index 0000000..b11af1e --- /dev/null +++ b/cap3/ex5.go @@ -0,0 +1,69 @@ +package cap3 + +import "lavos/common" + +// Ex5 例题5 汉诺塔问题,输出移动的步骤,可指定算法类型,每做出一步移动操作都将会调用callback +func Ex5(beadNum int, algoType AlgoType, callback func(from string, to string)) { + switch algoType { + case AlgoTypeRecursion: + Ex5Recursive(beadNum, "A", "B", "C", callback) + default: + Ex5NonRecursive(beadNum, "A", "B", "C", callback) + } +} + +func Ex5Recursive(beadNum int, a, b, c string, callback func(from string, to string)) { + if beadNum == 1 { + // 如果只有一颗珠子,直接从 A 移动到 C,结束 + callback(a, c) + } else { + // 第二步:将所有在 N 之上的珠子(即 N-1 颗珠子)从 A 移动到 B。此时 C 是中转站 + Ex5Recursive(beadNum-1, a, c, b, callback) + + // 第二步:将 A 的珠子移动到 C + callback(a, c) + + // 第三步:将剩余的 N-1 颗珠子从 B 移动到 C。此时 A 是中转站 + Ex5Recursive(beadNum-1, b, a, c, callback) + } +} + +type _step struct { + n int + from, transit, to string +} + +func Ex5NonRecursive(beadNum int, a, b, c string, callback func(from string, to string)) { + stack := common.NewStack[_step](beadNum * 5) + // 最终状态:a->c,b作为中转 + stack.Push(&_step{ + n: beadNum, + from: a, transit: b, to: c, + }) + for !stack.Empty() { + status := stack.Pop() + if status.n == 1 { + callback(status.from, status.to) + } else { + // 把子问题的三个基本步骤压入栈 + stack.Push(&_step{ + n: status.n - 1, + from: status.transit, + transit: status.from, + to: status.to, + }) + stack.Push(&_step{ + n: 1, + from: status.from, + transit: status.transit, + to: status.to, + }) + stack.Push(&_step{ + n: status.n - 1, + from: status.from, + transit: status.to, + to: status.transit, + }) + } + } +} diff --git a/cap3/ex6.go b/cap3/ex6.go new file mode 100644 index 0000000..46c6ec1 --- /dev/null +++ b/cap3/ex6.go @@ -0,0 +1,103 @@ +package cap3 + +// Ex6Output 例六的带输出结果 +func Ex6Output(num int, callback func(nums []int)) { + Ex6RecursionOutput(num, callback) +} + +// Ex6NoneOutput 例六的不带输出结果版本,只求数量 +func Ex6NoneOutput(num int, algoType AlgoType, callback func(nums []int)) int { + switch algoType { + case AlgoTypeRecursion: + return Ex6Recursion(num) + default: + return Ex6NoneRecursion(num) + } +} + +// Ex6Recursion 整数划分,递归写法,只计算结果,但对算法经过调整以适合输出 +func Ex6Recursion(num int) int { + count := 0 + var divider func(num, m int) + divider = func(num, m int) { + // num == 0 来源会有两种:递归中num==m,以及传入的num本身就是0,此时可以作为一个划分 + if num == 0 { + count++ + return + } + + // 最大划分大小m逐级-1求划分数 + if m > 1 { + divider(num, m-1) + } + + // m <= num,对剩余未加的数进行划分 + if m <= num { + divider(num-m, m) + } + } + + divider(num, num) + return count +} + +// Ex6RecursionOutput 整数划分,由上面的递归写法修改而来,可求划分情况,对算法经过调整以适合输出 +func Ex6RecursionOutput(num int, callback func(dividedNums []int)) { + var divider func(num, m int, dividedNums []int, callback func(dividedNums []int)) + divider = func(num, m int, dividedNums []int, callback func(dividedNums []int)) { + // num==0时,当前已划分完毕,回调输出 + if num == 0 { + callback(dividedNums) + return + } + + // 最大划分大小m逐级-1求划分数 + if m > 1 { + divider(num, m-1, dividedNums, callback) + } + + if m <= num { + // m <= num,对剩余未加的数进行划分,当前的m已经是划分中的一个成员,将其添加进dividedNums中 + divider(num-m, m, append(dividedNums, m), callback) + } + } + + divider(num, num, make([]int, 0, num), callback) +} + +// Ex6NoneRecursion 整数划分,非递归写法,由递归法改写而来,模拟递归过程 +func Ex6NoneRecursion(num int) int { + divider := func(num, divideMax int) int { + // 初始化 + result := make([][]int, num+1) + for i := 0; i < num+1; i++ { + result[i] = make([]int, num+1) + } + for i := 1; i <= num; i++ { + result[0][i] = 1 + } + + // i从1开始到num进行划分计算, + // 里层j从1开始到m(最大划分大小)开始计算子问题的划分 + // 从1开始,自底向上计算 + // 此处为非递归写法,当前的结果依赖上一个结果,因此需要先计算上一个结果, + // 因此整个问题过程需要从最小的问题开始计算 + divideMax = min(num, divideMax) + for n := 1; n <= num; n++ { + for m := 1; m <= divideMax; m++ { + if n <= m { + // 对应 Q(n,n) = 1 + Q(n, n-1) 的情况 + // n < m (n < m) 时均看作i == m (n == m) + result[n][m] = 1 + result[n][n-1] + } else { + // 对应 Q(n,m) = Q(n, m-1) + Q(n-m, m) 的情况 + result[n][m] = result[n][m-1] + result[n-m][m] + } + } + } + + return result[num][divideMax] + } + + return divider(num, num) +} diff --git a/cap3/ex7.go b/cap3/ex7.go new file mode 100644 index 0000000..8af8c22 --- /dev/null +++ b/cap3/ex7.go @@ -0,0 +1,26 @@ +package cap3 + +// Ex7 低位到高位输出正整数的数字 +func Ex7(num uint64, algoType AlgoType, callback func(digit uint8)) { + switch algoType { + case AlgoTypeRecursion: + Ex7Recursion(num, callback) + default: + Ex7NoneRecursion(num, callback) + } +} + +func Ex7Recursion(num uint64, callback func(digit uint8)) { + if num < 10 { + callback(uint8(num)) + } else { + callback(uint8(num % 10)) + Ex7Recursion(num/10, callback) + } +} + +func Ex7NoneRecursion(num uint64, callback func(digit uint8)) { + for i := num; i >= 1; i /= 10 { + callback(uint8(i % 10)) + } +} diff --git a/cap3/ex8.go b/cap3/ex8.go new file mode 100644 index 0000000..d1fae3b --- /dev/null +++ b/cap3/ex8.go @@ -0,0 +1,34 @@ +package cap3 + +// Ex8 高位到低位输出正整数的数字 +func Ex8(num uint64, algoType AlgoType, callback func(digit uint8)) { + switch algoType { + case AlgoTypeRecursion: + Ex8Recursion(num, callback) + default: + Ex8NoneRecursion(num, callback) + } +} + +// Ex8Recursion 高位到低位输出正整数的数字(递归) +func Ex8Recursion(num uint64, callback func(digit uint8)) { + if num < 10 { + callback(uint8(num)) + } else { + // 跟ex7的小区别 + Ex8Recursion(num/10, callback) + callback(uint8(num % 10)) + } +} + +// Ex8NoneRecursion 高位到低位输出正整数的数字(非递归) +func Ex8NoneRecursion(num uint64, callback func(digit uint8)) { + digits := make([]uint8, 0, 20) + for i := num; i >= 1; i /= 10 { + digits = append(digits, uint8(i%10)) + } + + for i := len(digits) - 1; i >= 0; i-- { + callback(digits[i]) + } +} diff --git a/cap3/ex9.go b/cap3/ex9.go new file mode 100644 index 0000000..e1aa4f5 --- /dev/null +++ b/cap3/ex9.go @@ -0,0 +1,28 @@ +package cap3 + +// Ex9t1 整数拆解为2的幂之和(返回整数的二进制bit取值情况,bit的idx对应相应的幂) +func Ex9t1(num int) []bool { + // 传进来的是int,一般是32bit,这里就用64来存bit,肯定够了 + bits := make([]bool, 0, 64) + for i := 0; num != 0; i++ { + bits = append(bits, num%2 == 1) + num /= 2 + } + + return bits +} + +// Ex9t2 整数拆解为2的幂之和(返回幂取值) +func Ex9t2(num int) []uint8 { + // 传进来的是int,一般是32bit,这里就用64来存bit,肯定够了 + bits := make([]uint8, 0, 64) + for i := uint8(0); num != 0; i++ { + if num%2 == 1 { + bits = append(bits, i) + } + + num /= 2 + } + + return bits +} diff --git a/cap3/example.go b/cap3/example.go new file mode 100644 index 0000000..ef562b7 --- /dev/null +++ b/cap3/example.go @@ -0,0 +1,26 @@ +package cap3 + +import "strings" + +type AlgoType int + +const ( + AlgoTypeRecursion AlgoType = iota + AlgoTypeNoneRecursion +) + +func DigitSlice2String(nums []uint8) string { + zeroPrefix := true + sb := strings.Builder{} + for _, num := range nums { + if zeroPrefix && num == 0 { + continue + } else { + zeroPrefix = false + } + + sb.WriteByte(num + 48) + } + + return sb.String() +} diff --git a/cap3/example_test.go b/cap3/example_test.go new file mode 100644 index 0000000..e07f382 --- /dev/null +++ b/cap3/example_test.go @@ -0,0 +1,217 @@ +package cap3 + +import ( + "fmt" + "strconv" + "testing" +) + +func TestEx1(t *testing.T) { + fmt.Println(Ex1(1)) + fmt.Println(Ex1(2)) + fmt.Println(Ex1(3)) + fmt.Println(Ex1(4)) + fmt.Println(Ex1(5)) + fmt.Println(Ex1(6)) + fmt.Println(Ex1(7)) + fmt.Println(Ex1(8)) + fmt.Println(Ex1(9)) +} + +func TestEx2(t *testing.T) { + result := Ex2() + for num, factors := range result { + fmt.Printf("%d's factors are %+v\n", num, factors) + } +} + +func TestEx3(t *testing.T) { + matrix := [][]int{ + {1, 4, 5, 1}, + {8, 9, 6, 7}, + {1, 1, 4, 2}, + {0, 8, 2, 9}, + } + + row, col, val := Ex3(matrix) + if row != -1 && col != -1 { + fmt.Printf("(%d,%d): %d\n", row, col, val) + } +} + +func TestEx5(t *testing.T) { + beadsNum := 5 + step := 0 + + fmt.Println("NoneRecursive: ") + Ex5(beadsNum, AlgoTypeNoneRecursion, func(from string, to string) { + step++ + fmt.Printf("step %d: %s -> %s\n", step, from, to) + }) + + fmt.Println("----------------") + + step = 0 + fmt.Println("Recursive: ") + Ex5(beadsNum, AlgoTypeRecursion, func(from string, to string) { + step++ + fmt.Printf("step %d: %s -> %s\n", step, from, to) + }) +} + +func TestEx6(t *testing.T) { + prev := 0 + Ex6RecursionOutput(6, func(dividedNums []int) { + for i, num := range dividedNums { + if i == 0 { + if prev != num { + prev = num + fmt.Println() + } + fmt.Printf("%d", num) + } else { + fmt.Printf("+%d", num) + } + } + + fmt.Print("\t\t\t") + }) + + fmt.Println() + fmt.Println("------------") + + result := Ex6Recursion(6) + fmt.Println(result) + + result = Ex6NoneRecursion(6) + fmt.Println(result) +} + +func TestEx7(t *testing.T) { + num := uint64(123456) + Ex7(num, AlgoTypeNoneRecursion, func(digit uint8) { + fmt.Printf("%d ", digit) + }) + fmt.Println() + + Ex7(num, AlgoTypeRecursion, func(digit uint8) { + fmt.Printf("%d ", digit) + }) + fmt.Println() +} + +func TestEx8(t *testing.T) { + num := uint64(123456) + Ex8(num, AlgoTypeNoneRecursion, func(digit uint8) { + fmt.Printf("%d ", digit) + }) + fmt.Println() + + Ex8(num, AlgoTypeRecursion, func(digit uint8) { + fmt.Printf("%d ", digit) + }) + fmt.Println() +} + +func TestEx9(t *testing.T) { + bits := Ex9t1(137) + fmt.Printf("%d=", 137) + for i, bit := range bits { + if bit { + fmt.Printf("2^%d + ", i) + } + } + + println() + + pows := Ex9t2(137) + fmt.Printf("%d=", 137) + for _, pow := range pows { + fmt.Printf("2^%d + ", pow) + } + + println() +} + +func TestEx10(t *testing.T) { + Ex10(5, 3, func(nums []int) { + fmt.Printf("%+v\n", nums) + }) +} + +func TestEx14(t *testing.T) { + num := uint64(8734112) + Ex14StrInput(strconv.FormatUint(num, 10), func(str string) { + fmt.Printf("%s-", str) + }) + fmt.Println() + + Ex14NumInput(num, func(str string) { + fmt.Printf("%s-", str) + }) + fmt.Println() +} + +func TestEx15(t *testing.T) { + result := Ex15(60, 70) + for cash, cashNum := range result { + fmt.Printf("%d->%d, ", cashes[cash], cashNum) + } + fmt.Println() + + result = Ex15(60, 100) + for cash, cashNum := range result { + fmt.Printf("%d->%d, ", cashes[cash], cashNum) + } + fmt.Println() + + result = Ex15(60, 74) + for cash, cashNum := range result { + fmt.Printf("%d->%d, ", cashes[cash], cashNum) + } + fmt.Println() + + result = Ex15(60, 130) + for cash, cashNum := range result { + fmt.Printf("%d->%d, ", cashes[cash], cashNum) + } + + fmt.Println() +} + +func TestEx16(t *testing.T) { + count := 0 + Ex16(func(x, x2 int) { + count++ + fmt.Printf("%d: %d, x^2=%d\n", count, x, x2) + }) +} + +func TestEx17(t *testing.T) { + n, k := 10, 2 + result := Ex17Normal(n, k) + fmt.Println(result) + + result = Ex17Math(n, k) + fmt.Println(result) +} + +func TestEx18(t *testing.T) { + fmt.Println(Ex18("6451", "6637") == "42815287") + fmt.Println(Ex18("1111", "2") == "2222") + + // 来个夸张点的 + ans := "59431035264803873745814101793588195732295068254603339623610036212240642359886400430165726959529476600423448130231213495885200101414878403047792277609642" + fmt.Println(Ex18( + "953249582974085793083245237450927435989430572386540298743509843545728475284751234", + "62345723854798175908734905872984724974984398572942345324535728479275413") == ans, + ) + + result := Ex18type3([]uint8{6, 4, 5, 1}, 6637) + fmt.Println(DigitSlice2String(result) == "42815287") +} + +func TestEx19(t *testing.T) { + fmt.Println(Ex19(10)) + fmt.Println(Ex19(100)) +} diff --git a/cap3/work.go b/cap3/work.go new file mode 100644 index 0000000..17e9fed --- /dev/null +++ b/cap3/work.go @@ -0,0 +1 @@ +package cap3 diff --git a/cap3/work1.go b/cap3/work1.go new file mode 100644 index 0000000..bfbf9af --- /dev/null +++ b/cap3/work1.go @@ -0,0 +1,27 @@ +package cap3 + +// Work1 求 2 + 22 + 222 + 2222 + ...22...22(n个2)的精确值,返回大端序结果,高位在前存放结果 +// 按位直接计算结果,从最低位(结果索引的末尾)开始, +// 每一位(索引i)的计算结果为2*(i+1)加上进位,该位的数字即为结果%10,进位即为结果/10取整。 +// 如n=10,则最低位(i=9)的计算结果为(9+1)*2 + 0 = 20,该位数字为result[i]=20%10=0,进位carry=20/10=2, +// 下一位(i=8)计算结果为(8+1)*2 + 2 = 20,该位数字为result[i]=20%10=0,进位carry=20/10=2, +// 下一位(i=7)计算结果为(7+1)*2 + 2 = 18,该位数字为result[i]=18%10=8,进位carry=18/10=1, +// 以此类推 +func Work1(n int) []uint8 { + // 大端序,高位在前存放结果 + result := make([]uint8, n) + carry := 0 + for i := n - 1; i >= 0; i-- { + num := (i+1)*2 + carry + carry = num / 10 + result[i] = uint8(num % 10) + } + + if carry != 0 { + result = append(result, 0) + copy(result[1:], result[0:]) + result[0] = uint8(carry) + } + + return result +} diff --git a/cap3/work2.go b/cap3/work2.go new file mode 100644 index 0000000..e54fa8f --- /dev/null +++ b/cap3/work2.go @@ -0,0 +1,41 @@ +package cap3 + +// Work2 返回给定数组的偏移布局矩阵 +func Work2(nums []int) [][]int { + n := len(nums) + result := make([][]int, n) + for i := 0; i < n; i++ { + result[i] = make([]int, n) + } + + colStart := 0 + for _, num := range nums { + for row, col := 0, colStart; row < n; row++ { + result[row][col%n] = num + col++ + } + + colStart++ + } + + return result +} + +// Work2type2 返回给定数组的偏移布局矩阵,另一种写法 +func Work2type2(nums []int) [][]int { + n := len(nums) + result := make([][]int, n) + for i := 0; i < n; i++ { + result[i] = make([]int, n) + } + + offset := 0 + for row := 0; row < n; row++ { + for col := 0; col < n; col++ { + result[row][(offset+col)%n] = nums[col] + } + offset++ + } + + return result +} diff --git a/cap3/work3.go b/cap3/work3.go new file mode 100644 index 0000000..ea82c33 --- /dev/null +++ b/cap3/work3.go @@ -0,0 +1,44 @@ +package cap3 + +// Work3 从外到里填充矩阵 +func Work3(n int) [][]int { + matrix := make([][]int, n) + for i := range matrix { + matrix[i] = make([]int, n) + } + + // 逐层往里填充 + num := 1 + for layer := 0; layer < n/2; layer++ { + // 方阵上边 + for i := layer; i < n-layer; i++ { + matrix[layer][i] = num + num++ + } + + // 方阵右边 + for i := layer + 1; i < n-layer; i++ { + matrix[i][n-layer-1] = num + num++ + } + + // 方阵下边 + for i := n - layer - 2; i >= layer; i-- { + matrix[n-layer-1][i] = num + num++ + } + + // 方阵左边 + for i := n - layer - 2; i > layer; i-- { + matrix[i][layer] = num + num++ + } + } + + // 奇数阶中间位置需要手动设置 + if n%2 != 0 { + matrix[n/2][n/2] = n * n + } + + return matrix +} diff --git a/cap3/work4.go b/cap3/work4.go new file mode 100644 index 0000000..7be59bd --- /dev/null +++ b/cap3/work4.go @@ -0,0 +1,20 @@ +package cap3 + +func Work4(n int) [][]int { + matrix := make([][]int, n) + // 逐行长度递减的上三角矩阵 + for i := range matrix { + matrix[i] = make([]int, n-i) + } + + // 沿右上方向斜向上填充 + num := 1 + for i := 0; i < n; i++ { + for j := 0; j <= i; j++ { + matrix[i-j][j] = num + num++ + } + } + + return matrix +} diff --git a/cap3/work5.go b/cap3/work5.go new file mode 100644 index 0000000..af7955c --- /dev/null +++ b/cap3/work5.go @@ -0,0 +1,38 @@ +package cap3 + +func Work5(n int) [][]int { + matrix := make([][]int, n) + for i := range matrix { + matrix[i] = make([]int, n) + } + + // 逐层往里填充 + for layer := 0; layer < n/2; layer++ { + // 方阵上边 + for i := layer; i < n-layer; i++ { + matrix[layer][i] = layer + 1 + } + + // 方阵右边 + for i := layer + 1; i < n-layer; i++ { + matrix[i][n-layer-1] = layer + 1 + } + + // 方阵下边 + for i := n - layer - 2; i >= layer; i-- { + matrix[n-layer-1][i] = layer + 1 + } + + // 方阵左边 + for i := n - layer - 2; i > layer; i-- { + matrix[i][layer] = layer + 1 + } + } + + // 奇数阶中间位置需要手动设置 + if n%2 != 0 { + matrix[n/2][n/2] = n/2 + 1 + } + + return matrix +} diff --git a/cap3/work_test.go b/cap3/work_test.go new file mode 100644 index 0000000..da45d14 --- /dev/null +++ b/cap3/work_test.go @@ -0,0 +1,64 @@ +package cap3 + +import ( + "fmt" + "testing" +) + +func TestWork1(t *testing.T) { + result := Work1(100) + for _, b := range result { + fmt.Print(b) + } + + fmt.Println() +} + +func _printMatrix(matrix [][]int) { + for _, row := range matrix { + for _, element := range row { + fmt.Printf("%d\t", element) + } + fmt.Println() + } +} + +func TestWork2(t *testing.T) { + matrix := Work2([]int{5, 7, 4, 8, 9, 1}) + _printMatrix(matrix) + + println("----------------") + + matrix = Work2type2([]int{5, 7, 4, 8, 9, 1}) + _printMatrix(matrix) +} + +func TestWork3(t *testing.T) { + matrix := Work3(7) + _printMatrix(matrix) + + fmt.Println("----------------") + + matrix = Work3(6) + _printMatrix(matrix) +} + +func TestWork4(t *testing.T) { + matrix := Work4(5) + _printMatrix(matrix) +} + +func TestWork5(t *testing.T) { + matrix := Work5(6) + _printMatrix(matrix) + + fmt.Println("----------------") + + matrix = Work5(5) + _printMatrix(matrix) + + fmt.Println("----------------") + + matrix = Work5(10) + _printMatrix(matrix) +} diff --git a/cap4/example.go b/cap4/example.go new file mode 100644 index 0000000..dea1aa4 --- /dev/null +++ b/cap4/example.go @@ -0,0 +1 @@ +package cap4 diff --git a/cap4/work.go b/cap4/work.go new file mode 100644 index 0000000..dea1aa4 --- /dev/null +++ b/cap4/work.go @@ -0,0 +1 @@ +package cap4 diff --git a/cap5/example.go b/cap5/example.go new file mode 100644 index 0000000..813a9f9 --- /dev/null +++ b/cap5/example.go @@ -0,0 +1 @@ +package cap5 diff --git a/cap5/work.go b/cap5/work.go new file mode 100644 index 0000000..813a9f9 --- /dev/null +++ b/cap5/work.go @@ -0,0 +1 @@ +package cap5 diff --git a/common/stack.go b/common/stack.go new file mode 100644 index 0000000..25af4f8 --- /dev/null +++ b/common/stack.go @@ -0,0 +1,55 @@ +package common + +type Stack[T any] struct { + data []*T +} + +func NewStack[T any](size int) *Stack[T] { + stack := Stack[T]{} + if size != 0 { + stack.data = make([]*T, 0, size) + } else { + stack.data = make([]*T, 0, 16) + } + + return &stack +} + +func (s *Stack[T]) Push(element *T) *Stack[T] { + s.data = append(s.data, element) + return s +} + +func (s *Stack[T]) Pop() *T { + if s.Size() == 0 { + return nil + } + + element := s.Top() + s.data = s.data[:len(s.data)-1] + return element +} + +func (s *Stack[T]) Top() *T { + if s.Size() == 0 { + return nil + } + + return s.data[len(s.data)-1] +} + +func (s *Stack[T]) Bottom() *T { + if s.Size() == 0 { + return nil + } + + return s.data[0] +} + +func (s *Stack[T]) Size() int { + return len(s.data) +} + +func (s *Stack[T]) Empty() bool { + return s.Size() == 0 +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c696a2b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module lavos + +go 1.22 diff --git a/main.go b/main.go new file mode 100644 index 0000000..7905807 --- /dev/null +++ b/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +}