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

/*堆排序是指利用堆这种数据结构所设计的一种排序算法。
大顶堆用于实现升序排列,小顶堆用于实现降序排列。
堆排序的平均时间复杂度为 Ο(nlogn),空间复杂度为O(1)。
*/

func HeapSort(arr []int) []int {
	count := len(arr)
	for i := count/2 - 1; i >= 0; i-- {
		down(arr, i, count)
	}
	for i := count - 1; i > 0; i-- {
		arr[0], arr[i] = arr[i], arr[0]
		down(arr, 0, i)
	}
	return arr
}

func down(arr []int, root, count int) {
	child := root*2 + 1
	for child < count {
		if r := child + 1; r < count && arr[child] < arr[r] {
			child = r
		}
		if arr[root] >= arr[child] {
			return
		}
		arr[root], arr[child] = arr[child], arr[root]
		root = child // 继续往下沉
		child = root*2 + 1
	}
}

/*快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。
时间复杂度O(nlogn),空间复杂度O(logn)。
*/

//单路快排
func QuickSort1(arr []int) {
	length := len(arr)
	if length < 2 {
		return
	}
	pivot := arr[0]
	p := 0
	for i := 1; i < length; i++ {
		if arr[i] < pivot {
			p++
			arr[i], arr[p] = arr[p], arr[i]
		}
	}
	arr[0], arr[p] = arr[p], arr[0]
	QuickSort1(arr[:p])
	QuickSort1(arr[p+1:])
}
func QuickSortOne(arr []int, left, right int) { //前闭后开区间[left,right)
	if right-left < 2 {
		return
	}
	pivot := arr[left]
	p := left
	for i := left + 1; i < right; i++ {
		if arr[i] < pivot {
			p++
			arr[i], arr[p] = arr[p], arr[i]
		}
	}
	arr[left], arr[p] = arr[p], arr[left]
	QuickSortOne(arr, 0, p)
	QuickSortOne(arr, p+1, right)
}

//双路快排
func QuickSort2(arr []int) {
	length := len(arr)
	if length < 2 {
		return
	}
	pivot := arr[0]
	i, j := 1, length-1
	for {
		for i < length && arr[i] < pivot {
			i++
		}
		for j > 0 && arr[j] > pivot {
			j--
		}
		if i >= j {
			break
		}
		arr[i], arr[j] = arr[j], arr[i]
		i++
		j--
	}
	arr[0], arr[j] = arr[j], arr[0]
	QuickSort2(arr[:j])
	QuickSort2(arr[j+1:])
}
func QuickSortTwo(arr []int, left, right int) { //前闭后开区间[left,right)
	if right-left < 2 {
		return
	}
	pivot := arr[left]
	i, j := left+1, right-1
	for {
		for i < right && arr[i] < pivot {
			i++
		}
		for j > left && arr[j] > pivot {
			j--
		}
		if i >= j {
			break
		}
		arr[i], arr[j] = arr[j], arr[i]
		i++
		j--
	}
	arr[left], arr[j] = arr[j], arr[left]
	QuickSortTwo(arr, left, j)
	QuickSortTwo(arr, j+1, right)
}

//三路快排
func QuickSort3(arr []int) {
	length := len(arr)
	if length < 2 {
		return
	}
	pivot := arr[0]
	l, i, r := 0, 1, length-1
	for i <= r {
		if arr[i] > pivot {
			arr[i], arr[r] = arr[r], arr[i]
			r--
		} else if arr[i] < pivot {
			arr[i], arr[l] = arr[l], arr[i]
			i++
			l++
		} else {
			i++
		}
	}
	//循环结束后,[l,r]区间内的值均为pivot
	QuickSort3(arr[:l])
	QuickSort3(arr[r+1:])
}
func QuickSortThree(arr []int, left, right int) { //前闭后开区间[left,right)
	if right-left < 2 {
		return
	}
	pivot := arr[left]
	l, i, r := left, left+1, right-1
	for i <= r {
		if arr[i] > pivot {
			arr[i], arr[r] = arr[r], arr[i]
			r--
		} else if arr[i] < pivot {
			arr[i], arr[l] = arr[l], arr[i]
			i++
			l++
		} else {
			i++
		}
	}
	//循环结束后,[l,r]区间内的值均为pivot
	QuickSortThree(arr, left, l)
	QuickSortThree(arr, r+1, right)
}

/*
平均时间上,堆排序的时间常数比快排要大一些,因此通常会慢一些。

三种快排效率比较:
单路快排在重复较少的情况下表现最佳,
随着重复率增加双路快排优于单路,
大量重复的情况下三路快排优势最大。

pdqsort(Pattern-defeating quicksort)是一种融合插入排序、堆排序和优化后的快排的新型排序算法,在rust和go1.19中采用。
对于短序列(go中长度12以内),使用插入排序。
其他情况,使用快速排序保证整体性能。
当快速排序表象不佳,使用堆排序保证最坏情况小时间复杂度仍然为O(nlogn)。

算法复杂度比较:
               Best      Avg       Worst
InsertionSort  O(n)      O(n^2)    O(n^2)
QuickSort      O(nlogn)  O(nlogn)  O(n^2)
HeapSort       O(nlogn)  O(nlogn)  O(nlogn)
pdqsort        O(n)      O(nlogn)  O(nlogn)
*/