堆heap

定义——参考

堆就是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。
堆总是满足下列性质:

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。
下图是一个最大堆:
在这里插入图片描述

注意:堆的根节点中存放的是最大或者最小元素,但是其他节点的排序顺序是未知的。例如,在一个最大堆中,最大的那一个元素总是位于 index 0 的位置,但是最小的元素则未必是最后一个元素。–唯一能够保证的是最小的元素是一个叶节点,但是不确定是哪一个。

堆是非线性数据结构,相当于一维数组,有两个直接后继。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)

对如何确定父节点和子节点呢?

parent(i) = floor((i - 1)/2)
left(i)   = 2i + 1
right(i)  = 2i + 2

堆的操作

1、shiftUp(): 如果一个节点比它的父节点大(最大堆)或者小(最小堆),那么需要将它同父节点交换位置。这样是这个节点在数组的位置上升。
2、shiftDown(): 如果一个节点比它的子节点小(最大堆)或者大(最小堆),那么需要将它向下移动。这个操作也称作“堆化(heapify)”。

堆排序

构造最大二叉堆并进行堆排序

class Solution:
    def sink(self,s,root):
        if 2*root+1<len(s):
            k = 2*root+2 if 2*root+2<len(s) and s[2*root+2]>s[2*root+1] else 2*root+1
            if s[k] > s[root]:
                s[k],s[root] = s[root],s[k]
                self.sink(s,k)
    def maxheap(self,s):
        for i in range(len(s)//2-1,-1,-1):#一定得从下往上来
            self.sink(s,i)
        return s
    def heap_sort(self,s):
        last = len(s)-1
        self.maxheap(s)#构造最大堆
        while last >0:
            s[0],s[last] = s[last],s[0]
            s[:last] = self.maxheap(s[:last])#因对s的切片进行最大堆排序,一定要把排序结果赋值给s,不然就没有任何结果
            last -= 1
        return s
        
answer = Solution()
print(answer.maxheap([1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]))
print(answer.heap_sort([1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]))

如果一个堆有n个节点,那么他的高度是h=floor(log2n)
层数是ceil(log2n)
叶节点索引位于n//2和n-1之间

堆排序的原理

应用-数据流中的中位数

参考
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
注意:数据流的数目是随时间变化而增加的,所以存放数据的容器的动态变化的
思路:
将数据容器分成两部分,令左边部分的所有数据都小于右边部分中的所有数据,将左边构造成一个最大堆,右边构造成一个最小堆,这样插入一个数的时间复杂度是O(logn),获取中位数的时间复杂度是O(1)

class Solution:
    def __init__(self):
        self.left = []#最大堆
        self.right = []#最小堆
        self.count = 0
    def Insert(self,num):
        if self.count&1 == 0:#容器总个数是偶数个,插入最小堆
            if self.left and num<self.left[0]:#如果插入的数比最大堆中的某些数小,先把这个数插入最大堆,然后把最大堆的根节点取出来插入最小堆
                self.left.append(num)
                self.maxheap(self.left)
                self.right.append(self.left.pop(0))
                self.minheap(self.right)
            else:
                self.right.append(num)
                self.minheap(self.right)
        else:#如果容器总个数是奇数,插入最大堆
            if self.right and num>self.right[0]:#如果这个数大于最小堆中的数
                self.right.append(num)
                self.minheap(self.right)
                self.left.append(self.right.pop(0))
                self.maxheap(self.left)
            else:
                self.left.append(num)
                self.maxheap(self.left)
        self.count += 1
    def GetMedian(self):
        if self.count & 1 == 0:#偶数个
            return (self.left[0]+self.right[0])/2
        else:
            return self.right[0]
                    
   def maxheap(self,s):
        length = len(s)
        if not s or length<= 0:
            return 
        if length == 1:
            return s
        for k in range((length-1)//2,-1,-1):#(length-1)//2
            heap = False
            while not heap and 2*k+1 <length:
                index = 2*k+2 if 2*k+2<length and s[2*k+2]>s[2*k+1] else 2*k+1
                if s[k] >s[index]:
                    heap = True#下层堆建好上面就不用管了
                else:
                    s[k],s[index] = s[index],s[k]
                    k = index
        return s
    def minheap(self,s):
        length = len(s)
        if not s or length<=0:
            return
        if length == 1:
            return s
        for k in range(length//2-1,-1,-1):
            heap = False
            while not heap and 2*k+1<length:
                index = 2*k+2 if 2*k+2<length and s[2*k+2]<s[2*k+1] else 2*k+1
                if s[k] < s[index]:
                    heap = True
                else:
                    s[k],s[index] = s[index],s[k]
                    k = index
        return s
        
answer = Solution()
string = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
for i in string:
    answer.Insert(i)
    print(answer.GetMedian())

打印二叉树的函数

def print_tree(self,array):
        index = 0
        depth = math.ceil(math.log2(len(array)))
        sep = ' '
        for i in range(depth):
            offset = 2**i
            print(sep*(2**(depth-i-1)-1),end = '')
            line = array[index:index+offset]
            for j,x in enumerate(line):
                print('{:>{}}'.format(x,len(sep)),end='')
                interval = 0 if i == 0 else 2**(depth-i)-1
                if j<len(line)-1:
                    print(sep*interval,end='')
            index+= offset
            print()

shiftup函数

def shiftUp(self,s,data):
        self.maxheap(s)
        pos = len(s)
        s.append(data)
        while (pos-1)//2>=0 and data>s[(pos-1)//2]:
            s[pos],s[(pos-1)//2] = s[(pos-1)//2],s[pos]
            pos = (pos-1)//2
        return s
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值