堆辅助运算Siftdown

//Siftdown是在2i或2i+1<n的情况下有H[i]<H[2i]或者H[i]<H[2i+1]时,需要将父节点下移
//分析首先要满足2i或2i+1存在,即存在左/右节点,因此要满足2i<n或2i+1<n,
//其次,i范围在1~n/2(从1开始),取i,2i,4i,8i..直到i*2^(k+1)>n,k表示至多需要比较的次数
//子节点存在有2种情况,一是存在左节点,二是存在左和右节点,需要进行判断,一般来说除最后一层,都是有左右节点
//一是左节点的情况下,只要比较,如果小于左节点,则交换,否则停止
//二是左右节点的情况下,又分为3种情况,1是小于左节点,2是小于右节点,3是都小于.
//    情况1/2和上面的情况一相同,同样操作
//    关键是情况3,需要左右字节相互比较,取较大者交换


#include <iostream>
using namespace std;

//预处理
template <class T>
int Getlength(T &a){
	return (sizeof(a)/sizeof(a[0]));
}


void Siftdown(int a[],int length ,int i){//a数组表示对应的堆,堆得第i个节点在a数组中的位置是i-1

	if (2*i > length)return;//表示没有左节点,更不可能有右节点
	int j=2*i;//表示左节点的位置
	while(j+1 <= length ){//表示左右节点存在
		/*if ((a[i-1]<a[2*i-1]&&a[i-1]>a[2*i])||((a[i-1]>a[2*i-1]&&a[i-1]<a[2*i])))//表示二种情况,一是左节点小,二是右节点小
		{
			int temp=a[j-1];
	    	a[j-1]=a[i-1];
	    	a[i-1]=temp;
		}*///表示太繁杂,舍弃
	    if (a[i-1]<a[j-1]&&a[i-1]>=a[j])//仅小于左节点,等号要加进去,否则会漏掉左节点等于父节点,右节点大于父节点的情况
	    {
	    	int temp=a[j-1];
	    	a[j-1]=a[i-1];
	    	a[i-1]=temp;
	    	i=j;
	    	j=2*i;
	    }
	    else if (a[i-1]>=a[j-1]&&a[i-1]<a[j])//仅小于右节点
	    {
	    	int temp=a[j];
	    	a[j]=a[i-1];
	    	a[i-1]=temp;
	    	i=j+1;//加1的理由是右节点与父节点交换,那么i应该重新指向右节点的位置,而不是左节点的位置
	    	j=2*i;
	    }
	    else if (a[i-1]<a[j-1]&&a[i-1]<a[j])//都小于左右节点
	    {
	    	if (a[j-1]<a[j])
	    	{
	    		int temp=a[j];
	    		a[j]=a[i-1];
	    		a[i-1]=temp;
	    		i=j+1;
	    		j=2*i;
	    	}
	    	else{
	    		int temp=a[j-1];
	    		a[j-1]=a[i-1];
	    		a[i-1]=temp;
	    		i=j;
	    		j=2*i;
	    	}
	    }else {
	    	return;
	    }
	}
	if (j==length&&a[i-1]<a[j-1])//表示左节点存在并且小于左节点,交换
	{
		int temp=a[j-1];
	    a[j-1]=a[i-1];
	    a[i-1]=temp;
	}
}


//改进方法
void Siftdown2(int a[],int length,int i){//由于除最后一次比较外,其他次比较都与左右节点比较
	//因此只要在左右节点先比较,找到最大值,再跟父节点比较
	if (2*i > length)return;
	bool done=false;
	int j=2*i;
	do{
		if (j+1<=length && a[j-1]<a[j])j++;//表示存在左右节点,并且右节点大于左节点,j指向左右节点中最大的节点位置
		if (a[j-1] > a[i-1])//父节点比子节点小,交换,否则跳出
		{
			int temp=a[j-1];
	    	a[j-1]=a[i-1];
	    	a[i-1]=temp;
	    	i=j;
	    	j=2*i;
		}else done=true;
	}while(j <= length && !done);
}

int main(int argc, char const *argv[])
{
	int a[]={20,3,9,12,11,4,5,3,7,5};
	int length=Getlength(a);
	Siftdown2(a,length,2);
	for (int i = 0; i < length; ++i)
	{
		cout<<a[i]<<" ";
	}



	system("pause");
	return 0;
}
posted @ 2016-08-22 09:09  jiangge3  阅读(1046)  评论(0)    收藏  举报