1. 堆排序(Heap Sort)
(1)堆:这里所指的堆,一般是一颗完全二叉树,同时满足以下两个性质(以大顶堆为例)
①每个父结点的键值总是大于等于其左右孩子的键值。
②每个结点的左子树和右子树都是一个大顶堆。
(2)基本思想(以大顶堆为例):
将待排序的序列构成一个大顶堆(如图①)。此时,整个序列的最大值就是堆顶的根结点。并将它与堆数组的堆末尾元素交换,在堆末尾形成有序区(最大值)(如图②)。然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值(如图③)。如此反复执行,便能得到一个有序序列。
(3)实例分析
2. 桶排序
(1)基本思想:将要排列的序列分成n组,每组分别进行排序,然后在合并到一起,这里面有分而治之的思想。
(2)基本流程:
①建立一定数量的桶(buckets);
②遍历原始数组,并按一定的算法将数据放入到各自的buckets中(类似于哈希函数);
③对非空的buckets进行排序;(可能使用别的排序算法)
④按照顺序遍历这些buckets并放回到原始数组中即可构成排序后的数组。
(3)实例分析
【编程实验】堆排序(HeapSort)和桶排序算法(BucketSort)
//Sort.h
#ifndef SORT_H #define SORT_H #include "Object.h" namespace DTLib { //结点(for BucketSort) template <typename T> class BNode : public Object { public: T data; BNode<T>* next; public: BNode(){next = NULL;} }; class Sort : public Object { private: //私有化构造函数、赋值、拷贝构造等函数 Sort(); Sort(const Sort&); Sort& operator=(const Sort&); template <typename T> static void Swap(T& a, T& b) { T temp(a); a = b; b = temp; } //两路合并算法中的合并过程 template <typename T> //将两个有序序列array[low...mid]和array[mid+1...high] static void Merge(T src[], T helper[], int low, int mid, int high, bool min2max) { int i = low; int j = mid +1; int k = 0; //按顺序将两个有序序列中较小的依次放入helper中 while( (i<=mid) && (j<=high) ){ if(min2max ? (src[i] < src[j]) : (src[i] > src[j])){ //取较小(大)者放入helper中 helper[k++] = src[i++]; }else{ helper[k++] = src[j++]; } } //将左子序列中剩余部分直接拷贝到helper中 while(i<=mid){ helper[k++] = src[i++]; } //将右子序列中剩余部分直接拷贝到helper中 while(j<=high){ helper[k++] = src[j++]; } //将数据拷贝回array中 for(int i=0; i< k; i++){ src[low+i] = helper[i]; } } //两路合并算法中的分解过程 template <typename T> static void MSort(T src[], T helper[], int low, int high, bool min2max) { int mid = (low + high) /2; if(low < high){ //递归结束条件,只要high>low说明还可以再分解。 //分解为两路 MSort(src, helper, low, mid, min2max); //函数返回后左子序列为有序序列 MSort(src, helper, mid + 1, high, min2max);//函数返回后右子序列为有序序列 //合并 Merge(src, helper, low, mid, high, min2max); } } template <typename T> static int Partition(T array[], int low, int high, bool min2max) { int pivot = low; //基准元素,将数组分为左右两个分区,并将基准放于正确的位置 while(low < high){ //high指针的移动,从右向左找到小于等于pivot的元素 while((low < high) && (min2max ? (array[high] > array[pivot]):(array[high] < array[pivot]))){ high--; } //low指针的移动,从左向右找到大于pivot的元素 while((low < high) && (min2max ? (array[low] <= array[pivot]) : (array[low] >= array[pivot]))){ low++; } Swap(array[low], array[high]); } //将基准元素放入low和high相遇的地方 Swap(array[low], array[pivot]); return low; //返回当前基准的位置 } template <typename T> static void Quick(T array[], int low, int high, bool min2max) { if(low < high){ //递归结束条件 int pvIndex = Partition(array, low, high, min2max); //依基准,分成两个区 Quick(array, low, pvIndex-1, min2max); //左区快排(递归) Quick(array, pvIndex+1, high, min2max); //右区快排(递归) } } //桶排序的映射函数: 计算桶号和散列函数相似 template<typename T> static int getBucketIndex(T value, T min, T max, int len) { int ret = (max == min) ? 0 : (value-min) * len / (max - min); if (value == max) ret = len - 1; return ret; } //桶内的插入排序 template<typename T> static void putIntoBucket(BNode<T>*& bucket, BNode<T>* node,bool min2max) { BNode<T>* tmp = bucket; BNode<T>* prev = bucket; while( tmp && (min2max ? (node->data > tmp->data) : (node->data < tmp->data))){ prev = tmp; tmp = tmp->next; } if(prev == tmp){ bucket = node; }else{ prev->next = node; } node->next = tmp; } //堆排序:返回左孩子结点 static int left(int i) { return (2*i + 1); } //堆排序:返回右孩子 static int right(int i) { return (2 * i + 2); } //堆排序(以某一结点为根的子树做堆调整(保证最大堆性质) template <typename T> static void HeapAdjust(T array[], int i, int heapSize, bool min2max) { int lf = left(i); int rt = right(i); int largest; T tmp; //比较父结点和左孩子(并将较大者的索引记录下来) if(lf < heapSize && (min2max ? (array[lf] > array[i]) : (array[lf] < array[i]))){ largest = lf; }else{ largest = i; } //比较父结点和右孩子(并将较大者的索引记录下来) if(rt < heapSize && (min2max ? (array[rt] > array[largest]) :(array[rt] < array[largest]) )){ largest = rt; } //找到父结点与左右孩子三个结点中的最大/小值 if(largest != i){ //将最大/小值与父结点交换 tmp = array[i]; array[i] = array[largest]; array[largest] = tmp; //交换后导致左右孩子的数值发生变化,对有变化结点(如左孩子)的子树进一步做堆调整。 HeapAdjust(array, largest, heapSize, min2max); } } //堆排序:创建最大堆 template <typename T> static void BuildHeap(T array[], int len, bool min2max) { //根据结定的数据创建最大堆(不断进行堆调整) for(int i=(len-2)/2; i>=0; i--){ //注意:从后向前创建堆。 //叶子结点无左右孩子,不必调整。所以 //i=(len-2)/2开始,跳过所有叶子结点。 HeapAdjust(array, i, len, min2max); //对以结点i的子树进行堆调整。 } } public: //选择排序(O(n*n),不稳定) template <typename T> static void Select(T array[], int len, bool min2max = true) { for(int i=0; i<len-1; i++){ //第i趟。有序区为[0,i-1],无序区为[i, len-1] //1.找到无序区的首元素: array[i] //2.从无序区中找到最小值 int min = i; //假设:无序区中最小元素的为首元素 for(int j=i+1; j<len; j++){ if (min2max ? (array[j] < array[min]) : (array[j] > array[min])){ min = j; } } //3. 交换无序区中首元素和最小值的位置,将最小值放在无序区的首部 if(i != min){ Swap(array[i], array[min]); } } } //插入排序(O(n*n), 稳定) template <typename T> static void Insert(T array[], int len, bool min2max = true) { //1. 初始状态有序区为[0],无序区为[1..n-1] for(int i=1; i<len; i++){ //注意,从1开始,因为要将无序区的每个元素插入到有序区中 //有序区[0, i-1],无序区[i, n-1] //2. 取出无序区首元素,从有序区后面往前面比较依次与其比较 T e = array[i]; //无序区首元素 int k = i; //k记录着e最终应该放置的位置 for(int j=i-1; (j>=0) && (min2max ? (e<array[j]) : (e>array[j]));j--){ //3. 边比较边移动元素,e最终的位置记录在k中 array[j+1] = array[j]; //将a[j]往后移一位 k = j; } //4. 将e放置下来 if(k != i){ array[k] = e; } } } //冒泡排序: O(n*n), 稳定 template <typename T> static void Bubble(T array[], int len, bool min2max = true) { bool exchange = true; //如果无序区元素己排好序,则exchange为false, //否则当发生元素交换时,说明仍是无序的 //1. 初始状态:有序区[],无序区[0..n-1] for(int i=0; (i<len) && exchange; i++){ exchange = false; //2. 从无序的区最后面开始,让小的元素向上冒 for(int j=len-1; j>i; j--){ if(min2max ? (array[j]<array[j-1]) : (array[j]>array[j-1])){ //发生逆序,则交换 Swap(array[j], array[j-1]); exchange = true; } } } } //希尔排序 template <typename T> static void Shell(T array[], int len, bool min2max = true) { int d = len; //增量 do { d = d/3 + 1; //增量序列,要保证每次d增量是递减的,且最终为1 //分成d个组(子序列),再对各个子序列进行插入排序: //1. 注意插入排序会将每个序列内各自的第1个元素视为有序区。各个子序列详情如下: //{R[0], R[0+d], R[0+2d],…,R[0+kd]} //第0个序列:有序区[0],无序区[0+d, 0+kd] //{R[1], R[1+d], R[1+2d],…,R[1+kd]} //第1个序列:有序区[1],无序区[1+d, 1+kd] //{R[2], R[2+d], R[2+2d],…,R[2+kd]} //第2个序列:有序区[2],无序区[2+d, 2+kd] //... //{R[d-1], R[(d-1)+d], ,…,R[(d-1)+kd]} //第d个序列:有序区[d-1],无序区[(d-1)+d, (d-1)+kd] //2. 以下是经过优化的代码,其思路是交替处理每个组,而不是处理完一组后再处理下一组。 //i的意义:表示第(i % d)个子序列无序区首元素的位置。 for(int i=d; i<len; i++){ //选择无序区中的第1个元素,第1个被选中的是第0个序列的无序区首元素array[d] //(1)i每次自增,会取出第(i % d)个序列中无序区的首元素,然后对该组再进行组内插入排序。 //(注意,经过组内排序后该组的有序区扩大,因此交替处理分组后,当重新轮到该组时其无序区的缩小了,当然 //首元素位置也发生了变化) //(2)由于i表示的是该组无序区首元素,所以i-d即有该组有序区的最后一个元素,从这元素开始向0方向依次比较 //该组内的各个元素并进行插入操作 int k = i; //应插入的位置 T e = array[i]; //取出该组无序区首元素 for(int j=i-d; (j>=0) && (min2max ? (array[j] > e) : (array[j] < e)); j-=d){ //i为无序区首元素,i-d为有序区的最后一个元素 array[j+d] = array[j]; //元素后移 k = j; } if(k != i){ array[k] = e; } } }while (d>1); } //归并排序 template <typename T> static void Merge(T array[], int len, bool min2max = true) { //申请一片与待排数据大小一样的空间 T* helper = new T[len]; if(helper != NULL){ MSort(array, helper, 0, len-1, min2max); } delete[] helper; } //快速排序 template <typename T> static void Quick(T array[], int len, bool min2max = true) { Quick(array, 0, len-1, min2max); } //桶排序 template <typename T> static void Bucket(T array[], int len, bool min2max = true) { //创建桶(Buckets) BNode<T>** buckets = new BNode<T>*[len]; //初始化Buckets,同时找到数组中的最大/小值 T min = array[0]; T max = array[0]; for(int i=0; i<len; i++){ buckets[i] = NULL; if(array[i] > max) max = array[i]; if(array[i] < min) min = array[i]; } //将数据放入桶中 for(int i=0; i<len; i++){ int pos = getBucketIndex(array[i], min, max, len); if(0<=pos && pos <len){ BNode<T>* curr = new BNode<T>(); curr->data = array[i]; curr->next = NULL; putIntoBucket(buckets[pos], curr, min2max); //放入相应的桶中并做插入排序 } } //将桶中的数据放回原数组中去 if(min2max){ for(int i=0, j = 0; i<len; i++){ BNode<T>* node = buckets[i]; while(node){ array[j++] = node->data; node = node->next; } } }else{ for(int i=len-1, j = 0; i>=0; i--){ BNode<T>* node = buckets[i]; while(node){ array[j++] = node->data; node = node->next; } } } //删除各个桶 for(int i=0; i<len; i++){ BNode<T>* node = buckets[i]; while(node){ BNode<T>* tmp = node->next; delete node; node = tmp; } } delete[] buckets; } //堆排序 template <typename T> static void Heap(T array[], int len, bool min2max = true) { BuildHeap(array, len, min2max); T tmp; for(int i=len-1; i>=0; i--){ tmp = array[0]; array[0] = array[i]; array[i] = tmp; HeapAdjust(array, 0, i, min2max); } } }; } #endif // SORT_H
//main.cpp
#include <iostream> #include "Sort.h" using namespace std; using namespace DTLib; int main() { //int array[]={49,38,65,97,76,13,49,27}; int array[]={78,17,39,26,72,94,21,12,23,68}; int len = sizeof(array)/sizeof(*array); Sort::Heap(array, len, false);//降序排序 for(int i=0; i<len; i++){ cout << array[i] << " "; } cout << endl; return 0; } /*测试结果 94 78 72 68 39 26 23 21 17 12 */