📌算法Go语言描述📌algorithm📌sort1.go
package algorithm

/* 冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
比较次数: C(n,2)=n(n-1)/2
时间复杂度O(n^2),空间复杂度O(1)。
*/

func BubbleSort(arr []int) {
	length := len(arr)
	for i := 0; i < length; i++ {
		for j := 0; j < length-1-i; j++ {
			if arr[j] > arr[j+1] {
				arr[j], arr[j+1] = arr[j+1], arr[j]
			}
		}
	}
}

// 优化,相同的循环次数,更少的交换次数
func BubbleSort2(arr []int) {
	end := len(arr) - 1
	for i := 0; i < end; i++ {
		for j := end; j > i; j-- {
			if arr[i] > arr[j] {
				arr[i], arr[j] = arr[j], arr[i]
			}
		}
	}
}

/* 选择排序
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复上一步,直到所有元素均排序完毕。
时间复杂度O(n^2),空间复杂度O(1)。
同样的时间复杂度,选择排序的性能上还是要略优于冒泡排序。
*/

func SelectionSort(arr []int) {
	length := len(arr)
	for i := 0; i < length-1; i++ {
		m := i // 最小索引
		for j := i + 1; j < length; j++ {
			if arr[j] < arr[m] {
				m = j
			}
		}
		if m != i {
			arr[m], arr[i] = arr[i], arr[m]
		}
	}
}

/* 插入排序
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
时间复杂度O(n^2),空间复杂度O(1)。
同样的时间复杂度,直接插入排序法比冒泡和选择排序的性能要好一些。
*/

func InsertionSort(arr []int) {
	length := len(arr)
	for i := 1; i < length; i++ {
		for j := i; j > 0 && arr[j-1] > arr[j]; j-- {
			arr[j], arr[j-1] = arr[j-1], arr[j]
		}
	}
}

/* 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。
选择一个增量序列t1,t2,……,tk,其中ti>tj, tk = 1;按增量序列个数k,对序列进行k趟排序;
每趟排序,根据对应的增量gap,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
时间复杂度取决于gap增量,约为O(n^1.3),空间复杂度O(1)。
*/

func ShellSort(arr []int) {
	length := len(arr)
	for gap := length >> 1; gap > 0; gap >>= 1 {
		for i := gap; i < length; i++ {
			for j := i - gap; j >= 0 && arr[j+gap] < arr[j]; j -= gap {
				arr[j], arr[j+gap] = arr[j+gap], arr[j]
			}
		}
	}
}

func ShellSort2(arr []int) {
	length := len(arr)
	gap := 1
	for gap < length/3 {
		gap = gap*3 + 1 //动态计算间隔
	}
	for gap > 0 {
		for i := gap; i < length; i++ {
			for j := i - gap; j >= 0 && arr[j+gap] < arr[j]; j -= gap {
				arr[j], arr[j+gap] = arr[j+gap], arr[j]
			}
		}
		gap /= 3
	}
}

/* 归并排序是建立在归并操作上的一种有效的排序算法。是采用分治法(Divide and Conquer)的一个非常典型的应用。
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
重复上一步直到某一指针达到序列尾;将另一序列剩下的所有元素直接复制到合并序列尾。
时间复杂度O(nlogn),空间复杂度O(n)。
*/

// 相邻两段有序区间[left,mid)和[mid,right)归并排序
func merge(arr []int, left, mid, right int) {
	result := make([]int, 0, right-left)
	i, j := left, mid
	for i < mid && j < right {
		if arr[i] < arr[j] {
			result = append(result, arr[i])
			i++
		} else {
			result = append(result, arr[j])
			j++
		}
	}
	for i < mid {
		result = append(result, arr[i])
		i++
	}
	for j < right {
		result = append(result, arr[j])
		j++
	}
	//将辅助数组的元素复制回原数组,这样该辅助空间就可以被释放掉
	copy(arr[left:right], result)
}

func MergeSort(arr []int, left, right int) { //前闭后开区间[left,right)
	if right-left < 2 {
		return
	}
	mid := (left + right) >> 1
	MergeSort(arr, left, mid)
	MergeSort(arr, mid, right)
	merge(arr, left, mid, right)
}

func MergeSort1(arr []int) {
	length := len(arr)
	if length < 2 {
		return
	}
	mid := length >> 1
	MergeSort1(arr[:mid])
	MergeSort1(arr[mid:])
	merge(arr, 0, mid, length)
}

func MergeSort2(arr []int) {
	length := len(arr)
	if length < 2 {
		return
	}
	for step := 1; step < length; step <<= 1 {
		left, mid, right := 0, step, step+step
		for right <= length {
			merge(arr, left, mid, right)
			left = right
			mid = left + step
			right = mid + step
		}
		if mid < length { //是否有右半段,此时right一定越界
			merge(arr, left, mid, length)
		}
	}
}

/* 小结
O(n^2)的排序效率:BubbleSort < BubbleSort2 < SelectionSort < InsertionSort
ShellSort与ShellSort2效率大致相当,MergeSort1与MergeSort2效率大致相当。
中等规模排序效率ShellSort优于MergeSort。大规模数据排序效率MergeSort优于ShellSort。
*/