lua实现的二叉堆,图文并茂,简单易懂

最近在学习a星算法的时候遇到这样一个问题,算法有一个操作:每次都要从一个不规则的数组中找出最小值(做法自然是遍历整个数组,找出最小值)。当然算法在遇到数据量较小时处理起来自然轻松,但假如处理海量的数据呢,每次都要遍历整个数组,那将导致整个算法相当低效。那我们就自然而然的想,有没有这样一种数组,它的最小值(或者最大值)永远都是在数组头部呢,这样,我们每次取最小值(或者最大值)的时候,直接从头部取就好了。嗯,这样的数组是有的。可以用二叉堆算法来实现这样的数组,那什么是二叉堆呢,也许你听过二叉树,是的,二叉堆具有二叉树的性质。我们先来看看数据使用二叉堆结构是如何存储的,

如图

根据上图左侧的二叉堆和右侧的数组,有发现什么规则吗。假如根节点在数组中的索引是1,那么存储在第n个位置的父节点它的子节点在数组中的存储位置为2n与2n+1。从上面的数组可以发现,用二叉堆数据结构实现的数组,最小值都是存储在第一个位置,这样的二叉堆我们称为最小堆,那么最大堆自然是最大值存储在数组第一个位置。

利用这样的数组我们就可以很轻易的拿到最小值(或最大值),因为我们不需要再遍历整个数组了,是不是很方便,其实二叉堆就是专门为取出最大或最小节点而设计的数据结构。好了,那么问题来了,怎么实现呢?下面我用一个例子来说明

以上就是我们对数组所做的向上整形,目的是每次都把最小值放到数组首位。聪明的你应该会想到,既然有向上整形,那是不是也有向下整形呢。哈哈,对的,那什么是向下整形呢。有没有想过,假如我们把数组的首位拿出来,即删除首位,那这个时候该数组的最小值应该是哪个呢。是的向下整形就是解决这个问题的。

下面我还是用例子解释

以上就是向下的整形过程。

通过上面的内容,我们已经大概知道二叉堆的结构以及如何构建一个二叉堆,下面我们通过代码再加深巩固一下,别怕哈,代码不长

--[[
	二叉堆
        by fly
]]
local BinaryHeap = class("BinaryHeap")

function BinaryHeap:ctor()
	self.m_array = {}    --数组
	self.m_lastIndex = 0  --当前数组末尾的位置
end

--插入数据
function BinaryHeap:insert(data)

	table.insert(self.m_array,data)
	self.m_lastIndex = self.m_lastIndex + 1
	if self.m_lastIndex > 1 then
		self:swimUp(self.m_lastIndex)
	end
end

--将插入的数据上移到合适的位置(向上整形)
function BinaryHeap:swimUp(index)
	local pindex = math.floor(index/2)  --取出父节点的位置

	--父节点还在数组里并且当前节点的值小于父节点的值,于是交换
	while pindex > 0 and self:compare(index,pindex) do
		self:change(index,pindex)
		index = pindex
		pindex = math.floor(pindex/2)   --寻找父节点的父节点
	end
end

--比较数据的大小
function BinaryHeap:compare(n,m)
	local a = self.m_array[n]
	local b = self.m_array[m]
	if a and b then
		return a < b
	end
end

--交换两个数据
function BinaryHeap:change(n,m)
	local temp = self.m_array[n]
	self.m_array[n] = self.m_array[m]
	self.m_array[m] = temp
end

--获取最小值
function BinaryHeap:getMin()
	return self.m_array[1] 
end

--删除数据(相当于删除最后一个节点的数据)
function BinaryHeap:popMin()
	self.m_array[1] = self.m_array[self.m_lastIndex]
	table.remove(self.m_array,self.m_lastIndex)
	
	self.m_lastIndex = self.m_lastIndex - 1
	self:sinkDown(1)
end

--将根节点的数据下移到合适的位置(向下整形)
function BinaryHeap:sinkDown(index)
	local cindex = index * 2   --当前节点的子节点

	while cindex <= self.m_lastIndex do
		--找出两个子节点中值最小的子节点
		if cindex+1 <= self.m_lastIndex and self:compare(cindex+1,cindex) then
			cindex = cindex + 1
		end
		if self:compare(cindex,index) then
			self:change(cindex,index)
			index = cindex
			cindex = cindex*2   --寻找子节点的子节点
		else
			break    --不满足条件,退出
		end
	end
end

--打印
function BinaryHeap:printArr()
	dump(self.m_array,"array",10)
end


return BinaryHeap

看完代码,我们已经基本了解二叉堆是一个什么东东了。一个算法的优劣,可以通过计算它的时间复杂度来衡量,这里简单的给出答案:二叉堆的操作如:删除,插入节点,都是O(log2n),也就是对一个有n个数据的二叉堆,最糟糕情况下需要执行log2n次操作才完成。

二叉堆有很多应用,比如用在排序和某些算法里面,这里就不详细说了,感兴趣的可以看看堆排序和a*算法哦。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值