【堆】建堆 | 删除栈顶元素 | 添加元素 | 堆排序

堆的定义

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。

如果父亲结点大于或等于孩子结点的值,那么称这样的堆为最大堆。反之,最小堆。以下都以最大堆作为实例。

int heap[100]; 

建堆

建堆的过程是不断调整的过程,每次调整都是把结点从上往下的调整(父结点和子结点的交换),即向下调整

调整方法是这样的:总是将当前结点V与它的左右孩子比较(如果有的话),假如孩子中存在权值比结点V的权值大的,就将其中权值最大的那个孩子结点与结点V交换;交换完毕后继续让结点V和孩子比较,直到结点V的孩子的权值都比结点V的权值小或是结点V不存在。

时间复杂度为O(logN)

//向下调整
//left为欲调整结点的数组下标,right一般为堆的最后一个元素的数组下标 
void downAdjust(int left,int right)
{
	int i = left,j = i*2; //i为欲调整目标,j为它的左孩子 
	while(j<=right) //存在孩子结点 
	{
		//如果右孩子存在且大于左孩子的值 
		if(j+1<=right&&heap[j+1]>heap[j])
			j = j+1; //更新将要进行比较和交换的坐标
		//如果左右孩子中值最大的结点比欲调整的目标结点还要大 
		if(heap[j]>heap[i])
		{
			swap(heap[j],heap[i]); //交换 
			i = j; //更新欲调整目标 
			j = i*2; //更新左孩子 
		}
		else break; //否则退出循环 
	}
}

建堆从倒数第一个拥有孩子的结点开始。即n的父结点n/2。 

时间复杂度为O(N)

//建堆
void creatHeap()
{
	int i;
	for(i=n/2;i>=1;i--)
		downAdjust(i,n);
}

删除栈顶元素

如果要删除栈顶元素,并让其仍然保持堆的结构,那么只需要最后一个元素覆盖栈顶元素,然后对根结点进行调整即可。

时间复杂度O(logN)

//删除栈顶元素
void deleteTop()
{
    heap[1] = heap[n--]; //用最后一个元素覆盖栈顶元素,并将元素个数减一
    downAdjust(1,n); //想下调整根结点
}

添加元素

如果想往堆里添加一个元素,可以把想要添加的元素放在数组最后,然后进行向上调整操作。向上调整总是把欲调整结点与父亲结点比较,如果权值比父亲结点大,那么就交换其与父亲结点,这样反复比较,直到达堆顶或者父亲结点的权值较大为止。

时间复杂度为O(logN)

//向上调整,left一般为1,right一般为欲调整的下标 
void upAdjust(int left,int right)
{
	//i为欲调整的坐标,j为其父结点 
	int i = right,j = i/2;
	while(j>=left) //父亲结点没有出界 
	{
		if(heap[i]>heap[j]) //父亲结点的权值小于孩子结点的权值 
		{
			swap(heap[i],heap[j]); //交换 
			i = j; //更新欲调整结点 
			j = i/2; //更新父亲结点 
		}
		else break; //否则退出循环 
	}
}
void insertHeap(int x)
{
    heap[++n] = x; //让元素先自加,再赋值
	upAdjust(1,n); //向上调整,使添加元素后仍为堆结构
}

堆排序

时间复杂度是O(NlogN),不稳定。

顺便又拓展了一种排序算法【排序算法】冒泡排序|选择排序|插入排序|sort函数|归并排序|快速排序

堆排序的思路是——取出栈顶元素,然后将堆的最后一个元素替换至堆顶,再进行一次针对堆顶元素的向下调整。

void heapSort()
{
    createHeap(); //先建堆
    for(i=n;i>=1;i--) //倒着枚举
    {
        swap(heap[1],heap[i]); //交换堆顶元素
        downAdjust(1,i-1); //向下调整未交换过的元素
    }
}

这里给出堆排序的完整代码。

#include
#include

using namespace std;

int n,heap[1010];

void downAdjust(int left,int right)
{
	int i = left,j = left*2;
	while(j<=right)
	{
		if(j+1<=right&&heap[j+1]>heap[j])
			j = j+1;
		if(heap[j]>heap[i])
		{
			swap(heap[i],heap[j]);
			i = j;
			j = i*2;
		}
		else break;
	}
}

void createHeap()
{
	int i;
	for(i=n/2;i>=1;i--)
		downAdjust(i,n);
}

int main()
{
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%d",&heap[i]);
	createHeap();
	for(i=n;i>1;i--)
	{
		swap(heap[1],heap[i]);
		downAdjust(1,i-1);
	} 
	for(i=1;i<=n;i++)
	{
		printf("%d",heap[i]);
		if(i!=n) printf(" ");
	}	
	return 0;
}

摘自《算法笔记》胡凡、曾磊著

你可能感兴趣的:(数据结构与算法)