算法——堆排序
Posted cxc1357
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法——堆排序相关的知识,希望对你有一定的参考价值。
Heap.h
1 #include <algorithm> 2 #include <cassert> 3 4 using namespace std; 5 6 template<typename Item> 7 class MaxHeap{ 8 private: 9 Item *data; 10 int count; 11 int capacity; 12 13 // 比较新加入的子节点和父节点大小并交换,直到最顶 14 // O(logn) 15 void shiftUp(int k){ 16 while( k > 1 && data[k/2] < data[k] ){ 17 swap(data[k/2],data[k]); 18 k /= 2; 19 } 20 } 21 22 // 比较两个子节点,和大的交换,直到最底 23 // 复杂度O(logn) 24 void shiftDown(int k){ 25 // 是否有左孩子 26 while( 2*k <= count ){ 27 int j = 2*k; 28 // 是否有右孩子&&右孩子是否比左孩子大 29 if( j+1 <= count && data[j+1] > data[j]) j++; 30 if( data[k] >= data[j]) break; 31 swap(data[k] , data[j]); 32 k = j; 33 } 34 } 35 public: 36 // 构造一个空堆,可容纳capacity个元素 37 // 时间复杂度O(nlogn) 38 MaxHeap(int capacity){ 39 data = new Item[capacity+1]; 40 count = 0; 41 this -> capacity = capacity; 42 } 43 44 // 通过一个给定数组创建最大堆(堆化) 45 // 时间复杂度O(n) 46 MaxHeap(Item arr[], int n){ 47 data = new Item[n+1]; 48 capacity = n; 49 for( int i = 0 ; i < n ; i ++ ) 50 data[i+1] = arr[i]; 51 count = n; 52 53 // 堆的第一个非叶子节点的索引为count/2 54 // 从这个节点开始,逐个shiftDown到最顶 55 for( int i = count/2 ; i >= 1 ; i -- ) 56 shiftDown(i); 57 } 58 ~MaxHeap(){ 59 delete[] data; 60 } 61 // 返回堆中元素个数 62 int size(){ 63 return count; 64 } 65 // 是否为空堆 66 bool isEmpty(){ 67 return count == 0; 68 } 69 // 插入新元素,shiftUp 70 void insert(Item item){ 71 assert( count +1 <= capacity ); 72 data[count+1] = item; 73 shiftUp(count+1); 74 count ++; 75 } 76 // 从最大堆取出堆顶元素,shiftDown 77 Item extractMax(){ 78 assert( count > 0 ); 79 Item ret = data[1]; 80 swap( data[1] , data[count] ); 81 count --; 82 shiftDown(1); 83 return ret; 84 } 85 // 获取最大堆的堆顶元素 86 Item getMax(){ 87 assert( count > 0 ); 88 return data[1]; 89 } 90 };
main.cpp
1 #include <iostream> 2 #include <algorithm> 3 #include "Student.h" 4 #include "Heap.h" 5 #include "SortTestHelper.h" 6 #include "MergeSort.h" 7 #include "QuickSort.h" 8 9 using namespace std; 10 11 12 // 先将元素添加到堆中再取出,即完成排序 13 // 添加和取出的复杂度均为O(nlogn),故整体复杂度为O(nlogn) 14 15 template<typename T> 16 void heapSort1(T arr[], int n){ 17 18 MaxHeap<T> maxheap = MaxHeap<T>(n); 19 for( int i = 0 ; i < n ; i ++ ) 20 maxheap.insert(arr[i]); 21 22 for( int i = n-1 ; i >= 0 ; i--) 23 arr[i] = maxheap.extractMax(); 24 } 25 26 template<typename T> 27 void heapSort2(T arr[], int n){ 28 MaxHeap<T> maxheap = MaxHeap<T>(arr,n); 29 for( int i = n-1 ; i >= 0 ; i--) 30 arr[i] = maxheap.extractMax(); 31 } 32 33 // 优化的shiftDown过程,用赋值取代swap,从0开始索引 34 template<typename T> 35 void __shiftDown2(T arr[], int n, int k){ 36 // k为起始位置 37 T e = arr[k]; 38 // 左孩子不越界 39 while( 2*k+1 < n ){ 40 int j = 2*k+1; 41 // 右孩子比左孩子大 42 if( j + 1 < n && arr[j+1] > arr[j] ) 43 j += 1; 44 if( e >= arr[j] ) break; 45 // 和左、右孩子中较大的交换 46 arr[k] = arr[j]; 47 // k向下传递 48 k = j; 49 } 50 // 将堆头元素放到堆尾 51 arr[k] = e; 52 } 53 54 // 原地堆排序,直接在原数组上进行,不需额外空间 55 template<typename T> 56 void heapSort(T arr[], int n){ 57 // 堆化 58 // 从0开始索引 59 // 第一个非叶子节点索引 = (最后一个元素索引-1)/2 60 for( int i = (n-1-1)/2 ; i >= 0 ; i-- ) 61 __shiftDown2(arr,n,i); 62 // 不断把堆顶元素换到最后,完成排序 63 for( int i = n-1; i>0 ; i -- ){ 64 swap( arr[0] , arr[i] ); 65 __shiftDown2(arr, i, 0); 66 } 67 } 68 69 int main(){ 70 int n = 10000; 71 //int *arr1 = SortTestHelper::generateNearlyOrderedArray(n,10); 72 int *arr1 = SortTestHelper::generateRandomArray(n,0,10); 73 int *arr2 = SortTestHelper::copyIntArray(arr1, n); 74 //SortTestHelper::testSort("Insertion Sort",insertionSort,arr1,n); 75 //SortTestHelper::testSort("Merge Sort",mergeSort,arr2,n); 76 SortTestHelper::testSort("Heap Sort",heapSort,arr1,n); 77 SortTestHelper::testSort("Quick Sort 3",quickSort3,arr2,n); 78 79 delete[] arr1; 80 delete[] arr2; 81 82 return 0; 83 }
- 堆的存储:在数组中,下标从1开始
- shiftUp干了什么:在堆尾部新插入元素后,调整堆以维护堆的定义
- shiftDown干了什么:从堆头取出一个元素后,将堆尾元素放到堆头,调整堆以维护堆的定义
- 直接建堆和堆化的区别:直接建堆是一个个插入,堆化是在现拥有数组的基础上调整数据
- 如何优化:额外用了O(n)的空间,可改为原地排序
以上是关于算法——堆排序的主要内容,如果未能解决你的问题,请参考以下文章