堆和堆排序

堆的定义:

父子结点是可以相等的,比如压入的都是相同的数也可以在堆里。

 

堆排序:

堆排序的原理:

     堆排序是利用数组实现的。

大根堆和小根堆:

堆排序的步骤:

 根据堆的性质(以最大堆举例):堆的根节点肯定是所有数据中最大的,但是他的所有子结点不是完全有序的,也就是不知道谁是第二个大的结点。这样就每次都拿出去根结点,剩下所有的子结点再重新组成堆,递归执行之前取根节点和维护堆的两个步骤。

 堆的实现:

   堆的实现主要是两个操作:上浮和下沉。

通过基础的上浮和下沉就可以实现堆的插入和删除操作:

删除最大元素:

插入元素:

堆的实现代码:

通知上面的分析已经知道,实现堆要实现四个操作:上浮、下沉、通过上浮实现的插入、通过下沉实现的添加。

class MaxHeap(object):
    def __init__(self, maxsize=0):
        self.maxsize = maxsize
        self._elements = [None]*maxsize
        self._count = 0

    def __len__(self):
        return self._count

    # 添加
    def add(self, value):
        if self._count >= self.maxsize:
            raise Exception('full,maxsize:{}'.format(self.maxsize))
        self._elements[self._count] = value
        self._count += 1
        self._siftup(self._count-1)  # 维持堆的特性

    # 上浮
    def _siftup(self, ndx):
        if ndx > 0:
            parent = int((ndx-1)/2)
            if self._elements[ndx] > self._elements[parent]:    # 如果插入的值大于 parent,一直交换
                self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx]
                self._siftup(parent)    # 递归

    # 删除/提取最大值
    def extract(self):
        if self._count <= 0:
            raise Exception('empty')
        value = self._elements[0]    # 保存 root 值
        self._count -= 1
        self._elements[0] = self._elements[self._count]    # 最右下的节点放到root后siftDown
        self._siftdown(0)    # 维持堆特性
        return value
    # 下沉
    def _siftdown(self, ndx):
        '''
        由于是最大堆,所以当一个结点要下沉时候,一定要和它两个子结点中更大的那一个交换位置
        '''
        left = 2 * ndx + 1
        right = 2 * ndx + 2
        # determine which node contains the larger value
        tmp_ndx = ndx
        # 左子结点大于根结点而且大于右子结点  才让左子结点上位
        if (left < self._count and     # 有左孩子
                self._elements[left] >= self._elements[tmp_ndx] and
                self._elements[left] >= self._elements[right]):  # 原书这个地方没写实际上找的未必是largest
            tmp_ndx = left
        # 先判断左子结点不大于右子结点,所以只要右子结点大于根结点  右子结点就上位
        elif right < self._count and self._elements[right] >= self._elements[tmp_ndx]:
            tmp_ndx = right
        # 如果试探的索引没有改变  也就是根结点比两个子结点都大  就不用交换位置了
        # 如果改变了 就交换位置 
        if tmp_ndx != ndx:
            self._elements[ndx], self._elements[tmp_ndx] = self._elements[tmp_ndx], self._elements[ndx]
            # 递归调用  一个父结点下沉之后 不意味就不用再下沉了
            # 所以要递归调用
            self._siftdown(tmp_ndx)


def test_maxheap():
    import random
    n = 5
    h = MaxHeap(n)
    for i in range(n):
        h.add(i)
    for i in reversed(range(n)):
        assert i == h.extract()

if __name__=="__main__":
    test_maxheap()

堆排序的代码实现:

另外一种写法:

如果是给出一组数,直接给他们排序,那也就是没有了插入操作了。

由于直接就是一堆数,在建立一个堆的时候,就不用一个一个往里面插入元素并且上浮了,直接从最下面依次下沉找到它应该在的位置。建立堆之后,再依次删除根结点,用最下面的结点放到根结点的位置,再下沉产生新的根结点也就是最大值。

# 下沉操作
def heapify(arr, n, i):
    '''
    n:数组里属于堆数值个数  [0,n]这些都是属于堆
    i:要下沉的结点索引
    ''' 
    largest = i  
    l = 2 * i + 1     # left = 2*i + 1 
    r = 2 * i + 2     # right = 2*i + 2 
  
    # 先看看左子结点是不是大
    if l < n and arr[i] < arr[l]: 
        largest = l 
    
    # 右子结点是否大于左子结点或者根节点
    if r < n and arr[largest] < arr[r]: 
        largest = r 
    
    # 如果发生了交换
    if largest != i: 
        arr[i],arr[largest] = arr[largest],arr[i]  # 交换
        # 继续下沉
        heapify(arr, n, largest) 
  
def heapSort(arr): 
    n = len(arr) 
  
    # 建立最大堆
    # 从最下面(数组属于堆的最有一个)的结点开始  让每个结点下沉
    for i in range(n, -1, -1): 
        heapify(arr, n, i) 
  
    # 一个个交换元素
    for i in range(n-1, 0, -1): 
        # arr[0]是最大的  把这个最大的和最后面的交换
        arr[i], arr[0] = arr[0], arr[i]   # 交换
        # 让新的根结点下沉
        # 新的堆的结点树是i
        heapify(arr, i, 0) 
  
arr = [ 12, 11, 13, 5, 6, 7] 
heapSort(arr) 
n = len(arr) 
print ("排序后") 
for i in range(n): 
    print ("%d" %arr[i])

堆排序的应用:

top  K 问题:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值