《算法导论》读书笔记之第6章 堆排序
2013-01-23 16:43
429 查看
本章开始介绍了堆的基本概念,然后引入最大堆和最小堆的概念。全章采用最大堆来介绍堆的操作,两个重要的操作是调整最大堆和创建最大堆,接着着两个操作引进了堆排序,最后介绍了采用堆实现优先级队列。
1、堆
堆给人的感觉是一个二叉树,但是其本质是一种数组对象,因为对堆进行操作的时候将堆视为一颗完全二叉树,树种每个节点与数组中的存放该节点值的那个元素对应。所以堆又称为二叉堆,堆与完全二叉树的对应关系如下图所示:
View Code
程序测试结果如下所示:
从结果可以看出按照最大堆进行堆排序最终使得结果是从小到大排序(非递减的)。
堆排序算法时间复杂度:调整堆过程满足递归式T(n)<=T(2n/3)+θ(1),有master定义可以知道T(n) = O(lgn),堆排序过程中执行一个循环,调用最大堆调整函数,总的时间复杂度为O(nlgn)。
5、问题
(1)在创建最大堆的过程中,为什么从最后一个非叶子节点(n/2)开始到第一个非叶子(1)结束,而不是从第一个非叶子节点(1)到最后一个非叶子节点(n/2)结束呢?
我的想法是:如果是从第一个非叶子节点开始创建堆,有可能导致创建的堆不满足堆的性质,使得第一个元素不是最大的。这样做只是使得该节点的和其左右孩子节点满足堆性质,不能确保整个树满足堆的性质。如果最大的节点在叶子节点,那么将可能不会出现在根节点中。例如下面的例子:
从图中可以看出,从第一个非叶子节点开始创建最大堆,最后得到的结果并不是最大堆。而从最后一个非叶子节点开始创建堆时候,能够保证该节点的子树都满足堆的性质,从而自底向上进行调整堆,最终使得满足最大堆的性质。
1、堆
堆给人的感觉是一个二叉树,但是其本质是一种数组对象,因为对堆进行操作的时候将堆视为一颗完全二叉树,树种每个节点与数组中的存放该节点值的那个元素对应。所以堆又称为二叉堆,堆与完全二叉树的对应关系如下图所示:
View Code
#include <stdio.h> #include <stdlib.h> //array's index begins 1,not 0 #define PARENT(i) (i/2) #define LEFT(i) (i*2) #define RIGHT(i) (i*2+1) #define NOTNUSEDATA -65536 void adjust_max_heap(int *datas,int length,int i); void adjust_max_heap_recursive(int *datas,int length,int i); void build_max_heap(int *datas,int length); void heap_sort(int *datas,int length); int main() { int i; //array's index begin 1 int datas[11] = {NOTNUSEDATA,5,3,17,10,84,19,6,22,9,35}; heap_sort(datas,10); for(i=1;i<11;++i) printf("%d ",datas[i]); printf("\n"); exit(0); } void adjust_max_heap_recursive(int *datas,int length,int i) { int left,right,largest; int temp; left = LEFT(i); //left child right = RIGHT(i); //right child //find the largest value among left and rihgt and i. if(left<=length && datas[left] > datas[i]) largest = left; else largest = i; if(right <= length && datas[right] > datas[largest]) largest = right; //exchange i and largest if(largest != i) { temp = datas[i]; datas[i] = datas[largest]; datas[largest] = temp; //recursive call the function,adjust from largest adjust_max_heap(datas,length,largest); } } void adjust_max_heap(int *datas,int length,int i) { int left,right,largest; int temp; while(1) { left = LEFT(i); //left child right = RIGHT(i); //right child //find the largest value among left and rihgt and i. if(left <= length && datas[left] > datas[i]) largest = left; else largest = i; if(right <= length && datas[right] > datas[largest]) largest = right; //exchange i and largest if(largest != i) { temp = datas[i]; datas[i] = datas[largest]; datas[largest] = temp; i = largest; continue; } else break; } } void build_max_heap(int *datas,int length) { int i; //build max heap from the last parent node for(i=length/2;i>0;i--) adjust_max_heap(datas,length,i); } void heap_sort(int *datas,int length) { int i,temp; //bulid max heap build_max_heap(datas,length); i=length; //exchange the first value to the last unitl i=1 while(i>1) { temp = datas[i]; datas[i] = datas[1]; datas[1] =temp; i--; //adjust max heap,make sure the fisrt value is the largest adjust_max_heap(datas,i,1); } }
程序测试结果如下所示:
从结果可以看出按照最大堆进行堆排序最终使得结果是从小到大排序(非递减的)。
堆排序算法时间复杂度:调整堆过程满足递归式T(n)<=T(2n/3)+θ(1),有master定义可以知道T(n) = O(lgn),堆排序过程中执行一个循环,调用最大堆调整函数,总的时间复杂度为O(nlgn)。
5、问题
(1)在创建最大堆的过程中,为什么从最后一个非叶子节点(n/2)开始到第一个非叶子(1)结束,而不是从第一个非叶子节点(1)到最后一个非叶子节点(n/2)结束呢?
我的想法是:如果是从第一个非叶子节点开始创建堆,有可能导致创建的堆不满足堆的性质,使得第一个元素不是最大的。这样做只是使得该节点的和其左右孩子节点满足堆性质,不能确保整个树满足堆的性质。如果最大的节点在叶子节点,那么将可能不会出现在根节点中。例如下面的例子:
从图中可以看出,从第一个非叶子节点开始创建最大堆,最后得到的结果并不是最大堆。而从最后一个非叶子节点开始创建堆时候,能够保证该节点的子树都满足堆的性质,从而自底向上进行调整堆,最终使得满足最大堆的性质。
相关文章推荐
- 《算法导论》读书笔记之第6章 堆排序
- 《算法导论》第6章 堆排序 个人笔记
- 算法导论第6章实现堆排序的完整程序
- 算法导论---第6章---堆排序
- 算法导论第6章代码之堆排序
- 算法导论 第6章 堆排序
- 算法导论第6章 堆排序
- 算法导论代码 第6章 堆排序
- 算法导论 第6章 堆排序
- 《算法导论》 - 第6章 - 堆排序 - 习题解答
- 《算法导论》第6章 堆排序 (3)K路归并
- 算法导论 第6章 堆排序(简单选择排序、堆排序)
- 《算法导论》 - 第6章 - 堆排序 - 习题解答
- 《算法导论》第6章 堆排序 (3)K路归并
- 《算法导论》第6章 堆排序 (4)Young氏矩阵
- 《算法导论》第6章 堆排序 (3)K路归并
- 《算法导论》第6章 堆排序 (4)Young氏矩阵
- 《算法导论》第6章 堆排序 (3)K路归并
- 《算法导论》第6章 堆排序 (4)Young氏矩阵
- 【CLRS】《算法导论》读书笔记(一):堆排序(Heapsort)