title: 堆排序原来这么简单
date: 2019-09-21 20:17:41
tags: 数据结构
categories: 计算机理论
前言
之前上数据结构这门课的时候,一直没搞明白堆排序到底是为啥就能排序了呢。最近终于弄懂了,其实很简单。
顺便吐槽一下,严蔚敏的紫书代码可读性真滴不咋滴,有了注释也很难知道在说啥,没有就更不用说了,一股浓浓的学究代码气息扑面而来?anyway,先把堆排序这页撕了吧 ?。
堆排序的前提
堆要符合什么条件
- 堆排序的堆是完全二叉树,因此用数组实现是最好的(因为完全二叉树的节点是连续的)
- 父节点比子节点大,或父节点比子节点小
获取父子节点
易观察并总结出规律得:
获取子节点的公式
c1 = 2n+1
c2 = 2n+2
获取父节点公式
p = (c+1)/2
说在前面
别急,在进行真正的堆排序之前,不妨先实现两个操作,堆调整和创建堆,他们都是堆排序所必需的操作。
调整堆
为了保持每个父节点对于其子节点之间的大小关系,需要进行堆的调整
假设我现在是大根堆,就是父节点要比子节点大,具体调整办法就是,求出父节点的子节点,然后进行比较,把最大的那个节点与父节点进行交换,因为发生了交换,所以原来的子节点与他的子节点的关系可能发生了变化,所以还要对子节点进行堆调整
举个例子
框框圈住的地方需要调整堆,于是与其子节点12、7进行比较发现12号子节点最大,于是交换上去,12号发生了交换所以,12号和其子节点7、8号也需要进行调整堆。这样就保持了堆的数据特性
上代码,看得更清楚
//调整堆
void heapify(int tree[],int n,int i){
if(i >= n){
return;
}
int c1 = 2*i + 1;
int c2 = 2*i + 2;
int max = i;
if(c1 < n && tree[c1] > tree[max]){
max = c1;
}
if(c2 < n && tree[c2] > tree[max]){
max = c2;
}
if(max != i){
swap(tree,i,max);
heapify(tree,n,max);
}
}
创建堆
however,通常情况是,给你的数完全不符合堆的结构,然后不知道该从哪开始调整堆了。莫慌,从h-1层的最左边的节点开始逐步调整即可。这样就能把每个红框框圈起来的父子节点们都进行了一边堆调整了哦,使得树的结构符合堆的要求
h-1的阶段序号怎么求?easy,求最后一个的节点的父节点就是了嘛
如图所示,从3号节点开始,然后2号,1号,0号。没错,你也发现了! 其实就是一个递减的关系 3,2,1,0这样在遍历的时候就很容易了。
上代码:
//创建堆
//从h-1层的最左边的节点开始调整
void build_heap(int tree[],int n){
int last_node = n - 1;
int parent = (last_node - 1)/ 2 ;
for(int i=parent;i >=0;i--){
heapify(tree,n,i);
}
}
堆排序
好了,现在随便给你一串序列,你都能通过build_heap
构造大根堆了
例如:
输入:
{4,10,3,5,1,2,9,6,13}
输出:
{13,10,9,6,1,2,3,4,5}
堆排序的过程如下:
因为可以发现整个树的根节点就是最大值,所以直接将最后一个节点与第一个节点交换,然后“斩断”最后一个节点。然后对新的根节点进行堆调整。
“斩断”的节点就是当前的最大值,随便你怎么搞,输出也好,放在队列中也好,放在栈中也好。
这样你就可以又得到一颗去除了最大值的堆了~~,重复此过程直到树中没有树了或者遍历到头了(其实是懒得作图了,看代码你会明白一切的。)。就完成了堆排序了哦。
上代码:
//堆排序,升序
void heap_sort(int tree[],int n){
build_heap(tree,n);
for(int i=n-1;i>=0;i--){
//最后一个与根节点进行交换
swap(tree,i,0);
//调整根节点,注意此时的第二个参数也就是处理的数组大小变成了i哦,相当于斩断了最后一个节点
heapify(tree,i,0);
}
}
测试
完整代码(C语言)
#include<stdio.h>
#include<stdlib.h>
void swap(int tree[],int i,int j){
int tmp = tree[i];
tree[i] = tree[j];
tree[j] = tmp;
}
//调整堆
void heapify(int tree[],int n,int i){
if(i >= n){
return;
}
int c1 = 2*i + 1;
int c2 = 2*i + 2;
int max = i;
if(c1 < n && tree[c1] > tree[max]){
max = c1;
}
if(c2 < n && tree[c2] > tree[max]){
max = c2;
}
if(max != i){
swap(tree,i,max);
heapify(tree,n,max);
}
}
//创建堆
//从h-1层的最左边的节点开始调整
void build_heap(int tree[],int n){
int last_node = n - 1;
int parent = (last_node - 1)/ 2 ;
for(int i=parent;i >=0;i--){
heapify(tree,n,i);
}
}
//堆排序,升序
void heap_sort(int tree[],int n){
build_heap(tree,n);
for(int i=n-1;i>=0;i--){
//最后一个与根节点进行交换
swap(tree,i,0);
//调整根节点,注意此时的第二个参数也就是处理的数组大小变成了i哦,相当于斩断了最后一个节点
heapify(tree,i,0);
}
}
int main(){
int tree[] = {4,10,3,5,1,2,9,6,13};
int n = 9;
heap_sort(tree,n);
for(int i=0;i<n;i++){
printf("%d ",tree[i]);
}
return 0;
}
1 2 3 4 5 6 9 10 13
完整代码(Python)
# coding:utf-8
# 交换数据元素
def swap(tree, i, j):
temp = tree[i]
tree[i] = tree[j]
tree[j] = temp
# 调整堆,调整为大根堆
def heapify(tree, i):
if i >= len(tree):
return
c1 = 2 * i + 1
c2 = 2 * i + 2
max = i
if c1 < len(tree) and tree[c1] > tree[max]:
max = c1
if c2 < len(tree) and tree[c2] > tree[max]:
max = c2
if max != i:
swap(tree, max, i)
heapify(tree, max)
# 构造堆
def build_heap(tree):
last_node = len(tree) - 1
parent = int((last_node - 1) / 2)
for i in range(parent, -1, -1):
heapify(tree, i)
# 堆排序.大根堆,降序
def heap_sort(tree):
build_heap(tree)
result = []
for i in range(len(tree) - 1, -1, -1):
# 交换根节点与最后一个节点,砍断最后一个节点,重新调整堆
swap(tree, i, 0)
result.append(tree.pop())
heapify(tree, 0)
return result
# 调整堆,调整为小根堆
def heapify1(tree, i):
if i >= len(tree):
return
c1 = 2 * i + 1
c2 = 2 * i + 2
min = i
if c1 < len(tree) and tree[c1] < tree[min]:
min = c1
if c2 < len(tree) and tree[c2] < tree[min]:
min = c2
if min != i:
swap(tree, min, i)
heapify(tree, min)
# 构造小根堆
def build_heap1(tree):
last_node = len(tree) - 1
parent = int((last_node - 1) / 2)
for i in range(parent, -1, -1):
heapify1(tree, i)
# 堆排序.小根堆,升序
def heap_sort1(tree):
build_heap1(tree)
result = []
for i in range(len(tree) - 1, -1, -1):
# 交换根节点与最后一个节点,砍断最后一个节点,重新调整堆
swap(tree, i, 0)
result.append(tree.pop())
heapify1(tree, 0)
return result
if __name__ == "__main__":
tree = [2, 5, 3, 1, 10, 4]
print("原始数据:")
print(tree)
result = heap_sort(tree)
print("大根堆排序:")
print(result)
# 小根堆测试用例
tree = [345, 312, 65, 765, 143, 564]
print("原始数据:")
print(tree)
result = heap_sort1(tree)
print("小根堆排序:")
print(result)
原始数据:
[2, 5, 3, 1, 10, 4]
大根堆排序:
[10, 5, 4, 3, 2, 1]
原始数据:
[345, 312, 65, 765, 143, 564]
小根堆排序:
[65, 143, 312, 345, 564, 765]