一、堆排序
-
先了解什么是堆。这里的堆,其实是一个完全二叉树。有两个性质
- 大顶堆的父节点比子节点要大。小顶堆的父节点比子节点要小。
- 父节点和子节点之间的关系是.父节点的下标是k,子节点就是2k+1/2k+2。
-
假设要排序一个数组{1,2,4,3,5}
按照层序遍历,则现在这个数组在数中的位置是:
-
现在这个堆还不是一个大顶堆。所以要做如下调整。
-
得到大顶堆之后,根节点就是整个堆上最最最大的元素。所以可以做如下操作:
将最大的元素换到屁股后面,这样,最大的元素就找到了它的位置。
-
然后把剩下的元素继续调整成大顶堆。继续换位置。这种重复的感觉,让我不由自主的想到了递归。
-
最终得到有序的堆。
二、抽象
-
首先需要一个调整函数,把乱序的堆调整成大顶堆。
heap_ajust() 函数
-
然后需要swap函数,将堆顶的元素放到屁股后面。
swap函数
-
然后需要把剩下的元素调整成大顶堆,把堆顶元素放到屁股后面。
因为做的操作完全一样,采用递归处理。
-
然后拆解heap_ajust函数。我需要先找到不合法的父节点。然后把这个不合法的父节点下移。和子节点调换。
find_invalid_node_index()函数。
heap_shift_down()函数。把一个节点下移。
-
考虑父节点和子节点的下标
父节点是index
子节点就是2*index+1和 2*index+2
三、实现
-
heap_ajust函数。把数组调整成大顶堆~
int heap_ajust(int *arr,int arr_len) { if(arr == NULL || arr_len <= 1) { return 0; } /*先找到不合法的节点,让它下移,-1表示找不到了,说明这个堆已经OK*/ int index_need_down = find_invalid_node_index(arr,arr_len); if(index_need_down == -1) { return 0; } /*把不合法的节点下移*/ heap_shift_down(arr,arr_len,index_need_down); /*继续做同样的事情,直到所有的节点合法*/ heap_ajust(arr,arr_len); return 0; }
-
heap_sort函数。不断的把最大的元素放到屁股后面,这样得到一个从小到大的数组。
int heap_sort(int *arr,int arr_len) { if(arr_len <= 1) { return 0; } /*先把数组调整成大顶堆*/ heap_ajust(arr,arr_len); /*把第一个和最后一个数字调换*/ swap(&arr[0],&arr[arr_len-1]); /*对剩下的堆元素做同样的事情*/ heap_sort(arr,arr_len-1); return 0; }
四、整理
-
完整代码
#include<stdio.h> int print_arr(int *arr,int arr_len) { if(arr == NULL || arr_len == 0) { return -1; } int i = 0; for(i = 0;i < arr_len;i++) { printf("%d,",arr[i]); } printf("\n"); return 0; } //换值 int swap(int *a,int *b) { int tmp = *a; *a = *b; *b = tmp; return 0; } int compare(int *a,int *b) { if(*a > *b) { return 1; } else if(*a == *b) { return 0; } return -1; } //找到不符合大顶堆规则的父节点下标。 int find_invalid_node_index(int *arr,int arr_len) { if(arr == NULL || arr_len <= 1) { return -1; } int index = 0; for(index = 0;2*index+1 < arr_len;index++) { if(compare(&arr[index],&arr[2*index+1])<0 || (2*index +2 < arr_len && compare(&arr[index],&arr[2*index+2])<0)) { break; } } if(2*index+1 >= arr_len) { index = -1; } return index; } //将某下标节点上移。 int heap_shift_up(int *arr,int arr_len,int index) { return 0; } //将某下标节点下移。 int heap_shift_down(int *arr,int arr_len,int index) { if(arr == NULL || arr_len <=1 || index < 0 || 2*index+1 >= arr_len) { return -1; } /*先获取到两个子节点比较大的那个的下标*/ if(2*index + 2 >= arr_len || (compare(&arr[2*index+1],&arr[2*index+2])>0)) { /*只有父节点比子节点小,才需要交换*/ if(compare(&arr[index],&arr[2*index+1])<0) { swap(&arr[index],&arr[2*index+1]); } } else { if(compare(&arr[index],&arr[2*index+2])<0) { swap(&arr[index],&arr[2*index+2]); } } return 0; } int heap_ajust(int *arr,int arr_len) { if(arr == NULL || arr_len <= 1) { return 0; } /*先找到不合法的节点,让它下移,-1表示找不到了,说明这个堆已经OK*/ int index_need_down = find_invalid_node_index(arr,arr_len); if(index_need_down == -1) { return 0; } /*把不合法的节点下移*/ heap_shift_down(arr,arr_len,index_need_down); /*继续做同样的事情,直到所有的节点合法*/ heap_ajust(arr,arr_len); return 0; } int heap_sort(int *arr,int arr_len) { if(arr_len <= 1) { return 0; } /*先把数组调整成大顶堆*/ heap_ajust(arr,arr_len); /*把第一个和最后一个数字调换*/ swap(&arr[0],&arr[arr_len-1]); /*对剩下的堆元素做同样的事情*/ heap_sort(arr,arr_len-1); return 0; } int main(int argc, const char *argv[]) { int arr[]={1,2,4,3,5}; int arr_len = sizeof(arr)/sizeof(arr[0]); heap_sort(arr,arr_len); print_arr(arr,arr_len); return 0; }
五、方法记忆
-
堆排序,是不断的调整出一个大顶堆,然后将最大的元素不断地放到最后面。
六、参考
- https://www.cnblogs.com/chengxiao/p/6129630.html
- libevent源码