背景

今天写leetcode签到题,滑动窗口的最大值,菜鸡的我只会一种暴力法,不出所料,超时了,看了题解发现用大根堆的一种解法,感觉还不错,很容易理解,虽然说复杂度也很高(有更好的解法),但是还是想回顾一下以前没弄特别明白的堆。

题目连接如下:求滑动窗口的最大值
题解里面有相应详细解题方法。

堆的定义

  • 堆就是一个二叉树,而且是一个完全二叉树(各种树的定义
  • 堆的某个节点总是不大于或者不小于父节点
  • 当所有节点都大于等于他的父节点,可想而知,根节点一定是最小的。称为小根堆。
  • 当所有节点都小于等于他的父节点,根节点值最大,称为大根堆

堆的实现方法

通过堆的排序来实现大根或者小根堆
堆排序图解

我就是白嫖之王。

主要算法步骤

  1. 筛选:主要是看一个树结构的三个节点(一个父节点和两个子节点)他们是否满足子节点不大于或者不小于父节点的条件。如果不满足的话就改变父子顺序,让他满足条件。这样又会出现问题,当改变顺序又会使下面的树结构不满足条件。再次改变,直到所有的都符合要求。(为了严谨,我们需要从下往上一次筛选,再一次从上到下筛选。就像是一个最大值从树的下面依次往上面挤,直到挤到根节点的位置(大根堆))
  2. 交换(出堆):将根节点和最下面最左边的值交换。

代码实现

主要参考自李春葆的数字结构教程,要我自己硬写出来我实力不太够。

1. 大根堆的筛选

void shift(Type R[],int low,int high)
{
	/*
	*class Type
	*{
	*	key: 关键字->就是用来比较大小
	*}
	*low和high代表着在R上,从low到high都要满足条件,
	*这里的R数组,他的下标意义对应着一个节点,把二叉树从左到右,从上到下,依次对应0-n。
	*/
	int i=low,j=low*2;//由于是完全二叉树,所以R[j]对应着R[i]的左节点,R[j+1]对应着右节点。
	Type temp=R[i];
	while(j<high)
	{
		if(j<high&&R[j].key<R[j+1].key)
			j++;//左节点小于右节点,就让j指向右节点。
		if(temp.key<R[j].key)
		{
			R[i]=R[j];//根节点的值小于子节点,就直接交换。
			i=j;//然后向下面筛选,直到到达指定的high。
			j=2*i;
		}
		else break;//根节点大于等于最大孩子关键字,结束
	}
	R[i]=temp;//一次归位一个节点,然后从最后一个父节点开始,反复筛选,直到最后都符合要求。
	/*
	for(int i=n/2;i>=1;i--)//n/2就是最后一个父节点。依次往上走。
	{
		shift(R,i,n);
	}
	*/
	
}

2. 大根堆的交换

void HeapSort(Type R[],int n)
{
	for(int i=n/2;i>=1;i--)//建立初始的堆
	{
		shift(R,i,n);//从最后一个父节点依次往上面筛选。
	}
	for(int i=n;i>=2;i++)
	{
		swap(R[1],R[i]);//把最后一个元素和根R[0]交换
		shift(R,1,i-1);//然后R[i]就像是出堆了一样(出了一个最大的),不再管它,对R[1...i-1]进行筛选。
	}
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值