二叉堆

二叉堆

1 基于二叉堆的优先队列

在优先级队列中,队列中的项的逻辑顺序由它们的优先级确定。最高优先级项在队列的前面,最低优先级的项在后面。因此,当你将项排入优先级队列时,新项可能会一直移动到前面。

我们只使用一个单一的列表作为二叉堆的内部表示。

二叉堆有两个常见的变体:最小堆(其中最小的键总是在前面)和最大堆(其中最大的键值总是在前面)。

我们将实现最小堆。

2 二叉堆实现

利用二叉树的对数性质来表示我们的堆。

2.1 完整二叉树

一个完整的二叉树是一个树,其中每个层都有其所有的节点,除了树的最底层,从左到右填充。

2.2 堆的属性

可以使用单个列表来表示它。

父节点的左子节点(在位置 p 处) 是在列表中位置 2p 中找到的节点。 类似地,父节点的右子节点在列表中的位置 2p + 1。为了找到树中任意节点的父节点,我们可以简单地使用 Python 的整数除法。

1539144165586

堆的排序属性如下:在堆中,对于具有父 p 的每个节点 x,p 中的键小于或等于 x 中的键。

2.3 堆操作

2.3.1 init 函数

一个空的二叉堆有一个单一的零作为 heapList 的第一个元素,这个零只是放那里,用于以后简单的整数除法。

def __init__(self):
	self.heapList = [0]
	self.currentSize = 0

2.3.2 insert 函数

将项添加到列表中最简单,最有效的方法是将项附加到列表的末尾。 它维护完整的树属性。但可能违反堆结构属性。可以编写一个方法,通过比较新添加的项与其父项,我们可以重新获得堆结构属性。 如果新添加的项小于其父项,则我们可以将项与其父项交换。

下图展示了将新添加的项替换到其在树中的适当位置所需的操作。

1539144683528

当添加完一个新项后,需要对二叉堆进行维护,由 perUp 函数接管,它在树中向上遍历一个新项并正确定位该新项。

def perUp(self, i):
	while i // 2 > 0:
		if self.heapList[i] < self.heapList[i // 2]:
			tmp = self.heapList[i // 2]
			self.heapList[i // 2] = self.heapList[i]
			self.heapList[i] = tmp
		i = i // 2
def insert(self, k):
	self.heapList.append(k)
	self.currentSize += 1
	self.perUp(self.currentSize)

2.3.3 delMin 函数

堆属性要求树的根是树中的最小项,所以找到最小项很容易。

难点在根被删除后恢复堆结构和堆顺序属性。

  1. 通过获取列表中的最后一个项并将其移动到根位置来恢复根项。
  2. 通过将新的根节点沿着树向下推到其正确位置来恢复堆顺序属性。

1539247149051

为了维护堆顺序属性,我们所需要做的是将根节点和最小的子节点交换。

将节点和其子节点重复交换,直到节点被交换到正确的位置,使它小于两个子节点。

percDown 和 minChild 方法实现树交换节点。percDown 方法确保最大的子节点总是沿着树向下移动。

def minChild(self, i):
	if i * 2 + 1 > self.currentSize:
		return i * 2
	else:
		if self.heapList[i*2] < self.heapList[i*2+1]:
			return i * 2
		else:
			return i * 2 + 1

def perDown(self, i):
	while (i * 2) <= self.currentSize:
		mc = self.minChild(i)
		if self.heapList[i] > self.heapList[mc]:
			tmp = self.heapList[i]
			self.heapList[i] = self.heapList[mc]
			self.heapList[mc] = tmp
		i = mc

delMin 函数的代码。

def delMin(self):
	retval = self.heapList[1]
	self.heapList[1] = self.heapList[self.currentSize]
	self.currentSize = self.currentSize - 1
	self.heapList.pop()
	self.perDown(1)
	return retval

2.3.4 buildHeap 函数

buildHeap 函数用于构建整个堆。

1539247490777

def buildHeap(self, alist):
	i = len(alist) // 2
	self.currentSize = len(alist)
	self.heapList = [0] + alist[:]
	while i > 0:
		self.perDown(i)
		i = i - 1

2.4 完整代码

class BinHeap(object):
    def __init__(self):
        self.heapList = [0]
        self.currentSize = 0

    def perUp(self, i):
        while i // 2 > 0:
            if self.heapList[i] < self.heapList[i // 2]:
                tmp = self.heapList[i // 2]
                self.heapList[i // 2] = self.heapList[i]
                self.heapList[i] = tmp
            i = i // 2

    def insert(self, k):
        self.heapList.append(k)
        self.currentSize += 1
        self.perUp(self.currentSize)

    def minChild(self, i):
        if i * 2 + 1 > self.currentSize:
            return i * 2
        else:
            if self.heapList[i*2] < self.heapList[i*2+1]:
                return i * 2
            else:
                return i * 2 + 1

    def perDown(self, i):
        while (i * 2) <= self.currentSize:
            mc = self.minChild(i)
            if self.heapList[i] > self.heapList[mc]:
                tmp = self.heapList[i]
                self.heapList[i] = self.heapList[mc]
                self.heapList[mc] = tmp
            i = mc

    def delMin(self):
        retval = self.heapList[1]
        self.heapList[1] = self.heapList[self.currentSize]
        self.currentSize = self.currentSize - 1
        self.heapList.pop()
        self.perDown(1)
        return retval

    def buildHeap(self, alist):
        i = len(alist) // 2
        self.currentSize = len(alist)
        self.heapList = [0] + alist[:]
        while i > 0:
            self.perDown(i)
            i = i - 1


if __name__ == '__main__':
    bh = BinHeap()
    bh.buildHeap([9, 5, 6, 2, 3])
    print(bh.delMin())
    print(bh.delMin())
    print(bh.delMin())
    print(bh.delMin())
    print(bh.delMin())

posted @ 2018-10-11 16:47  banshaohuan  阅读(195)  评论(0编辑  收藏  举报