堆排序
参考:
https://www.cnblogs.com/chengxiao/p/6129630.html
https://blog.csdn.net/qq_34374664/article/details/79545940
https://www.cnblogs.com/wengshuhang/articles/10039476.html
堆分为最大堆和最小堆,其实就是完全二叉树。
最大堆要求节点的元素都要不小于其孩子;
最小堆要求节点元素都不大于其左右孩子;
两者对左右孩子的大小关系不做任何要求,其实很好理解。有了上面的定义,我们可以得知,处于最大堆的根节点的元素一定是这个堆中的最大值。
其实我们的堆排序算法就是抓住了堆的这一特点,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最大堆,依次类推,最终得到排序的序列。
其基本思想为(大顶堆)
1.将初始待排序关键字序列(R1,R2…Rn)构建成大顶堆,此堆为初始的无序区
2.将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,…Rn-1)和新的有序区(Rn)
3.由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,…Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2…Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成
package main
import(
"fmt"
)
// 构建堆
func build_heap(data_list []int, i int, length int){
tmp := data_list[i] // 子树的root节点
for k := i * 2 + 1; k < length; k = k * 2 + 1{
if (k + 1 < length) && (data_list[k] < data_list[k+1]) {//右子树的游标比数组长度小,并且左子树小于右子树,让游标指向右子树
k++
}
if data_list[k] > tmp { //右子树大于root节点,调整,让root游标指向新的root节点
data_list[i] = data_list[k]
i = k
} else {
break
}
}
data_list[i] = tmp
}
// 排序
func sort(data_list []int) {
for i := len(data_list)/2 - 1; i >= 0; i-- {// 遍历数组中,子树的个数
build_heap(data_list, i, len(data_list))
}
for j := len(data_list) - 1; j > 0; j-- {// 遍历数组
swap(data_list, 0, j)
build_heap(data_list, 0, j)
}
}
// 交换
func swap(data_list []int, a int, b int){
tmp := data_list[a]
data_list[a] = data_list[b]
data_list[b] = tmp
}
func main() {
fmt.Println("=== 排序算法 - 堆排序 ===")
data_list := []int{19, 8, 3, 5, 7, 3, 2, 11}
sort(data_list)
for i := 0; i < len(data_list); i++ {
fmt.Printf("%d ", data_list[i])
}
// fmt.Println(data_list) //go支持直接遍历数组打印
fmt.Println("")
}