C++堆排序
---恢复内容开始---
本文通过C++实现了堆排序,通过构建最大堆对数组进行从小到大的排序,通过构建最小堆进行从大到小的排序
堆排序定义:
是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足。
堆排序性质:
即子结点的键值或索引总是小于(或者大于)它的父节点。
最大堆和最小堆:是二叉堆的两种形式
最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。
堆排序相关的具体理论请参照其他博文,下面给出具体代码和思想:
1. 构建最大堆:
通过该函数对节点i构成的子树构建最大堆
// arr[]为输入的数组,n为数组长度,i为节点的序号
void heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2*i + 1; // left = 2*i + 1 int r = 2*i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { std::swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } }
例如,通过下面的测试测试程序可以实现最大堆的构建
#include <iostream> void heapify(int arr[], int n, int i); void printArray(int arr[], int n); int main(int argc, char **argv) { const int n = 5; int a[n] = {4,10,3,5,1}; std::cout << "Before: "; printArray(a,n); heapify(a,n,0); std::cout << " After: "; printArray(a,n); return 0; } void heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2*i + 1; // left = 2*i + 1 int r = 2*i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { std::swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } } void printArray(int arr[], int n) { for(int i = 0; i < n; i++) { if(arr[i] != 0) std::cout << arr[i] << " "; else std::cout << "Null" << " "; } std::cout << std::endl; }
输出:
Before: 4 10 3 5 1 After: 10 5 3 4 1
即实现了最大堆排列。还有一种可以减少计算次数的方法实现堆排列,思想是先对较小的子树进行堆排列,再对较大的子树进行堆排列,可以写为下列函数
void MaxHeap(int arr[], int n) { for(int i = n / 2 - 1; i >= 0; i--) { heapify(arr,n,i); } }
通过下面的测试测试程序同样可以实现最大堆的构建
#include <iostream> void heapify(int arr[], int n, int i); void MaxHeap(int arr[], int n); void printArray(int arr[], int n); int main(int argc, char **argv) { const int n = 5; int a[n] = {4,10,3,5,1}; std::cout << "Before: "; printArray(a,n); MaxHeap(a,n); std::cout << " After: "; printArray(a,n); return 0; } void heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2*i + 1; // left = 2*i + 1 int r = 2*i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { std::swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } } void MaxHeap(int arr[], int n) { for(int i = n / 2 - 1; i >= 0; i--) { heapify(arr,n,i); } } void printArray(int arr[], int n) { for(int i = 0; i < n; i++) { if(arr[i] != 0) std::cout << arr[i] << " "; else std::cout << "Null" << " "; } std::cout << std::endl; }
2. 堆排序(升序排列):
思想:1. 根据输入数据构建最大堆
2. 此时,最大的项在最大堆的顶端,将最大项与堆的最后一项交换,将其从堆中删除作为已排列好的数
3. 将堆的大小减一,再次构建最大堆,重复上一步的操作,即将每次构建最大堆后的根往后放,最终实现排序
通过下面的函数实现堆排序
void HeapSort(int arr[], int n) { MaxHeap(arr,n); for(int i = n - 1; i >= 0; i--) { std::swap(arr[0],arr[i]); heapify(arr,i,0); } }
测试代码:
#include <iostream> void heapify(int arr[], int n, int i); void MaxHeap(int arr[], int n); void HeapSort(int arr[], int n); void printArray(int arr[], int n); int main(int argc, char **argv) { const int n = 5; int a[n] = {4,10,3,5,1}; std::cout << "Before: "; printArray(a,n); HeapSort(a,n); std::cout << " After: "; printArray(a,n); return 0; } void heapify(int arr[], int n, int i) { int largest = i; // Initialize largest as root int l = 2*i + 1; // left = 2*i + 1 int r = 2*i + 2; // right = 2*i + 2 // If left child is larger than root if (l < n && arr[l] > arr[largest]) largest = l; // If right child is larger than largest so far if (r < n && arr[r] > arr[largest]) largest = r; // If largest is not root if (largest != i) { std::swap(arr[i], arr[largest]); // Recursively heapify the affected sub-tree heapify(arr, n, largest); } } void MaxHeap(int arr[], int n) { for(int i = n / 2 - 1; i >= 0; i--) { heapify(arr,n,i); } } void HeapSort(int arr[], int n) { MaxHeap(arr,n); for(int i = n - 1; i >= 0; i--) { std::swap(arr[0],arr[i]); heapify(arr,i,0); } } void printArray(int arr[], int n) { for(int i = 0; i < n; i++) { if(arr[i] != 0) std::cout << arr[i] << " "; else std::cout << "Null" << " "; } std::cout << std::endl; }
输出:
Before: 4 10 3 5 1 After: 1 3 4 5 10
可见实现了对数组的升序排列
3. 构建最小堆
构建最小堆的思想和构建最大堆基本相同,程序上也没什么区别,只是条件判断上有点不同,下面看代码
void MinHeapify(int arr[], int n, int i) { int minimum = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < n && arr[left] < arr[minimum]) { minimum = left; } if(right < n && arr[right] < arr[minimum]) { minimum = right; } if(minimum != i) { std::swap(arr[minimum],arr[i]); MinHeapify(arr,n,minimum); } }
4. 堆排序(降序排列)
程序思想和利用最大堆进行升序排列一样,这里我们只是用了最小堆就可以实现降序排列,程序如下
void HeapSort(int arr[], int n) { MinHeapify(arr,n,0); for(int i = n - 1; i >= 0; i--) { std::swap(arr[0],arr[i]); MinHeapify(arr,i,0); } }
5. 既能升序也能进行降序的堆排列
我们进一步完善代码,通过函数指针的方式,将构建最大堆和最小堆的函数作为参数传入排列函数,当传入构建最大堆的函数时,完成升序排列,当传入构建最小堆的函数时,完成降序排列,下面直接给出完整代码
#include <iostream> void printArray(int arr[], int n); void MaxHeapify(int arr[], int n, int i); void MinHeapify(int arr[], int n, int i); void HeapSort(int arr[], int n, void (*pf)(int [], int, int)); int main() { const int n = 6; int a[n] = {9,79,46,30,58,49}; int b[n] = {9,79,46,30,58,49}; std::cout << "升序排列:\n"; std::cout << "\tBefore: "; printArray(a,n); HeapSort(a,n,MaxHeapify); std::cout << "\t After: "; printArray(a,n); std::cout << "降序排列:\n"; std::cout << "\tBefore: "; printArray(b,n); HeapSort(b,n,MinHeapify); std::cout << "\t After: "; printArray(b,n); return 0; } //函数功能: 构建最大堆 void MaxHeapify(int arr[], int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2; if(left < n && arr[left] > arr[largest]) { largest = left; } if(right < n && arr[right] > arr[largest]) { largest = right; } if(largest != i) { std::swap(arr[largest],arr[i]); MaxHeapify(arr,n,largest); } } //函数功能: 构建最小堆 void MinHeapify(int arr[], int n, int i) { int minimum = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < n && arr[left] < arr[minimum]) { minimum = left; } if(right < n && arr[right] < arr[minimum]) { minimum = right; } if(minimum != i) { std::swap(arr[minimum],arr[i]); MinHeapify(arr,n,minimum); } } //函数功能: 堆排列函数,可完成升序排列(MaxHeapify)和降序排列(MinHeapify) void HeapSort(int arr[], int n, void (*pf)(int [], int, int)) { (*pf)(arr,n,0); for(int i = n - 1; i >= 0; i--) { std::swap(arr[0],arr[i]); (*pf)(arr,i,0); } } //函数功能: 打印数组 void printArray(int arr[], int n) { for(int i = 0; i < n; i++) { if(arr[i] != 0) std::cout << arr[i] << " "; else std::cout << "Null" << " "; } std::cout << std::endl; }
输出:
升序排列: Before: 9 79 46 30 58 49 After: 9 30 46 49 58 79 降序排列: Before: 9 79 46 30 58 49 After: 79 58 30 49 46 9
---恢复内容结束---