zoukankan      html  css  js  c++  java
  • 堆排序

    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)


  • 相关阅读:
    socket错误码获取
    代码整洁之道读书笔记函数
    算法学习之堆排序
    包含与继承区别
    提高 LayerBacked Memory Use
    RenderBuffer
    算法学习之快速排序
    NSTimer
    DNS and BIND ... (转载) zhumao
    Samba学习笔记(转载) zhumao
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5224878.html
Copyright © 2011-2022 走看看