排序算法总结 go实现代码

Posted 布羽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法总结 go实现代码相关的知识,希望对你有一定的参考价值。

排序算法的分类如下

 

 

各个算法的时间和空间代价如下

注:另外还有一个时间代价为O(N)的桶排序算法,局限性比较大,感兴趣可以另作了解。

 

那么,如何选择各个排序算法呢?

1. 首先,在待排序数组长度比较短的情况下,使用简单排序算法效果比较好。

    在简单排序算法中,直接插入排序的平均情况是相对较好的。而简单选择排序则适合记录(数据元素)的关键字比较大的情况,因为选择排序的移动次数比较少,主要消耗在比较。

2. 如果数组长度比较长,需要考虑改进算法。

   3.希尔排序的平均和最坏情况不如其他改进算法,不用重点考虑。 

   4.如果对稳定性有要求,考虑归并排序。

   5.如果对辅助空间有限制,考虑堆排序

   6.如果没有以上考虑,综合现实使用情况来看,快排算法,如其名,是使用最广泛,效果最好的,选它没错。

 

以下放go代码实现

 

package main

import (
	"fmt"
)

//BSort0 最简单排序,
func BSort0(nums []int) 
	for i := 0; i < len(nums); i++ 
		for j := i + 1; j < len(nums); j++ 
			if nums[i] > nums[j] 
				nums[i], nums[j] = nums[j], nums[i]
			
		
	


//BSort1 冒泡排序
func BSort1(nums []int) 
	for i := 0; i < len(nums)-1; i++ 
		for j := len(nums) - 1; j > i; j-- 
			if nums[j] < nums[j-1] 
				nums[j], nums[j-1] = nums[j-1], nums[j]
			
		
	


//BSort2 优化冒泡,增加了标志变量,如果一次J的循环下来发现没有产生交换,则直接return
func BSort2(nums []int) 
	flag := true
	for i := 0; i < len(nums)-1 && flag; i++ 
		flag = false
		for j := len(nums) - 1; j > i; j-- 
			if nums[j] < nums[j-1] 
				nums[j], nums[j-1] = nums[j-1], nums[j]
				flag = true
			
		
	


//SelectSort 简单选择排序,类似BSort0的思路,但是保存索引位,以减小交换次数
func SelectSort(nums []int) 
	var minIndex int
	for i := 0; i < len(nums); i++ 
		minIndex = i
		for j := i + 1; j < len(nums); j++ 
			if nums[minIndex] > nums[j] 
				minIndex = j
			
		
		nums[i], nums[minIndex] = nums[minIndex], nums[i]
	


//InsertSort 插入排序,参考整理扑克
func InsertSort(nums []int) 
	var i, j, temp int
	for i = 1; i < len(nums); i++ 
		temp = nums[i]
		for j = i; j > 0; j-- 
			if temp < nums[j-1] 
				nums[j] = nums[j-1]
			 else 
				break
			
		
		nums[j] = temp
	


//ShellSort 思路就是将一个长序列,按照h为间隔拆分成多个子序列(交叉拆分),对每个子序列插入排序。不断减小h,重复上述行为。最后h=1再执行一次插入排序。
func ShellSort(nums []int) 
	var i, j, temp int
	n := len(nums)
	h := n/3 + 1
	for h >= 1 
		//i从h开始,因为每个子序列的第一个元素默认排好了
		for i = h; i < n; i++ 
			temp = nums[i]
			for j = i; j > h-1; j -= h 
				if temp < nums[j-h] 
					nums[j] = nums[j-h]
				 else 
					break
				
			
			nums[j] = temp
		
		h /= 3
	


//HeapAdjust堆调整函数,默认(s,m]区间内的元素已经满足大顶堆性质,调整nums[s]
func HeapAdjust(nums []int, s int, m int) 
	temp := nums[s]
	for i := 2 * s; i <= m; i *= 2 
		if i < m && nums[i] < nums[i+1] 
			i++
		
		if nums[i] < temp 
			break
		 else 
			nums[s] = nums[i]
			s = i
		
	
	nums[s] = temp


//HeapSort 堆排序,将一个堆按层次序编号,编号就是数组索引,将这个堆放在数组中,每次抽走根节点(最小或者最大),剩下的堆从新调整,维持最小顶或者最大顶性质。
//实现最大顶堆排序算法,堆排序比较特殊,数组索引应该从1开始,而不是0。
func HeapSort(nums []int) 
	n := len(nums) - 1
	//初始化,从最底层的非叶节点开始,往上调整
	for i := n / 2; i >= 1; i-- 
		HeapAdjust(nums, i, n)
	
	//开始排序
	for i := 0; i < n-1; i++ 
		nums[1], nums[n-i] = nums[n-i], nums[1]
		HeapAdjust(nums, 1, n-i-1)
	


//归并排序,有递归版本和迭代版本,这里直接实现迭代版本
//Merge 合并[a,b]和[b+1,c]
func Merge(SR []int, TR []int, a int, b int, c int) 
	i, s := a, a
	j := b + 1
	for ; i <= b && j <= c; s++ 
		if SR[i] < SR[j] 
			TR[s] = SR[i]
			i++
		 else 
			TR[s] = SR[j]
			j++
		
	
	if i <= b 
		for ; i <= b; i++ 
			TR[s] = SR[i]
			s++
		
	
	if j <= c 
		for ; j <= c; j++ 
			TR[s] = SR[j]
			s++
		
	


//MergePass 归并SR中长度为s的子序列,n为序列总长度
func MergePass(SR []int, TR []int, s int, n int) 
	var i int
	for i = 0; i < n-2*s; i += 2 * s 
		Merge(SR, TR, i, i+s-1, i+2*s-1)
	
	//如果剩余长度大于s,那么合并两个子序列(其中一个长度为s)
	if n-i+1 > s 
		Merge(SR, TR, i, i+s-1, n-1)
	 else 
		for j := i; j < n; j++ 
			TR[j] = SR[j]
		
	


//MergeSort 归并排序,有递归版本和迭代版本,这里直接实现迭代版本
func MergeSort(nums []int) 
	TR := make([]int, len(nums))
	k := 1
	for k < len(nums) 
		MergePass(nums, TR, k, len(nums))
		k *= 2
		if k >= len(nums) 
			nums = TR
			break
		 else 
			MergePass(TR, nums, k, len(nums))
		
		k *= 2
	


//Partition 取nums[low]为中枢值,将[low,high]之间的数分割到左右两边
func Partition(nums []int, low int, high int) int 
	temp := nums[low]
	for low < high 
		//先搜索右边
		for low < high && temp < nums[high] 
			high--
		
		nums[low], nums[high] = nums[high], nums[low]
		for low < high && temp > nums[low] 
			low++
		
		nums[low], nums[high] = nums[high], nums[low]
	
	return low


//Partition2 优化不必要的交换,取nums[low]为中枢值,将[low,high]之间的数分割到左右两边
func Partition2(nums []int, low int, high int) int 
	temp := nums[low]
	for low < high 
		for low < high && nums[high] > temp 
			high--
		
		nums[low] = nums[high]
		for low < high && nums[low] < temp 
			low++
		
		nums[high] = nums[low]
	
	nums[low] = temp
	return low


//QSort 递归快排函数,对[a,b]之间的区域快排
func QSort(nums []int, a int, b int) 
	if a < b 
		q := Partition(nums, a, b)
		QSort(nums, a, q-1)
		QSort(nums, q+1, b)
	


//QSort2 尾递归优化版本,基于尾递归的思想,将尾递归转变为迭代,减少初始序列极度不平衡下的函数栈的深度。这种优化主要是为了减小栈空间的利用和切换的时间代价。
func QSort2(nums []int, a int, b int) 
	for a < b 
		q := Partition(nums, a, b)
		QSort2(nums, a, q-1)
		a = q + 1
	


//QuickSort 快排入口
func QuickSort(nums []int) 
	QSort2(nums, 0, len(nums)-1)

//其它的快排优化:
//1.根据数组大小,小于某个阈值时,使用直接插入排序,否则使用快排
//2. 优化选取中枢值,根据数组的大小,随机选择一定数量的数据元,进行排序,选出中间的值作为中枢值

func main() tt := []int5, 6, 9, 4, 7, 3, 1, 8, 2 //tt := []int2, 1, 3, 4, 5, 6, 7 QuickSort(tt) fmt.Println(tt)

  

以上是关于排序算法总结 go实现代码的主要内容,如果未能解决你的问题,请参考以下文章

八大排序算法总结

go语言实现排序算法

十大经典排序算法最强总结(含Java代码实现)

Go 数据结构和算法篇:选择排序

Go 数据结构和算法篇:选择排序

最全排序算法原理解析java代码实现以及总结归纳