上一篇博客中的堆排序需要先将数组中的元素放入堆中,然后再从堆中取出来,整个操作开辟了n个额外元素的堆,其实也可以将数组看做是一个堆,如下图
(1)最大堆的第一个元素
v
v
v 是最大值,根据排序需要将最大元素放入数组中的最后一个位置
w
w
w 处,因此交换
v
v
v 和
w
w
w 的位置
(2)此时已经将
v
v
v 放到排好序的位置,但是左边的橙色部分就不是个最大堆了
(3)需要将其恢复为最大堆,使用上篇的ShiftDown操作就可将左边部分恢复为最大堆,此时最左边的元素又是最大值,因此将其与倒数第二个元素交换位置,以此类推
此时整个算法的空间复杂度是O(1),由于使用数组进行堆排序,数组一般索引从0开始,所以就需要将上篇博客中从1开始设定的一些操作改为从0开始,如下图
在heapify中最后一个非叶子节点的索引变为
程序实现如下
//heapSorting.cpp
#include <iostream>
#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_
#ifndef _HEAP_H_
#define _HEAP_H_
#include "Heap.h"
#endif // _HEAP_H_
#include "MergeSorting.h"
#include "quickSorting.h"
#include "heapSorting12.h"
using namespace std;
template<typename T>
void __shiftDown(T arr[], int n, int k){
while (2*k + 1 < n){
int j = 2*k + 1;
if (arr[j] < arr[j+1] && j + 1 < n)
j += 1;
if (arr[k] >= arr[j])
break;
swap(arr[j], arr[k]);
k = j;
}
}
//原地堆排序
template<typename T>
void heapSorting(T arr[], int n){
for (int i = (n-1)/2; i >=0; i--)
__shiftDown(arr, n, i);
for (int i = n - 1; i > 0; i --){
swap(arr[0], arr[i]);
__shiftDown(arr, i, 0);
}
}
int main()
{
//对普通的随机数组进行排序
int n = 500000;
int *arr = generateRandomArray(n, 0, n);
int *arr2 = copyIntArray(arr, n);
int *arr3 = copyIntArray(arr, n);
int *arr4 = copyIntArray(arr, n);
int *arr5 = copyIntArray(arr, n);
cout<<"普通数组进行排序"<<endl;
testSorting("MergeSorting", MergeSorting, arr, n);
testSorting("quickSorting2", quickSorting2, arr2, n);
testSorting("heapSorting1", heapSorting1, arr3, n);
testSorting("heapSorting2", heapSorting2, arr4, n);
testSorting("heapSorting", heapSorting, arr5, n);
delete[] arr;//最后删除数组开辟的空间
delete[] arr2;
delete[] arr3;
delete[] arr4;
delete[] arr5;
//对近乎有序的数组进行排序
arr = generateNearlyOrderedArray(n, 100);//生成只有200个无序元素的数组
arr2 = copyIntArray(arr, n);
arr3 = copyIntArray(arr, n);
arr4 = copyIntArray(arr, n);
arr5 = copyIntArray(arr, n);
cout<<"近乎有序的数组进行排序"<<endl;
testSorting("MergeSorting", MergeSorting, arr, n);
testSorting("quickSorting2", quickSorting2, arr2, n);
testSorting("heapSorting1", heapSorting1, arr3, n);
testSorting("heapSorting2", heapSorting2, arr4, n);
testSorting("heapSorting", heapSorting, arr5, n);
delete[] arr;//最后删除数组开辟的空间
delete[] arr2;
delete[] arr3;
delete[] arr4;
delete[] arr5;
//对有大量重复元素的数组进行排序
arr = generateRandomArray(n, 0, 10);
arr2 = copyIntArray(arr, n);
arr3 = copyIntArray(arr, n);
arr4 = copyIntArray(arr, n);
arr5 = copyIntArray(arr, n);
cout<<"有大量重复元素的数组进行排序"<<endl;
testSorting("MergeSorting", MergeSorting, arr, n);
testSorting("quickSorting2", quickSorting2, arr2, n);
testSorting("heapSorting1", heapSorting1, arr3, n);
testSorting("heapSorting2", heapSorting2, arr4, n);
testSorting("heapSorting", heapSorting, arr5, n);
delete[] arr;//最后删除数组开辟的空间
delete[] arr2;
delete[] arr3;
delete[] arr4;
delete[] arr5;
return 0;
}
输出为
可以看出运行时间要比前两个堆排序要段
程序中调用的
"SortingHelp.h"
“Heap.h”
“heapSorting12.h”
“MergeSorting.h”
"quickSorting.h"
分别为
也可见博客 SortingHelp.h
//SortingHelp.h
#include <iostream>
#include <ctime> //time()函数
#include <cstdlib> //rand()函数
#include <cassert> //assert()函数
using namespace std;
int* generateRandomArray(int n, int rangeL, int rangeR){//生成随机数组
assert(rangeL < rangeR);
int *arr = new int[n];
srand(time(NULL));
for (int i = 0; i < n; i++){
arr[i] = rand() % (rangeR - rangeL + 1) + rangeL;
}
return arr;
}
int* generateNearlyOrderedArray(int n, int swapTimes){//生成近乎有序的数组
int *arr = new int[n];
for (int i = 0; i < n; i++){
arr[i] = i;
}
srand(time(NULL));
for (int i = 0; i < swapTimes; i++){
int posx = rand() % n;
int posy = rand() % n;
swap(arr[posx], arr[posy]);
}
return arr;
}
template<typename T>
void printArray(T arr[], int n){//打印数组元素
for (int i = 0; i < n; i ++){
cout<<arr[i]<<" ";
}
cout<<endl; //换行
return;
}
template<typename T>
bool isSorted(T arr[], int n){//测试排序算法是否正确
for (int i = 0; i < n - 1; i++){
if (arr[i] > arr[i + 1])
return false;
}
return true;
}
template<typename T>
void testSorting(string sortName, void(*sorting)(T[], int), T arr[], int n){
//第二个参数是传入排序函数的指针
clock_t startClock = clock();
sorting(arr, n);
clock_t endClock = clock();
assert(isSorted(arr, n));
cout<<sortName<<" : "<<double(endClock-startClock)/CLOCKS_PER_SEC<<" s"<<endl;
return;
}
int* copyIntArray(int arr[], int n){
int* arr2 = new int[n];
copy(arr, arr+n, arr2);
return arr2;
}
template<typename T> //定义模板类型,使对各种数据类型都适用,如double,float,string
void SelectionSorting(T a[], int n){//选择排序算法
for (int i = 0; i < n; i++){
int minIndex = i;
for (int j = i + 1; j < n; j++){
if (a[j] < a[minIndex])
minIndex = j;
}
swap(a[i], a[minIndex]);
}
}
template<typename T>
void InsertionSortingImproved(T arr[], int n){
for (int i = 0; i < n - 1; i++){
T temp = arr[i+1];
int j;
for (j = i + 1; j > 0; j--){
if (arr[j-1] > temp){
arr[j] = arr[j-1];
}
else{
break;
}
}
arr[j] = temp;
}
return;
}
//Heap.h
#include <iostream>
#include <algorithm>
#include <string>
#include <ctime>
#include <cmath>
#include <cassert>
#include <typeinfo>
//#ifndef _SORTINGHELP_H_
//#define _SORTINGHELP_H_
//#include "SortingHelp.h"
//#endif // _SORTINGHELP_H_
//
//#include "MergeSorting.h"
//#include "quickSorting.h"
using namespace std;
template<typename Item>
class MaxHeap{
private:
Item *data;
int count;
int capacity;
void ShiftUp(int k){
while (data[k/2] < data[k] && k > 1){
swap(data[k/2], data[k]);
k /= 2;
}
}
void ShiftDown(int k){
while (k <= count/2){
int j = 2*k; //此轮循环中,data[k]和data[j]交换位置
if (data[j] < data[j+1] && j + 1 <= count)
j += 1;
if (data[k] >= data[j])
break;
swap(data[j], data[k]);
k = j;
}
}
public:
MaxHeap(int capacity){
data = new Item[capacity + 1];
count = 0;
this->capacity = capacity;
}
MaxHeap(Item arr[], int n){
data = new Item[n+1];
capacity = n;
for (int i = 0; i < n; i ++)
data[i+1] = arr[i];
count = n;
for (int i = count / 2; i >= 1; i--)
ShiftDown(i);
}
~MaxHeap(){
delete[] data;
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
void insert(Item item){
assert( count + 1 <= capacity );
data[count+1] = item;
count ++;
ShiftUp(count);
}
Item extractMax(){
assert( count > 0);
Item ret = data[1];
swap(data[1], data[count]);
count --;
ShiftDown(1);
return ret;
}
public:
void testPrint(){
if( size() >= 100 ){
cout<<"Fancy print can only work for less than 100 int";
return;
}
if( typeid(Item) != typeid(int) ){
cout <<"Fancy print can only work for int item";
return;
}
cout<<"The Heap size is: "<<size()<<endl;
cout<<"data in heap: ";
for( int i = 1 ; i <= size() ; i ++ )
cout<<data[i]<<" ";
cout<<endl;
cout<<endl;
int n = size();
int max_level = 0;
int number_per_level = 1;
while( n > 0 ) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}
int max_level_number = int(pow(2, max_level-1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for( int level = 0 ; level < max_level ; level ++ ){
string line1 = string(max_level_number*3-1, ' ');
int cur_level_number = min(count-int(pow(2,level))+1,int(pow(2,level)));
bool isLeft = true;
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index ++ , index_cur_level ++ ){
putNumberInLine( data[index] , line1 , index_cur_level , cur_tree_max_level_number*3-1 , isLeft );
isLeft = !isLeft;
}
cout<<line1<<endl;
if( level == max_level - 1 )
break;
string line2 = string(max_level_number*3-1, ' ');
for( int index_cur_level = 0 ; index_cur_level < cur_level_number ; index_cur_level ++ )
putBranchInLine( line2 , index_cur_level , cur_tree_max_level_number*3-1 );
cout<<line2<<endl;
cur_tree_max_level_number /= 2;
}
}
private:
void putNumberInLine( int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft){
int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width+1) + sub_tree_width;
assert(offset + 1 < line.size());
if( num >= 10 ) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else{
if( isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
void putBranchInLine( string &line, int index_cur_level, int cur_tree_width){
int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width+1) + sub_sub_tree_width;
assert( offset_left + 1 < line.size() );
int offset_right = index_cur_level * (cur_tree_width+1) + sub_tree_width + 1 + sub_sub_tree_width;
assert( offset_right < line.size() );
line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
};
//heapSorting12.h
#include <iostream>
#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_
//#include "MergeSorting.h"
//#include "quickSorting.h"
//#include "Heap.h"
#ifndef _HEAP_H_
#define _HEAP_H_
#include "Heap.h"
#endif // _HEAP_H_
using namespace std;
template<typename T>
void heapSorting1(T arr[], int n){
MaxHeap<T> maxheap = MaxHeap<T>(n);
for (int i = 0; i < n; i ++)
maxheap.insert(arr[i]);
for (int i = n - 1; i >=0; i--)
arr[i] = maxheap.extractMax();
}
//Heapify
template<typename T>
void heapSorting2(T arr[], int n){
MaxHeap<T> maxheap = MaxHeap<T>(arr, n);
for (int i = n - 1; i >=0; i--)
arr[i] = maxheap.extractMax();
}
//MergeSorting.h
#include <iostream>
#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_
using namespace std;
//将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename T>
void __merge(T arr[], int l, int mid, int r){
T aux[r-l+1];
for (int i = l; i <= r; i++){
aux[i-l] = arr[i];
}
int i = l, j = mid + 1;
for (int k = l; k <= r; k++){
if (i > mid){
arr[k] = aux[j-l];
j++;
}
else if (j > r){
arr[k] = aux[i-l];
i++;
}
else if (aux[i-l] < aux[j-l]){
arr[k] = aux[i-l];
i++;
}
else{
arr[k] = aux[j-l];
j++;
}
}
}
//递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSorting(T arr[], int l, int r){
if (l >= r)
return;
int mid = (l + r)/2;
__mergeSorting(arr, l, mid);
__mergeSorting(arr, mid+1, r);
if (arr[mid] > arr[mid+1])//如果左边的序列已经小于右边的序列,就不用合并了
__merge(arr, l, mid, r);
}
template<typename T>
void MergeSorting(T arr[], int n){
__mergeSorting(arr, 0, n-1);
}
//quickSorting.h
#include <iostream>
#ifndef _SORTINGHELP_H_
#define _SORTINGHELP_H_
#include "SortingHelp.h"
#endif // _SORTINGHELP_H_
using namespace std;
//对arr[l...r]进行partition操作
//返回p,使得arr[l...p-1] < arr[p]; arr[p+1...r] > arr[p]
template<typename T>
int __partition2(T arr[], int l, int r){
//随机找一个元素与最左边元素进行交换位置
swap(arr[l], arr[rand()%(r-l+1)+l]);
T v = arr[l];
//arr[l+1...i)<=v; arr(j...r]>=v
int i = l + 1;
int j = r;
while(true){
while(arr[i] < v && i <= r) i++;
while(arr[j] > v && j >= l + 1) j--;
if (i > j) break;
swap(arr[i], arr[j]);
i ++;
j --;
}
swap(arr[l], arr[j]);
return j;
}
//对arr[l...r]部分进行排序
template<typename T>
void __quickSorting2(T arr[], int l, int r){
if (l >= r)
return;
srand(time(NULL));
int p = __partition2(arr, l, r);
__quickSorting2(arr, l, p - 1);
__quickSorting2(arr, p + 1, r);
}
template<typename T>
void quickSorting2(T arr[], int n){
__quickSorting2(arr, 0, n-1);
}