From d4ad75eb7d07b4d8d62cccc3de78d9b474d05eca Mon Sep 17 00:00:00 2001 From: machangxin Date: Tue, 13 Dec 2022 09:24:59 +0800 Subject: [PATCH 1/4] time complexity using go --- .../time_complexity/time_complexity.go | 127 +++++++++++ .../time_complexity/time_complexity_test.go | 44 ++++ .../time_complexity.md | 213 ++++++++++++++++-- 3 files changed, 368 insertions(+), 16 deletions(-) create mode 100644 codes/go/chapter_computational_complexity/time_complexity/time_complexity.go create mode 100644 codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go diff --git a/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go b/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go new file mode 100644 index 00000000..4e8432ac --- /dev/null +++ b/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go @@ -0,0 +1,127 @@ +package time_complexity + +/* 常数阶 */ +func constant(n int) int { + count := 0 + size := 100000 + for i := 0; i < size; i++ { + count++ + } + return count +} + +/* 线性阶 */ +func linear(n int) int { + count := 0 + for i := 0; i < n; i++ { + count++ + } + return count +} + +/* 线性阶(遍历数组) */ +func arrayTraversal(nums []int) int { + count := 0 + // 循环次数与数组长度成正比 + for range nums { + count++ + } + return count +} + +/* 平方阶 */ +func quadratic(n int) int { + count := 0 + // 循环次数与数组长度成平方关系 + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + count++ + } + } + return count +} + +/* 平方阶(冒泡排序) */ +func bubbleSort(nums []int) int { + count := 0 // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i := len(nums) - 1; i > 0; i-- { + // 内循环:冒泡操作 + for j := 0; j < i; j++ { + if nums[j] > nums[j+1] { + // 交换 nums[j] 与 nums[j + 1] + tmp := nums[j] + nums[j] = nums[j+1] + nums[j+1] = tmp + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count +} + +/* 指数阶(循环实现)*/ +func exponential(n int) int { + count, base := 0, 1 + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for i := 0; i < n; i++ { + for j := 0; j < base; j++ { + count++ + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count +} + +/* 指数阶(递归实现)*/ +func expRecur(n int) int { + if n == 1 { + return 1 + } + return expRecur(n-1) + expRecur(n-1) + 1 +} + +/* 对数阶(循环实现)*/ +func logarithmic(n float64) int { + count := 0 + for n > 1 { + n = n / 2 + count++ + } + return count +} + +/* 对数阶(递归实现)*/ +func logRecur(n float64) int { + if n <= 1 { + return 0 + } + return logRecur(n/2) + 1 +} + +/* 线性对数阶 */ +func linearLogRecur(n float64) int { + if n <= 1 { + return 1 + } + count := linearLogRecur(n/2) + + linearLogRecur(n/2) + for i := 0.0; i < n; i++ { + count++ + } + return count +} + +/* 阶乘阶(递归实现) */ +func factorialRecur(n int) int { + if n == 0 { + return 1 + } + count := 0 + // 从 1 个分裂出 n 个 + for i := 0; i < n; i++ { + count += factorialRecur(n - 1) + } + return count +} diff --git a/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go b/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go new file mode 100644 index 00000000..3c9527ef --- /dev/null +++ b/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go @@ -0,0 +1,44 @@ +package time_complexity + +import ( + "fmt" + "testing" +) + +func TestTimeComplexity(t *testing.T) { + n := 8 + fmt.Println("输入数据大小 n =", n) + + count := constant(n) + fmt.Println("常数阶的计算操作数量 =", count) + + count = linear(n) + fmt.Println("线性阶的计算操作数量 =", count) + count = arrayTraversal(make([]int, n)) + fmt.Println("线性阶(遍历数组)的计算操作数量 =", count) + + count = quadratic(n) + fmt.Println("平方阶的计算操作数量 =", count) + nums := make([]int, n) + for i := 0; i < n; i++ { + nums[i] = n - i + } + count = bubbleSort(nums) + fmt.Println("平方阶(冒泡排序)的计算操作数量 =", count) + + count = exponential(n) + fmt.Println("指数阶(循环实现)的计算操作数量 =", count) + count = expRecur(n) + fmt.Println("指数阶(递归实现)的计算操作数量 =", count) + + count = logarithmic(float64(n)) + fmt.Println("对数阶(循环实现)的计算操作数量 =", count) + count = logRecur(float64(n)) + fmt.Println("对数阶(递归实现)的计算操作数量 =", count) + + count = linearLogRecur(float64(n)) + fmt.Println("线性对数阶(递归实现)的计算操作数量 =", count) + + count = factorialRecur(n) + fmt.Println("阶乘阶(递归实现)的计算操作数量 =", count) +} diff --git a/docs/chapter_computational_complexity/time_complexity.md b/docs/chapter_computational_complexity/time_complexity.md index ab55268c..1d374e26 100644 --- a/docs/chapter_computational_complexity/time_complexity.md +++ b/docs/chapter_computational_complexity/time_complexity.md @@ -64,7 +64,16 @@ $$ === "Go" ```go title="" - + // 在某运行平台下 + func algorithm(n int) { + a := 2 // 1 ns + a = a + 1 // 1 ns + a = a * 2 // 10 ns + // 循环 n 次 + for i := 0; i < n; i++ { // 1 ns + fmt.Println(a) // 5 ns + } + } ``` === "JavaScript" @@ -164,7 +173,22 @@ $$ === "Go" ```go title="" - + // 算法 A 时间复杂度:常数阶 + func algorithm_A(n int) { + fmt.Println(0) + } + // 算法 B 时间复杂度:线性阶 + func algorithm_B(n int) { + for i := 0; i < n; i++ { + fmt.Println(0) + } + } + // 算法 C 时间复杂度:常数阶 + func algorithm_C(n int) { + for i := 0; i < 1000000; i++ { + fmt.Println(0) + } + } ``` === "JavaScript" @@ -249,13 +273,20 @@ $$ # 循环 n 次 for i in range(n): # +1 print(0) # +1 - } ``` === "Go" ```go title="" - + func algorithm(n int) { + a := 1 // +1 + a = a + 1 // +1 + a = a * 2 // +1 + // 循环 n 次 + for i := 0; i < n; i++ { // +1 + fmt.Println(a) // +1 + } + } ``` === "JavaScript" @@ -389,7 +420,20 @@ $$ === "Go" ```go title="" - + func algorithm(n int) { + a := 1 // +0(技巧 1) + a = a + n // +0(技巧 1) + // +n(技巧 2) + for i := 0; i < 5 * n + 1; i++ { + fmt.Println(0) + } + // +n*n(技巧 3) + for i := 0; i < 2 * n; i++ { + for j := 0; j < n + 1; j++ { + fmt.Println(0) + } + } + } ``` === "JavaScript" @@ -500,7 +544,15 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 常数阶 */ + func constant(n int) int { + count := 0 + size := 100000 + for i := 0; i < size; i++ { + count ++ + } + return count + } ``` === "JavaScript" @@ -569,7 +621,14 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 线性阶 */ + func linear(n int) int { + count := 0 + for i := 0; i < n; i++ { + count++ + } + return count + } ``` === "JavaScript" @@ -645,7 +704,15 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 线性阶(遍历数组) */ + func arrayTraversal(nums []int) int { + count := 0 + // 循环次数与数组长度成正比 + for range nums { + count++ + } + return count + } ``` === "JavaScript" @@ -724,7 +791,17 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 平方阶 */ + func quadratic(n int) int { + count := 0 + // 循环次数与数组长度成平方关系 + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + count++ + } + } + return count + } ``` === "JavaScript" @@ -829,7 +906,24 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 平方阶(冒泡排序) */ + func bubbleSort(nums []int) int { + count := 0 // 计数器 + // 外循环:待排序元素数量为 n-1, n-2, ..., 1 + for i := len(nums) - 1; i > 0; i-- { + // 内循环:冒泡操作 + for j := 0; j < i; j++ { + if nums[j] > nums[j+1] { + // 交换 nums[j] 与 nums[j + 1] + tmp := nums[j] + nums[j] = nums[j+1] + nums[j+1] = tmp + count += 3 // 元素交换包含 3 个单元操作 + } + } + } + return count + } ``` === "JavaScript" @@ -918,7 +1012,19 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 指数阶(循环实现)*/ + func exponential(n int) int { + count, base := 0, 1 + // cell 每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1) + for i := 0; i < n; i++ { + for j := 0; j < base; j++ { + count++ + } + base *= 2 + } + // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 + return count + } ``` === "JavaScript" @@ -983,7 +1089,13 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 指数阶(递归实现)*/ + func expRecur(n int) int { + if n == 1 { + return 1 + } + return expRecur(n-1) + expRecur(n-1) + 1 + } ``` === "JavaScript" @@ -1061,7 +1173,15 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 对数阶(循环实现)*/ + func logarithmic(n float64) int { + count := 0 + for n > 1 { + n = n / 2 + count++ + } + return count + } ``` === "JavaScript" @@ -1126,7 +1246,13 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 对数阶(递归实现)*/ + func logRecur(n float64) int { + if n <= 1 { + return 0 + } + return logRecur(n/2) + 1 + } ``` === "JavaScript" @@ -1205,7 +1331,18 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 线性对数阶 */ + func linearLogRecur(n float64) int { + if n <= 1 { + return 1 + } + count := linearLogRecur(n/2) + + linearLogRecur(n/2) + for i := 0.0; i < n; i++ { + count++ + } + return count + } ``` === "JavaScript" @@ -1292,7 +1429,18 @@ $$ === "Go" ```go title="time_complexity_types.go" - + /* 阶乘阶(递归实现) */ + func factorialRecur(n int) int { + if n == 0 { + return 1 + } + count := 0 + // 从 1 个分裂出 n 个 + for i := 0; i < n; i++ { + count += factorialRecur(n - 1) + } + return count + } ``` === "JavaScript" @@ -1447,7 +1595,40 @@ $$ === "Go" ```go title="worst_best_time_complexity.go" + /* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ + func randomNumbers(n int) []int { + nums := make([]int, n) + // 生成数组 nums = { 1, 2, 3, ..., n } + for i := 0; i < n; i++ { + nums[i] = i + 1 + } + // 随机打乱数组元素 + rand.Shuffle(len(nums), func(i, j int) { + nums[i], nums[j] = nums[j], nums[i] + }) + return nums + } + /* 查找数组 nums 中数字 1 所在索引 */ + func findOne(nums []int) int { + for i := 0; i < len(nums); i++ { + if nums[i] == 1 { + return i + } + } + return -1 + } + + /* Driver Code */ + func main() { + for i := 0; i < 10; i++ { + n := 100 + nums := randomNumbers(n) + index := findOne(nums) + fmt.Println("\n数组 [ 1, 2, ..., n ] 被打乱后 =", nums) + fmt.Println("数字 1 的索引为", index) + } + } ``` === "JavaScript" From 7dc1dd54d47006e6e182588359ac78f63c6ec13a Mon Sep 17 00:00:00 2001 From: machangxin Date: Tue, 13 Dec 2022 09:53:17 +0800 Subject: [PATCH 2/4] Add annotation --- .../time_complexity/time_complexity.go | 4 ++++ .../time_complexity/time_complexity_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go b/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go index 4e8432ac..0ad885e7 100644 --- a/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go +++ b/codes/go/chapter_computational_complexity/time_complexity/time_complexity.go @@ -1,3 +1,7 @@ +// File: quick_sort_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + package time_complexity /* 常数阶 */ diff --git a/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go b/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go index 3c9527ef..1958329a 100644 --- a/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go +++ b/codes/go/chapter_computational_complexity/time_complexity/time_complexity_test.go @@ -1,3 +1,7 @@ +// File: quick_sort_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + package time_complexity import ( From 4d3128a4a7ee97b2a8a13128c82add47baec60e2 Mon Sep 17 00:00:00 2001 From: machangxin Date: Tue, 13 Dec 2022 14:24:17 +0800 Subject: [PATCH 3/4] merge sort using go --- .../chapter_sorting/merge_sort/merge_sort.go | 53 +++++++++++++++++++ .../merge_sort/merge_sort_test.go | 15 ++++++ docs/chapter_sorting/merge_sort.md | 49 ++++++++++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 codes/go/chapter_sorting/merge_sort/merge_sort.go create mode 100644 codes/go/chapter_sorting/merge_sort/merge_sort_test.go diff --git a/codes/go/chapter_sorting/merge_sort/merge_sort.go b/codes/go/chapter_sorting/merge_sort/merge_sort.go new file mode 100644 index 00000000..b6650a76 --- /dev/null +++ b/codes/go/chapter_sorting/merge_sort/merge_sort.go @@ -0,0 +1,53 @@ +package merge_sort +// File: merge_sort.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +/* + 合并左子数组和右子数组 + 左子数组区间 [left, mid] + 右子数组区间 [mid + 1, right] +*/ +func merge(nums []int, left, mid, right int) { + // 初始化辅助数组 借助 copy模块 + tmp := make([]int, right-left+1) + for i := left; i <= right; i++ { + tmp[i-left] = nums[i] + } + // 左子数组的起始索引和结束索引 + left_start, left_end := left-left, mid-left + // 右子数组的起始索引和结束索引 + right_start, right_end := mid+1-left, right-left + // i, j 分别指向左子数组、右子数组的首元素 + i, j := left_start, right_start + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k := left; k <= right; k++ { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > left_end { + nums[k] = tmp[j] + j++ + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + } else if j > right_end || tmp[i] <= tmp[j] { + nums[k] = tmp[i] + i++ + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j] + j++ + } + } +} + + +func mergeSort(nums []int, left, right int) { + // 终止条件 + if left >= right { + return + } + // 划分阶段 + mid := (left + right) / 2 + mergeSort(nums, left, mid) + mergeSort(nums, mid+1, right) + // 合并阶段 + merge(nums, left, mid, right) +} \ No newline at end of file diff --git a/codes/go/chapter_sorting/merge_sort/merge_sort_test.go b/codes/go/chapter_sorting/merge_sort/merge_sort_test.go new file mode 100644 index 00000000..04abd443 --- /dev/null +++ b/codes/go/chapter_sorting/merge_sort/merge_sort_test.go @@ -0,0 +1,15 @@ +package merge_sort +// File: merge_sort_test.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +import ( + "fmt" + "testing" +) + +func TestMergeSort(t *testing.T) { + nums := []int{7, 3, 2, 6, 0, 1, 5, 4} + mergeSort(nums, 0, len(nums)-1) + fmt.Println("归并排序完成后 nums = ", nums) +} diff --git a/docs/chapter_sorting/merge_sort.md b/docs/chapter_sorting/merge_sort.md index 2d67dfd8..b7486bae 100644 --- a/docs/chapter_sorting/merge_sort.md +++ b/docs/chapter_sorting/merge_sort.md @@ -195,7 +195,54 @@ comments: true === "Go" ```go title="merge_sort.go" - + /* + 合并左子数组和右子数组 + 左子数组区间 [left, mid] + 右子数组区间 [mid + 1, right] + */ + func merge(nums []int, left, mid, right int) { + // 初始化辅助数组 借助 copy模块 + tmp := make([]int, right-left+1) + for i := left; i <= right; i++ { + tmp[i-left] = nums[i] + } + // 左子数组的起始索引和结束索引 + left_start, left_end := left-left, mid-left + // 右子数组的起始索引和结束索引 + right_start, right_end := mid+1-left, right-left + // i, j 分别指向左子数组、右子数组的首元素 + i, j := left_start, right_start + // 通过覆盖原数组 nums 来合并左子数组和右子数组 + for k := left; k <= right; k++ { + // 若 “左子数组已全部合并完”,则选取右子数组元素,并且 j++ + if i > left_end { + nums[k] = tmp[j] + j++ + // 否则,若 “右子数组已全部合并完” 或 “左子数组元素 < 右子数组元素”,则选取左子数组元素,并且 i++ + } else if j > right_end || tmp[i] <= tmp[j] { + nums[k] = tmp[i] + i++ + // 否则,若 “左子数组元素 > 右子数组元素”,则选取右子数组元素,并且 j++ + } else { + nums[k] = tmp[j] + j++ + } + } + } + + /* 归并排序 */ + func mergeSort(nums []int, left, right int) { + // 终止条件 + if left >= right { + return + } + // 划分阶段 + mid := (left + right) / 2 + mergeSort(nums, left, mid) + mergeSort(nums, mid+1, right) + // 合并阶段 + merge(nums, left, mid, right) + } ``` === "JavaScript" From 1ec97120a92543d8183e11908d9d24e69a3546c4 Mon Sep 17 00:00:00 2001 From: machangxin Date: Tue, 13 Dec 2022 17:53:23 +0800 Subject: [PATCH 4/4] add worst_best_time_complexity.go --- .../worst_best_time_complexity.go | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 codes/go/chapter_computational_complexity/time_complexity/worst_best_time_complexity.go diff --git a/codes/go/chapter_computational_complexity/time_complexity/worst_best_time_complexity.go b/codes/go/chapter_computational_complexity/time_complexity/worst_best_time_complexity.go new file mode 100644 index 00000000..bc3e7901 --- /dev/null +++ b/codes/go/chapter_computational_complexity/time_complexity/worst_best_time_complexity.go @@ -0,0 +1,45 @@ +// File: worst_best_time_complexity.go +// Created Time: 2022-12-13 +// Author: msk397 (machangxinq@gmail.com) + +package time_complexity + +import ( + "fmt" + "math/rand" +) + +/* 生成一个数组,元素为 { 1, 2, ..., n },顺序被打乱 */ +func randomNumbers(n int) []int { + nums := make([]int, n) + // 生成数组 nums = { 1, 2, 3, ..., n } + for i := 0; i < n; i++ { + nums[i] = i + 1 + } + // 随机打乱数组元素 + rand.Shuffle(len(nums), func(i, j int) { + nums[i], nums[j] = nums[j], nums[i] + }) + return nums +} + +/* 查找数组 nums 中数字 1 所在索引 */ +func findOne(nums []int) int { + for i := 0; i < len(nums); i++ { + if nums[i] == 1 { + return i + } + } + return -1 +} + +/* Driver Code */ +func main() { + for i := 0; i < 10; i++ { + n := 100 + nums := randomNumbers(n) + index := findOne(nums) + fmt.Println("\n数组 [ 1, 2, ..., n ] 被打乱后 =", nums) + fmt.Println("数字 1 的索引为", index) + } +}