算法:堆排序(go实现)

4 篇文章 0 订阅

照旧,老铁先开始讲理论:

理论:

堆排序,就是根据大顶堆的特性——父节点总是大于子节点,则root节点就是堆中的最大数值,所以只要每次取出root节点,就可以完成排序。
so,依然是循环排序,
循环内分两步走:
第一步:将数组按大顶堆结构排列
第二步:取出堆顶数据
。。。
第一步:将剩余数组按大顶堆结构排列
第二部:取出对顶数据
。。。
————————————————————————————————
所以思路就明确了,这是个嵌套循环,或者递归。
跳出条件,是所有数都已经取过。

————————————————————————————————
那思路是有了,代码怎么写呢?
咱们码农要明确,程序离不开两个东西:1,数据结构。2,计算逻辑。
万变不离其宗,如果离了……那当我没说……
1,数据结构
大家都知道,这是传统手艺。堆结构,用数组来存储 array。
root节点位于 array[0] 位置。
root的左子节点位于 array[1] 位置
root的右子节点位于 array[2] 位置
如果用 index 表示元素下标。
则满足
父节点位于 array[i]
左子节点位于 array[2i+1]
右子节点位于 array[2
i+2]

好了,数据模型已经描述清晰。
那咱们初始化一个数组:

sort_init := []int{5, 7, 9, 4, 8, 3, 1, 2, 6, 3}

开始讲计算的逻辑
2,计算逻辑
其实主要是拆解第一步的堆排列。将复杂问题拆解成最小单元,这样就比较容易解决了。
那对于堆这种结构来说,最小单元是这个样子:

A
B
C

那最小单元的计算逻辑,就是比较这三个数,然后将每个数放在合适的位置。
伪代码是这样的:

arr=[]int{A,B,C}
root=A的下标
child=B的下标
if C 并且 C>B{
	child=C的下标
}
比较root指向的元素,child指向的元素
大的位于堆顶arr[0]位置

好,那咱们写成一个方法:

fun sor_stack(arr []int,start int,end int){
	root := start;
	child:=2*root+1;
	if child>end{
		return
		}
	if child+1<=end&&arr[child+1]>arr[child]{
		child++
		}
	if arr[child]>arr[root]{
		arr[child],arr[root]=arr[root],arr[child]
		}
	}
}

这是3个节点的情况。
那如果有多个节点,咱们就要开始递归大法了。
递归,主要是找到入点,和出点。能进去能出来,就成功了。

func sort_stack(arr []int,start int,end int) []int {
	root:=start
	child:=2*root+1
	if child >end{ 
		return arr
	}
	if child+1<=end&&arr[child+1]>arr[child]{
		child++
	}
	if arr[child]>arr[root]{
		arr[child],arr[root]=arr[root],arr[child]
		root = child
		sort_stack(arr,root,end)
	}else{
	return arr
	}
	return arr
}

对于堆的处理函数已经有了,
那接下来就是按步骤执行排序。

1,获得一个数组,上面已经有了,就用 sort_init
2,将数组初始化为堆结构,这时候可以用到咱们的sort_stack函数,从后往前处理,

A
B
C
D
E
F

先处理

C
F

再处理

B
D
E

最后处理

A
B
C

将这个过程封装成函数

// 找到C所在的位置
func init_fun(sort_init []int) []int{
	first := len(sort_init)/2-1
	// 开始计算
	for i:=first;i>-1;i--{
		sort_init = sort_stack(sort_init,i,len(sort_init)-1)
	}
	return sort_init
}

3,利用堆结构排序
不断的取出堆顶元素
重新组织堆结构

res := []int{}
for{
	if len(sort_init)==0{
		break
		}
	tmp := []int{sort_init[0]}
	// 将堆顶元素放到res的0位置
	res = append(tmp,res...)
	// 去除堆顶元素
	sort_init = sort_init[1:]
	// 重新组织堆结构
	sort_init = init_fun(sort_init)
}

排序完成!
然后,我们来看看,这个过程,有没有办法优化呢?
既然我说了,那肯定是可以优化的。因为我们用了两个数组——
sort_init(初始化的数组)
res(保存排序结果的数组)
现在我们重写一下最后的排序过程,将两个数组合并为同一个。其实只要将堆顶元素放在数组尾部,
然后缩短堆结构覆盖的范围即可。
代码如下:

	for end := len(sort_init) - 1; end > 0; end-- {
		sort_init[0], sort_init[end] = sort_init[end], sort_init[0]
		sort_init = sort_stack(sort_init, 0, end-1)
	}

其实,最后的第三步,原来只要3行。
所以说,写代码吧,就是不断重构的过程
————————————————————————————————————
算法这个话题,大神们早已总结并完善很多很多年。
但对于大多数程序猿来说,算法真的既没有用,又枯燥,又需要很多时间。
所以很多人都会怀疑,算法真的有用吗?
其实,算法真的是有用的,不但对于专门搞算法研究的人有用,对于咱们搬砖垒长城的,也有用,优化代码、重构业务,算法的威力不容小觑。
那怎么学算法呢?大家只能一步一步,把算法的来龙去脉,揉碎了,掰开了开看,来理解。
祝大家好运~如果能在码农的路上,能走一生,其实也不错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值