diff --git a/codes/go/chapter_sorting/quick_sort/quick_sort.go b/codes/go/chapter_sorting/quick_sort/quick_sort.go new file mode 100644 index 000000000..cc39bc1ac --- /dev/null +++ b/codes/go/chapter_sorting/quick_sort/quick_sort.go @@ -0,0 +1,128 @@ +//File: quick_sort.go +//Created Time: 2022-12-12 +//Author: msk397 (machangxinq@gmail.com) + +package quick_sort + +// 快速排序 +type QuickSort struct{} + +// 快速排序(中位基准数优化) +type QuickSortMedian struct{} + +// 快速排序(尾递归优化) +type QuickSortTailCall struct{} + +/* 哨兵划分 */ +func (q *QuickSort) partition(nums []int, left, right int) int { + // 以 nums[left] 作为基准数 + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- // 从右向左找首个小于基准数的元素 + } + for i < j && nums[i] <= nums[left] { + i++ // 从左向右找首个大于基准数的元素 + } + // 元素交换 + nums[i], nums[j] = nums[j], nums[i] + } + // 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i // 返回基准数的索引 +} + +/* 快速排序 */ +func (q *QuickSort) quickSort(nums []int, left, right int) { + // 子数组长度为 1 时终止递归 + if left >= right { + return + } + // 哨兵划分 + pivot := q.partition(nums, left, right) + // 递归左子数组、右子数组 + q.quickSort(nums, left, pivot-1) + q.quickSort(nums, pivot+1, right) +} + +/* 选取三个元素的中位数 */ +func (q *QuickSortMedian) medianThree(nums []int, left, mid, right int) int { + if (nums[left] > nums[mid]) != (nums[left] > nums[right]) { + return left + } else if (nums[mid] < nums[left]) != (nums[mid] > nums[right]) { + return mid + } + return right +} + +/* 哨兵划分(三数取中值)*/ +func (q *QuickSortMedian) partition(nums []int, left, right int) int { + // 以 nums[left] 作为基准数 + med := q.medianThree(nums, left, (left+right)/2, right) + // 将中位数交换至数组最左端 + nums[left], nums[med] = nums[med], nums[left] + // 以 nums[left] 作为基准数 + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- //从右向左找首个小于基准数的元素 + } + for i < j && nums[i] <= nums[left] { + i++ //从左向右找首个大于基准数的元素 + } + //元素交换 + nums[i], nums[j] = nums[j], nums[i] + } + //将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i //返回基准数的索引 +} + +/* 快速排序 */ +func (q *QuickSortMedian) quickSort(nums []int, left, right int) { + // 子数组长度为 1 时终止递归 + if left >= right { + return + } + // 哨兵划分 + pivot := q.partition(nums, left, right) + // 递归左子数组、右子数组 + q.quickSort(nums, left, pivot-1) + q.quickSort(nums, pivot+1, right) +} + +/* 哨兵划分 */ +func (q *QuickSortTailCall) partition(nums []int, left, right int) int { + // 以 nums[left] 作为基准数 + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- // 从右向左找首个小于基准数的元素 + } + for i < j && nums[i] <= nums[left] { + i++ // 从左向右找首个大于基准数的元素 + } + // 元素交换 + nums[i], nums[j] = nums[j], nums[i] + } + // 将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i // 返回基准数的索引 +} + +/* 快速排序(尾递归优化)*/ +func (q *QuickSortTailCall) quickSort(nums []int, left, right int) { + // 子数组长度为 1 时终止 + for left < right { + // 哨兵划分操作 + pivot := q.partition(nums, left, right) + // 对两个子数组中较短的那个执行快排 + if pivot-left < right-pivot { + q.quickSort(nums, left, pivot-1) // 递归排序左子数组 + left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right] + } else { + q.quickSort(nums, pivot+1, right) // 递归排序右子数组 + right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1] + } + } +} diff --git a/codes/go/chapter_sorting/quick_sort/quick_sort_test.go b/codes/go/chapter_sorting/quick_sort/quick_sort_test.go new file mode 100644 index 000000000..cd1925e0c --- /dev/null +++ b/codes/go/chapter_sorting/quick_sort/quick_sort_test.go @@ -0,0 +1,34 @@ +//File: quick_sort_test.go +//Created Time: 2022-12-12 +//Author: msk397 (machangxinq@gmail.com) + +package quick_sort + +import ( + "fmt" + "testing" +) + +// 快速排序 +func TestQuickSort(t *testing.T) { + q := QuickSort{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("快速排序完成后 nums = ", nums) +} + +// 快速排序(中位基准数优化) +func TestQuickSortMedian(t *testing.T) { + q := QuickSortMedian{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("快速排序(中位基准数优化)完成后 nums = ", nums) +} + +// 快速排序(尾递归优化) +func TestQuickSortTailCall(t *testing.T) { + q := QuickSortTailCall{} + nums := []int{4, 1, 3, 1, 5, 2} + q.quickSort(nums, 0, len(nums)-1) + fmt.Println("快速排序(尾递归优化)完成后 nums = ", nums) +} diff --git a/docs/chapter_sorting/quick_sort.md b/docs/chapter_sorting/quick_sort.md index 7934b595a..d9ff7e107 100644 --- a/docs/chapter_sorting/quick_sort.md +++ b/docs/chapter_sorting/quick_sort.md @@ -109,7 +109,24 @@ comments: true === "Go" ```go title="quick_sort.go" - + /* 哨兵划分 */ + func partition(nums []int, left, right int) int { + //以 nums[left] 作为基准数 + i, j := left, right + for i < j { + for i < j && nums[j] >= nums[left] { + j-- //从右向左找首个小于基准数的元素 + } + for i < j && nums[i] <= nums[left] { + i++ //从左向右找首个大于基准数的元素 + } + //元素交换 + nums[i], nums[j] = nums[j], nums[i] + } + //将基准数交换至两子数组的分界线 + nums[i], nums[left] = nums[left], nums[i] + return i //返回基准数的索引 + } ``` === "JavaScript" @@ -225,7 +242,18 @@ comments: true === "Go" ```go title="quick_sort.go" - + /* 快速排序 */ + func quickSort(nums []int, left, right int) { + // 子数组长度为 1 时终止递归 + if left >= right { + return + } + // 哨兵划分 + pivot := partition(nums, left, right) + // 递归左子数组、右子数组 + quickSort(nums, left, pivot-1) + quickSort(nums, pivot+1, right) + } ``` === "JavaScript" @@ -369,7 +397,25 @@ comments: true === "Go" ```go title="quick_sort.go" + /* 选取三个元素的中位数 */ + func medianThree(nums []int, left, mid, right int) int { + if (nums[left] > nums[mid]) != (nums[left] > nums[right]) { + return left + } else if (nums[mid] < nums[left]) != (nums[mid] > nums[right]) { + return mid + } + return right + } + /* 哨兵划分(三数取中值)*/ + func partition(nums []int, left, right int) int { + // 以 nums[left] 作为基准数 + med := medianThree(nums, left, (left+right)/2, right) + // 将中位数交换至数组最左端 + nums[left], nums[med] = nums[med], nums[left] + // 以 nums[left] 作为基准数 + // 下同省略... + } ``` === "JavaScript" @@ -485,7 +531,22 @@ comments: true === "Go" ```go title="quick_sort.go" - + /* 快速排序(尾递归优化)*/ + func quickSort(nums []int, left, right int) { + // 子数组长度为 1 时终止 + for left < right { + // 哨兵划分操作 + pivot := partition(nums, left, right) + // 对两个子数组中较短的那个执行快排 + if pivot-left < right-pivot { + quickSort(nums, left, pivot-1) // 递归排序左子数组 + left = pivot + 1 // 剩余待排序区间为 [pivot + 1, right] + } else { + quickSort(nums, pivot+1, right) // 递归排序右子数组 + right = pivot - 1 // 剩余待排序区间为 [left, pivot - 1] + } + } + } ``` === "JavaScript"