首先讲一下堆这个数据结构的特征:堆是一个数组,可以看成是一个近似的完全二叉树,一个索引从零的数组,其父节点与子节点的索引存在下面的关系:
int parent(int child){
return (child - 1) / 2;
}
int left_child(int parent){
return parent * 2 + 1;
}
int right_child(int parent){
return parent * 2 + 2;
}
然后是父节点与子节点的值上面还存在着一定的要求,对于最大堆,父节点不能小于子节点;最小堆里面父节点不能大于子节点;所以最大堆里面的最大值以及最小堆里面的最小值都在数组的第一个元素,也就是树的根节点。
下面来看一下建堆的过程(以最大堆来进行描述),这个建堆的过程是为后面的堆排序做准备。这个建堆的过程是对这个堆的非叶节点从最大的索引开始一次的做shift_down操作。完成之后堆也就建立完成。
void build_heap(Heap &hp){
hp.size = hp.max_size;
for(int i = hp.size / 2; i >= 0; i--){
shift_down(hp, i);
}
}
shift_down操作就是在给定一个位置,在当前节点的子树都满足堆的要求情况下,然后当前节点与子节点进行对比,选择出比较大的子节点,如果这个子节点比当前节点大,那么交换两者的位置,然后对当前节点在进行递归的调用。当当前节点成为叶节点的时候,这棵树就是一个堆了;所以在上面的建堆过程中,由于每个叶节点自身组成的子树都是默认为堆的,虽然只有一个元素,那么在循环每次调用的时候,操作的当前节点的子树都是满足堆的条件的,那么做了shif_down之后,这棵树也是满足堆的要求的,当这个循环完成的时候,这个数组形成的树就是一个堆了。
void shift_down(Heap &hp, int parent_pos){
int left_pos = left_child(parent_pos);
int right_pos = right_child(parent_pos);
int largest_pos = parent_pos;
if(left_pos < hp.size && hp.array[left_pos] > hp.array[parent_pos])
largest_pos = left_pos;
if(right_pos < hp.size && hp.array[right_pos] > hp.array[largest_pos])
largest_pos = right_pos;
if(largest_pos != parent_pos){
int temp = hp.array[parent_pos];
hp.array[parent_pos] = hp.array[largest_pos];
hp.array[largest_pos] = temp;
shift_down(hp, largest_pos);
}
}
接着是在建立堆的基础上来进行堆排序,刚才的分析我们知道,每次一个最大堆的数组的第一个元素是这个数组里面最大的值,我们可以利用这个特征来完成排序的过程,每次我们都将数组的最大元素取出来,将堆的size减1,那么这个时候这个数组前面的元素仍然是一个堆,然后将这个元素与数组的首个元素进行交换,这个时候,第一个元素的两个子树保持原来的特征,都是一个满足条件的子树,那么根据前面的分析,对这个首要元素进行shift_down操作,那么这个数组又保持了堆的性质,当我们做这个交换一直到数组的首个元素的时候,一个个最大的值慢慢的一次呗迁移到了数组的后面,这个时候就已经是排好序的了。
void heap_sort(int *array, int n){
Heap hp = {
.size = 0,
.max_size = n,
.array = array
};
build_heap(hp);
for(int i = n - 1; i > 0; i--){
hp.size--;
int temp = array[i];
array[i] = array[0];
array[0] = temp;
shift_down(hp, 0);
}
}
下面是一个mian函数的测试:
int main()
{
const int ARRAY_LENGTH = 8;
int array[ARRAY_LENGTH] = {5, 2, 4, 7, 1, 3, 2, 6};
heap_sort(array, ARRAY_LENGTH);
return 1;
}
排序的结果:
[ 1 2 2 3 4 5 6 7 ]
本文深入探讨了堆数据结构的特点,详细解释了最大堆和最小堆的概念,以及如何通过堆来实现高效的排序算法。文章提供了堆排序的具体步骤,包括建堆过程和排序过程,并附带了完整的代码示例。
579

被折叠的 条评论
为什么被折叠?



