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

堆排序算法利用了大根堆(或者小根堆,下同)的两个固有性质,即:

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,所用时间如下:


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值