From 5bbcb12979157081d00cbc0cfde31672228da4dd Mon Sep 17 00:00:00 2001 From: Reanon <793584285@qq.com> Date: Mon, 29 May 2023 12:23:52 +0800 Subject: [PATCH] feat(sort/search): support heap/selection_sort/binary_search_edge in go code (#521) * feat(go): support binary search edge and testcase * feat(go): support selection sort and testcase * feat(go): support heap sort and testcase * Update selection_sort_test.go * Update selection_sort.go * Update heap_sort.go --------- Co-authored-by: Yudong Jin --- .../chapter_searching/binary_search_edge.go | 55 +++++++++++++++++++ .../chapter_searching/binary_search_test.go | 12 ++++ codes/go/chapter_sorting/heap_sort.go | 44 +++++++++++++++ codes/go/chapter_sorting/heap_sort_test.go | 16 ++++++ codes/go/chapter_sorting/selection_sort.go | 24 ++++++++ .../go/chapter_sorting/selection_sort_test.go | 16 ++++++ 6 files changed, 167 insertions(+) create mode 100644 codes/go/chapter_searching/binary_search_edge.go create mode 100644 codes/go/chapter_sorting/heap_sort.go create mode 100644 codes/go/chapter_sorting/heap_sort_test.go create mode 100644 codes/go/chapter_sorting/selection_sort.go create mode 100644 codes/go/chapter_sorting/selection_sort_test.go diff --git a/codes/go/chapter_searching/binary_search_edge.go b/codes/go/chapter_searching/binary_search_edge.go new file mode 100644 index 000000000..8a27a6261 --- /dev/null +++ b/codes/go/chapter_searching/binary_search_edge.go @@ -0,0 +1,55 @@ +// File: binary_search_edge.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_searching + +/* 二分查找最左一个元素 */ +func binarySearchLeftEdge(nums []int, target int) int { + // 初始化双闭区间 [0, n-1] + i, j := 0, len(nums)-1 + for i <= j { + // 计算中点索引 m + m := i + (j-i)/2 + if nums[m] < target { + // target 在区间 [m+1, j] 中 + i = m + 1 + } else if nums[m] > target { + // target 在区间 [i, m-1] 中 + j = m - 1 + } else { + // 首个小于 target 的元素在区间 [i, m-1] 中 + j = m - 1 + } + } + if i == len(nums) || nums[i] != target { + // 未找到目标元素,返回 -1 + return -1 + } + return i +} + +/* 二分查找最右一个元素 */ +func binarySearchRightEdge(nums []int, target int) int { + // 初始化双闭区间 [0, n-1] + i, j := 0, len(nums)-1 + for i <= j { + // 计算中点索引 m + m := i + (j-i)/2 + if nums[m] < target { + // target 在区间 [m+1, j] 中 + i = m + 1 + } else if nums[m] > target { + // target 在区间 [i, m-1] 中 + j = m - 1 + } else { + // 首个大于 target 的元素在区间 [m+1, j] 中 + i = m + 1 + } + } + if j < 0 || nums[j] != target { + // 未找到目标元素,返回 -1 + return -1 + } + return j +} diff --git a/codes/go/chapter_searching/binary_search_test.go b/codes/go/chapter_searching/binary_search_test.go index 0b6084d68..74d105050 100644 --- a/codes/go/chapter_searching/binary_search_test.go +++ b/codes/go/chapter_searching/binary_search_test.go @@ -22,3 +22,15 @@ func TestBinarySearch(t *testing.T) { t.Errorf("目标元素 6 的索引 = %d, 应该为 %d", actual, expected) } } + +func TestBinarySearchEdge(t *testing.T) { + target := 6 + nums := []int{1, 3, 6, 6, 6, 6, 6, 10, 12, 15} + // 二分查找最左一个元素 + indexLeft := binarySearchLeftEdge(nums, target) + fmt.Println("数组中最左一个元素 6 的索引 = ", indexLeft) + + // 二分查找最右一个元素 + indexRight := binarySearchRightEdge(nums, target) + fmt.Println("数组中最右一个元素 6 的索引 = ", indexRight) +} diff --git a/codes/go/chapter_sorting/heap_sort.go b/codes/go/chapter_sorting/heap_sort.go new file mode 100644 index 000000000..7f610dfd1 --- /dev/null +++ b/codes/go/chapter_sorting/heap_sort.go @@ -0,0 +1,44 @@ +// File: heap_sort.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +/* 堆的长度为 n ,从节点 i 开始,从顶至底堆化 */ +func siftDown(nums *[]int, n, i int) { + for true { + // 判断节点 i, l, r 中值最大的节点,记为 ma + l := 2*i + 1 + r := 2*i + 2 + ma := i + if l < n && (*nums)[l] > (*nums)[ma] { + ma = l + } + if r < n && (*nums)[r] > (*nums)[ma] { + ma = r + } + // 若节点 i 最大或索引 l, r 越界,则无需继续堆化,跳出 + if ma == i { + break + } + // 交换两节点 + (*nums)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i] + // 循环向下堆化 + i = ma + } +} + +/* 堆排序 */ +func heapSort(nums *[]int) { + // 建堆操作:堆化除叶节点以外的其他所有节点 + for i := len(*nums)/2 - 1; i >= 0; i-- { + siftDown(nums, len(*nums), i) + } + // 从堆中提取最大元素,循环 n-1 轮 + for i := len(*nums) - 1; i > 0; i-- { + // 交换根节点与最右叶节点(即交换首元素与尾元素) + (*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0] + // 以根节点为起点,从顶至底进行堆化 + siftDown(nums, i, 0) + } +} diff --git a/codes/go/chapter_sorting/heap_sort_test.go b/codes/go/chapter_sorting/heap_sort_test.go new file mode 100644 index 000000000..9477d89ea --- /dev/null +++ b/codes/go/chapter_sorting/heap_sort_test.go @@ -0,0 +1,16 @@ +// File: heap_sort_test.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestHeapSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + heapSort(&nums) + fmt.Println("堆排序完成后 nums = ", nums) +} diff --git a/codes/go/chapter_sorting/selection_sort.go b/codes/go/chapter_sorting/selection_sort.go new file mode 100644 index 000000000..021222aca --- /dev/null +++ b/codes/go/chapter_sorting/selection_sort.go @@ -0,0 +1,24 @@ +// File: selection_sort.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +/* 选择排序 */ +func selectionSort(nums []int) { + n := len(nums) + // 外循环:未排序区间为 [i, n-1] + for i := 0; i < n-1; i++ { + // 内循环:找到未排序区间内的最小元素 + k := i + for j := i + 1; j < n; j++ { + if nums[j] < nums[k] { + // 记录最小元素的索引 + k = j + } + } + // 将该最小元素与未排序区间的首个元素交换 + nums[i], nums[k] = nums[k], nums[i] + + } +} diff --git a/codes/go/chapter_sorting/selection_sort_test.go b/codes/go/chapter_sorting/selection_sort_test.go new file mode 100644 index 000000000..937f74503 --- /dev/null +++ b/codes/go/chapter_sorting/selection_sort_test.go @@ -0,0 +1,16 @@ +// File: selection_sort_test.go +// Created Time: 2023-05-29 +// Author: Reanon (793584285@qq.com) + +package chapter_sorting + +import ( + "fmt" + "testing" +) + +func TestSelectionSort(t *testing.T) { + nums := []int{4, 1, 3, 1, 5, 2} + selectionSort(nums) + fmt.Println("选择排序完成后 nums = ", nums) +}