您的位置:首页 > 编程语言 > C语言/C++

十大基础应用算法及C++实现(二)----堆排序算法

2017-08-08 17:56 405 查看
堆排序算法利用了大根堆(或者小根堆,下同)的两个固有性质,即:

1.堆本身是一棵完全二叉树;

2.大根堆的根元素永远大于或者小于其子树的所有元素。

性质1给我们的主要结论为:设完全二叉树的元素编号为i(i∈[1,n],n为元素总数),如果i>n/2,则i这个元素一定是叶节点;否则i一定不是叶节点。证明过程可以用归纳法,比较简单。这个性质的主要用途是构建初始大根堆,基本原理是按照序号从大到小,对所有的非叶节点做堆调整(将子树调整成大根堆)。

性质2的主要用途是每次取出(删除)根节点,与序号最大的叶节点元素互换,然后剔除这个叶节点,剩下的树结构再重复以上操作,即每次都取出剩余堆的最大元素,直到最后一个元素。下面贴代码,代码继承了本人博客十大基础应用算法及C++实现(一)的相关代码,和快速排序算法以及冒泡法的效率作比较。

#include "stdafx.h"
#include <stdlib.h>
#include "ConsoleApplication1.h"
#include <iostream>
#include <time.h>
#define SIZE 100000
inline void swap(int&a, int&b)
{
int temp = a;
a = b;
b = temp;
}

int main()
{

using namespace std;
double        dStartTime1;                //快速排序算法起始时间
double        dEndTime1;
double		  usetime1;
double        dStartTime2;                //冒泡算法起始时间
double        dEndTime2;
double		  usetime2;
double        dStartTime3;                //堆算法起始时间
double        dEndTime3;
double		  usetime3;
int* all = new int[SIZE];
int* all1 = new int[SIZE];//随机数组的三个排序副本
int* all2 = new int[SIZE];
int* all3 = new int[SIZE+1];

srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样

for (int i = 0; i < SIZE; i++)
{
all[i] = rand() % SIZE + 1;  //产生随机数组
all1[i] = all[i];
all2[i] = all[i];
all3[i+1] = all[i];
}
//printf("随机生成的数组为:\n");
//for (int i = 0; i < SIZE; i++)
//{
//	printf("%d\t",all[i]);
//	if ( (i+1) % 10 == 0)
//		printf("\n");
//}

dStartTime1 = clock(); //Get The Start Time
compare1(all1, 0, SIZE - 1);//比较排序函数,传入数组,数组头和尾index
dEndTime1 = clock();

dStartTime2 = clock();
compare2(all2);
dEndTime2 = clock();

dStartTime3 = clock();
compare3(all3);
dEndTime3 = clock();

//printf( "\n排序后输出:\n");
//for (int i = 1; i <= SIZE; i++)
//{
//printf( "\t%d" , all3[i]);
//if (i % 10 == 0)
//	printf("\n");
//}
usetime1 = (dEndTime1 - dStartTime1) / CLOCKS_PER_SEC;
usetime2 = (dEndTime2 - dStartTime2) / CLOCKS_PER_SEC;
usetime3 = (dEndTime3 - dStartTime3) / CLOCKS_PER_SEC;
printf("\n快速排序法所用时间: %.3f\n",usetime1);
printf("\n冒泡排序法所用时间: %.3f\n", usetime2);
printf("\n堆排序法所用时间: %.3f\n", usetime3);

//delete[] all;
//delete[] all1;
//delete[] all2;
//delete[] all3;
return 0;
}

//快速排序算法
void compare1(int* all, int pos1, int pos2)
{
int size = pos2 - pos1 + 1;
int standard = all[pos1];
int size1 = 0;
int size2 = 0;
int* half1 = new int[size - 1];//存放子序列
int* half2 = new int[size - 1];
for (int index = 1; index < size; index++)
{
if (all[pos1 + index] <= standard)
{
half1[size1] = all[pos1 + index];
size1++;
}
else
{
half2[size2] = all[pos1 + index];
size2++;
}
}
if (size1 > 0)
{
for (int i = 0; i < size1; i++)
all[pos1 + i] = half1[i];
all[pos1 + size1] = standard;
compare1(all, pos1, pos1 + size1 - 1);//对子序列递归调用
}
if (size2 > 0)
{
for (int j = 0; j < size2; j++)
all[pos2 - j] = half2[j];
compare1(all, pos2 - size2 + 1, pos2);//对子序列递归调用
}
delete[] half1;//注意释放内存,防止泄露
delete[] half2;

}

//冒泡排序算法
void compare2(int* all)
{
int i, j, temp;
for (j = 0; j < SIZE; j++)
{
for (i = 0; i < SIZE - j; i++)
{
if (all[i] > all[i + 1])
{
temp = all[i];
all[i] = all[i + 1];
all[i + 1] = temp;
}
}
}
}

//堆排序算法
void compare3(int* all)
{
for (int i = SIZE / 2; i >= 1; i--) //构建一个大顶堆
heapderactive(all, i, SIZE);
for (int i = SIZE; i >= 1; i--)
{
swap(all[1], all[i]);
heapderactive(all, 1, i - 1);
}
}

void heapderactive(int* all, int i,int size)
{
int lchild = 2 * i;       //i的左孩子节点序号
int rchild = 2 * i + 1;     //i的右孩子节点序号
int max = i;            //临时变量
if (i <= size / 2)          //如果i是叶节点就不用进行调整
{
if (lchild <= size && all[lchild]>all[max])
{
max = lchild;
}
if (rchild <= size && all[rchild]>all[max])
{
max = rchild;
}
if (max != i)
{
swap(all[i], all[max]);
heapderactive(all, max, size);    //避免调整之后以max为父节点的子树不是堆
}
}
}


当排序元素个数达到100000,所用时间如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: