堆排序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆排序相关的知识,希望对你有一定的参考价值。

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)











以上是关于堆排序的主要内容,如果未能解决你的问题,请参考以下文章

选择排序(简单选择排序堆排序的算法思想及代码实现)

排序--08---堆排序

python代码实现堆排序

算法-java代码实现堆排序

一文带你了解堆排序

堆排序