首页 > 代码库 > 堆排序

堆排序

N个元素称为堆,若它的元素序列k[1],k[2],k[3].....K[n]满足 

       k[i]<=k[2i]  ,k[i]<=k[2i+1]     1<=i<=n/2

则称之为最小堆(min_heaps),  如果满足

      k[i]>=k[2i]  ,k[i]>=k[2i+1]     1<=i<=n/2

则称之为最大堆(min_heaps)。

================

下面左边的图表示最大堆,右边表示堆在数组中的存取情况

下面以最大堆为例,说明堆的操作。


一:堆的上移动操作

如果修改堆中某个元素,使得它的值大于父节点的,这就破坏了最大堆的性质,因此需要对堆实现上移操作。

//对下标为i的数进行上移动操作,flag标记修改后的值是否大于父节点
void sift_up(int *H,int i)
{
	bool flag=true;
	while(flag &&i!=1)
	{
		if(H[i]>H[i/2])
			swap(H[i],H[i/2]);
		else
			flag=false;
		i=i/2;
	}
}

二:堆的下移操作

如果修改堆中某个元素,使得它的值小于孩子节点,这就破坏了最大堆的性质,因此需要对堆实现下移操作。

//形参m为堆元素个数

void sift_down(int *H,int m,int i)
{
	bool flag=true;
	while(flag && (i=2*i)<=m)
	{
		if(i+1<=m && H[i+1]>H[i])  //选取需要下移的节点的左右孩子节点中较大的那个元素
			i++;
		if(H[i]>H[i/2])  //此时的i/2即为我们需要下移的元素,如果它比它的左右孩子节点中较大的那个要小,那么两者互换
			swap(H[i],H[i/2]);
		else  flag=false;
	}
}

三:堆的删除操作

如果我们要删除下标为 i 的元素,那么我们可以用堆中最后1个元素替代i,然后对这个替代后的元素执行上移或者下移操作

//删除堆中下表为i的元素,最后元素个数由m带回
void heap_delete(int *H,int i ,int &m)
{
	if(i>m)
		cout<<"overflow"<<endl;
	else
	{
		int a,b;
		a=H[i];
		b=H[m];
		H[i]=b;
		m--;
		if(a>b)
		{
			sift_down(H,m,i);
		}
		else
		{
			sift_up(H,i);
		}

	}
}
四:堆的插入操作

插入到堆最后1个元素后面,然后对该元素执行上移操作

//形参m为堆元素的个数,num为插入的数
void Insert(int *H,int &m,int num)
{
	m++;
	H[m]=num;
	sift_up(H,m);
}
五:堆的建立

把要建立成堆的元素依次调用上面的插入函数,一个一个插入,就成了堆

// A为需要建立堆的数组,H为所建立的堆,g为A中的数组元素个数,m为堆的元素,最后该参数返回堆的元素个数
//堆是从H[1]开始存储元素
void make_heap(int *A,int *H,int g,int &m)
{
	m=0;
	for(int i=0;i<g;i++)
	{
		Insert(H,m,A[i]);
	}
}

六:堆的排序

假设堆有n个元素,由于堆的第一个元素一定是最大的元素,因此可以让第一个元素和第n个元素互换,然后对第一个元素执行下操作。接着,对n-1个元素执行相同的操作,让第一个元素与第n-1个元素互换,然后对第一个元素执行下移操作,依次类推,最后的堆就是从小到大排序好的堆。

//堆排序,m为堆的元素个数
void heap_sort(int *H,int m)
{
	int i;
	for(i=m;i>0;i--)
	{
		swap(H[i],H[1]);
		sift_down(H,i-1,1); //注意第2个参数为i-1,不是m,是对接下来的i-1个堆元素进行操作
	}
}

完整的代码如下:

#include<iostream>
using namespace std;

void swap(int &a,int &b)
{
	int c;
	c=a;
	a=b;
	b=c;
}

//对下标为i的数进行上移动操作
void sift_up(int *H,int i)
{
	bool flag=true;
	while(flag &&i!=1)
	{
		if(H[i]>H[i/2])
			swap(H[i],H[i/2]);
		else
			flag=false;
		i=i/2;
	}
}

//形参m为堆元素个数

void sift_down(int *H,int m,int i)
{
	bool flag=true;
	while(flag && (i=2*i)<=m)
	{
		if(i+1<=m && H[i+1]>H[i])  //选取需要下移的节点的左右孩子节点中较大的那个元素
			i++;
		if(H[i]>H[i/2])  //此时的i/2即为我们需要下移的元素,如果它比它的左右孩子节点中较大的那个要小,那么两者互换
			swap(H[i],H[i/2]);
		else  flag=false;
	}
}


//形参m为堆元素的个数,num为插入的数
void Insert(int *H,int &m,int num)
{
	m++;
	H[m]=num;
	sift_up(H,m);
}

// A为需要建立堆的数组,H为所建立的堆,g为A中的数组元素个数,m为堆的元素,最后该参数返回堆的元素个数
//堆是从H[1]开始存储元素
void make_heap(int *A,int *H,int g,int &m)
{
	m=0;
	for(int i=0;i<g;i++)
	{
		Insert(H,m,A[i]);
	}
}

//删除堆中下表为i的元素,最后元素个数由m带回
void heap_delete(int *H,int i ,int &m)
{
	if(i>m)
		cout<<"overflow"<<endl;
	else
	{
		int a,b;
		a=H[i];
		b=H[m];
		H[i]=b;
		m--;
		if(a>b)
		{
			sift_down(H,m,i);
		}
		else
		{
			sift_up(H,i);
		}

	}
}

//堆排序,m为堆的元素个数
void heap_sort(int *H,int m)
{
	int i;
	for(i=m;i>0;i--)
	{
		swap(H[i],H[1]);
		sift_down(H,i-1,1); //注意第2个参数为i-1,不是m,是对接下来的i-1个堆元素进行操作
	}
}


void main()
{
	int A[5]={34,11,242,22,4};
	int H[6]; 
	int m,i;
	make_heap(A,H,5,m);
	for(i=1;i<=m;i++)
	{
		cout<<H[i]<<"  ";
	}
	cout<<endl;
	cout<<"上移操作:"<<endl;
	H[5]=999;
	sift_up(H,5);
		for(i=1;i<=m;i++)
	{
		cout<<H[i]<<"  ";
	}
    cout<<endl;
	cout<<"下移操作:"<<endl;
	H[1]=1;
	sift_down(H,m,1);
		for(i=1;i<=m;i++)
	{
		cout<<H[i]<<"  ";
	}
	cout<<endl;
	cout<<"元素删除操作,删除堆顶元素"<<endl;
	heap_delete(H,1,m);
		for(i=1;i<=m;i++)
	{
		cout<<H[i]<<"  ";
	}
	cout<<endl;
	cout<<"堆排序:"<<endl;


	heap_sort(H,m);
		for(i=1;i<=m;i++)
	{
		cout<<H[i]<<"  ";
	}
    cout<<endl;
	system("pause");
}

时间复杂度为O(nlogn)


堆排序