[数据结构]堆

2 篇文章 0 订阅
2 篇文章 0 订阅

堆(heap)

堆是一种完全二叉树的变种,是用数组存放,其中第0个元素是堆的根,编号为k的节点的左右节点分别为2k+1和2k+2,调整后的堆应具有以下特点:父节点小于左右子节点(小根堆)

python中堆的方法

我们学习一下python里的heapq的实现,首先heap包含几个方法:
heappush(heap, item)
尾部添加节点
heappop(heap)
删除头节点
heappushpop(heap, item)
添加元素并删除添加元素后的头节点,该方法优于先push后pop的操作
heapify(x)
将一个list变成heap
heapreplace(heap, item)
删除头节点并把item添加进heap中,该方法优于先pop后push操作

由以上5种操作,衍生出3个常用方法:
*merge(iterables)
将n个列表进行merge
nlargest(n, iterable[, key])
取n个最大数
nsmallest(n, iterable[, key])
取n个最小数

python中堆的实现

def heappush(heap, item):
    """Push item onto heap, maintaining the heap invariant."""
    heap.append(item)
    _siftdown(heap, 0, len(heap)-1)

def heappop(heap):
    """Pop the smallest item off the heap, maintaining the heap invariant."""
    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty
    if heap:
        returnitem = heap[0]
        heap[0] = lastelt
        _siftup(heap, 0)
    else:
        returnitem = lastelt
    return returnitem

push的方法很简单,将元素添加至末尾,并执行siftdown操作。
pop的方法是先拿出一个last,然后和头节点进行替换,并执行siftup操作。
siftdown操作可以将尾部节点向头部节点进行转移,而siftup操作是将头部节点向尾部节点转移。

def heapreplace(heap, item):
    """Pop and return the current smallest value, and add the new item."""
    returnitem = heap[0]    # raises appropriate IndexError if heap is empty
    heap[0] = item
    _siftup(heap, 0)
    return returnitem

def heappushpop(heap, item):
    """Fast version of a heappush followed by a heappop."""
    if heap and cmp_lt(heap[0], item):
        item, heap[0] = heap[0], item
        _siftup(heap, 0)
    return item
    
def heapify(x):
    """Transform list into a heap, in-place, in O(len(x)) time."""
    n = len(x)
    for i in reversed(xrange(n//2)):
        _siftup(x, i)

replace操作是将头部节点进行替换,然后并执行siftup。
pushpop操作先将要插入的节点和头节点比较,比如比头节点更小,那么无需改动,如果比头节点大,那么头节点一定会被pop,此时我们只需替换头节点并执行sift操作即可。
heapify操作是从最深的中间父节点开始一步一步进行siftup操作,叶子节点无需进行sift,所以这里用的是n//2。

def _siftdown(heap, startpos, pos):
    newitem = heap[pos]
    # Follow the path to the root, moving parents down until finding a place
    # newitem fits.
    while pos > startpos:
        parentpos = (pos - 1) >> 1
        parent = heap[parentpos]
        if cmp_lt(newitem, parent):
            heap[pos] = parent
            pos = parentpos
            continue
        break
    heap[pos] = newitem

siftdown稍微简单一些,每次找到当前节点的父节点,对比当前节点和父节点,如果当前节点小于父节点则swap并更新pos,否则退出函数。

def _siftup(heap, pos):
    endpos = len(heap)
    startpos = pos
    newitem = heap[pos]
    # Bubble up the smaller child until hitting a leaf.
    childpos = 2*pos + 1    # leftmost child position
    while childpos < endpos:
        # Set childpos to index of smaller child.
        rightpos = childpos + 1
        if rightpos < endpos and not cmp_lt(heap[childpos], heap[rightpos]):
            childpos = rightpos
        # Move the smaller child up.
        heap[pos] = heap[childpos]
        pos = childpos
        childpos = 2*pos + 1
    # The leaf at pos is empty now.  Put newitem there, and bubble it up
    # to its final resting place (by sifting its parents down).
    heap[pos] = newitem
    _siftdown(heap, startpos, pos)

siftup比较复杂,首先记录要移动的节点,然后获得当前节点的左右子节点,比较左右子节点哪个更小,将小的节点进行上移,更新当前节点为上移的那个子节点,最后将记录节点设置为叶子节点进行上移操作。这样先往下后往上的操作能够保证在siftdown的过程中没有来自兄弟节点的干扰,因为在siftup的时候已经进行了比较,将更小的元素放在了父节点中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值