Heap & Heap Sort
1.堆及其性质
1.1 定义
堆其实是一棵有着特殊性质的完全二叉树,所以而可以成为二叉堆
这里的特殊性质是指:
1、父节点的键值总是不大于(或不小于)其左右孩子的值;
2、每个节点的左右子树都是一个二叉堆。」」
1.2 分类
父节点的键值不小于其左右孩子的键值的堆为大顶堆
父节点的键值不大于其左右孩子的键值的堆为小顶堆
1.3 性质
我们一般用数组构建堆,并且从数组的a[0]用来存放堆的大小,从a[1]开始存放节点,以下除特殊说明外默认都是这样
- 含有n个节点的堆的高度为 ⌊lgn⌋
- a[1]根节点
- 叶节点为a[(n/2)+1] ~ a[n]
- 最大的非叶结点为a[n/2]
- 对于除根节点外的所有节点a[i]的父节点为a[i/2]
- 对于除叶节点外的所有节点a[i]的左孩子节点为a[2i],右节点(如果有)为a[2i+1]
2.堆的基本操作
2.1 插入元素
将一个新的元素插入到堆中时,首先先将待插入元素放在数组最后
由于从这个新数据的父结点到根结点必然为一个有序的序列,现在的任务是将这个新数据插入到这个有序序列中——这就类似于直接插入排序中将一个数据并入到有序区间中
2.2 删除元素
按定义,堆中每次都只能删除堆顶元素。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。
调整时先在左右儿子结点中找最大的,如果父结点比这个最大的子结点还大说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程
2.3 建堆
将无序数组建成一个大顶堆的过程就是一个反复调整的过程。
若将此数组看成是一个完全二叉树,则最后一个非终端结点是第n/2个结点,由此调整过程只需从该结点开始,直到堆顶元素。
2.4 代码实现
1 // Author : Jincheng 2 // Date : 170319 3 // Description : Insert,Delete,Create 4 // Complexity : Time 0(nlgn) Space O(1) 5 6 #include <iostream> 7 using namespace std; 8 9 10 template <typename T> 11 void Print(T *a,int size); 12 13 // 调整堆,使以a[bottom]为“叶节点”的“最大树”成为大顶堆 14 template <typename T> 15 void HeapAdjustup(T *a,int bottom) 16 { 17 T temp = a[bottom]; // 保存数据 18 int parent = bottom/2; // 用来访问父节点 19 20 while(parent >= 1) 21 { 22 if(temp <= a[parent]) // 如果待插入节点小于当前节点的父节点,那么构成最大堆,无需继续调整 23 24 break; 25 26 a[bottom] = a[parent]; // 将父节点下移,替换当前节点 27 bottom = parent; 28 parent = bottom/2; 29 } 30 a[bottom] = temp; 31 } 32 33 34 template <typename T> 35 void HeapInsert(T *a,T data,int size) 36 { 37 a[size+1] = data; 38 HeapAdjustup(a,size+1); 39 } 40 41 // 已知a[top]的左右子树均为大顶堆 42 // 调整堆,使以a[top]为根节点的树使其成为大顶堆 43 template <typename T> 44 void HeapAdjustdown(T *a,int top,int size) 45 { 46 T temp = a[top]; 47 int child = top*2; 48 while(child <= size) 49 { 50 if(child+1 <= size && a[child+1]>a[child]) 51 { 52 child++; 53 } 54 if(temp >= a[child]) 55 { 56 break; 57 } 58 a[top] = a[child]; 59 top = child; 60 child = top*2; 61 } 62 a[top] = temp; 63 } 64 65 template <typename T> 66 void HeapDelete(T *a,int size) 67 { 68 a[1] = a[size]; 69 HeapAdjustdown(a,1,size-1); 70 } 71 72 template <typename T> 73 void CreatHeap(T *a,int size) 74 { 75 // 将原数组构建成为最大推 76 // 从第一个非叶节点开始调整 77 for(int i = size/2;i >= 1;i--) 78 { 79 HeapAdjustdown(a,i,size); 80 } 81 } 82 int main() 83 { 84 int a[7] = {5,2,5,1,6,3}; 85 Print(a,5); 86 CreatHeap(a,5); 87 Print(a,5); 88 HeapInsert(a,10,5); 89 Print(a,6); 90 HeapDelete(a,6); 91 Print(a,5); 92 return 0; 93 } 94 95 template <typename T> 96 void Print(T *a,int size) 97 { 98 for(int i=1;i <= size;i++) 99 100 cout << "a[" << i << "] = " << a[i] << endl; 101 }
3.堆排序
3.1 算法思想
堆排序本质是一种选择排序,是一种树形选择排序
在直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,从R[1...n-2]中选择最大记录需比较n-2次
事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数
堆排序的基本思想为:
1)将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。
4)不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成
操作过程如下:
1)初始化堆:将R[1..n]构造为堆;
2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
3.2 算法实现
3.2.1 非递归版本的实现
1 // Author : Jincheng 2 // Date : 170318 3 // Description : HeapSort non—recrusive method 4 // Complexity : Time 0(nlgn) Space O(1) 5 6 #include <iostream> 7 using namespace std; 8 9 template <typename T> 10 void Swap(T *a,T *b); 11 12 template <typename T> 13 void Print(T *a,int size); 14 15 // 调整以start的为根节点的子树使其成为最大堆 16 template <typename T> 17 void HeapAdjustdown(T *a,int start,int end) 18 { 19 T temp = a[start]; // 保存待调整节点 20 int j = 2*start; // 用来访问孩子节点 21 while(j <= end) 22 { 23 if(a[j] < a[j+1] && j+1 <= end) // 取左右孩子节点中较大的一个 24 { 25 j++; 26 } 27 if(temp >= a[j]) // 如果大于当前父节点的孩子节点,那么构成最大堆,无需继续调整 28 29 break; 30 31 a[start] = a[j]; // 将较大的孩子节点上移,替换父节点 32 start = j; 33 j = j*2; 34 } 35 a[start] = temp; 36 } 37 38 template <typename T> 39 void HeapSort(T *a,int size) 40 { 41 // 将原数组构建成为最大推 42 // 从第一个非叶节点开始调整 43 for(int i = size/2;i >= 1;i--) 44 { 45 HeapAdjustdown(a,i,size); 46 } 47 // 将根节点和最后一个元素交换,使最后一个节点成为最大值 48 // 交换后调整a[1,i-1],使其成为最大堆 49 for(int i = size;i > 1;i--) 50 { 51 Swap(&a[1],&a[i]); 52 HeapAdjustdown(a,1,i-1); 53 } 54 55 } 56 int main() 57 { 58 int a[] = {5,2,5,1,6,3}; 59 int size = sizeof(a)/sizeof(int) - 1; 60 Print(a,size); 61 HeapSort(a,size); 62 Print(a,size); 63 return 0; 64 } 65 66 template <typename T> 67 void Print(T *a,int size) 68 { 69 for(int i=1;i <= size;i++) 70 71 cout << "a[" << i << "] = " << a[i] << endl; 72 } 73 74 template <typename T> 75 void Swap(T *a,T *b) 76 { 77 T temp = *a; 78 *a = *b; 79 *b = temp; 80 }
3.2.2 递归版本的实现
1 // Author : Jincheng 2 // Date : 170318 3 // Description : HeapSort non—recrusive method 4 // Complexity : Time 0(nlgn) Space O(n) 5 6 #include <iostream> 7 using namespace std; 8 9 template <typename T> 10 void Swap(T *a,T *b); 11 12 template <typename T> 13 void Print(T *a,int size); 14 15 // 调整以start的为根节点的子树使其成为最大堆 16 template <typename T> 17 void HeapAdjust(T *a,int start,int end) 18 { 19 int j = 2*start; // 用来访问孩子节点 20 21 if(j <= end) 22 { 23 if(a[j] < a[j+1] && j+1 <= end) // 取左右孩子节点中较大的一个 24 { 25 j++; 26 } 27 if(a[start] >= a[j]) // 如果大于当前父节点的孩子节点,那么构成最大堆,无需继续调整 28 29 return; 30 31 // 将待调整节点与较大的孩子节点交换 32 Swap(&a[start],&a[j]); 33 34 // 递归的调整以j为根节点的子树 35 HeapAdjust(a,j,end); 36 37 } 38 39 } 40 41 template <typename T> 42 void HeapSort(T *a,int size) 43 { 44 // 将原数组构建成为最大推 45 // 从第一个非叶节点开始调整 46 for(int i = size/2;i >= 1;i--) 47 { 48 HeapAdjust(a,i,size); 49 } 50 // 将根节点和最后一个元素交换,使最后一个节点成为最大值 51 // 交换后调整a[1,i-1],使其成为最大堆 52 for(int i = size;i > 1;i--) 53 { 54 Swap(&a[1],&a[i]); 55 HeapAdjust(a,1,i-1); 56 } 57 58 } 59 int main() 60 { 61 int a[] = {5,2,5,1,6,3}; 62 int size = sizeof(a)/sizeof(int) - 1; 63 Print(a,size); 64 HeapSort(a,size); 65 Print(a,size); 66 return 0; 67 } 68 69 template <typename T> 70 void Print(T *a,int size) 71 { 72 for(int i=1;i <= size;i++) 73 74 cout << "a[" << i << "] = " << a[i] << endl; 75 } 76 77 template <typename T> 78 void Swap(T *a,T *b) 79 { 80 T temp = *a; 81 *a = *b; 82 *b = temp; 83 }
浙公网安备 33010602011771号