二叉堆的插入,删除和构建

二叉堆的本质是一个完全二叉树,它分为最大堆,和最小堆(所以学好二叉堆就要学好二叉树的知识点,感觉这些数据结构都是依次递进,有点进化的味道)

二叉堆的插入

插入节点:

也叫上浮

上浮就是孩子节点不断与父节点之间进行比较

最小堆的化,

孩子节点比父节点小,上浮;

最大堆的化,

孩子节点比父节点大,上浮;
其他情况那就不动;当然堆的插入肯定是已经构建好的堆

插入的位置:

就是插入位置是完全二叉树的最后一个位置,也就是最新的一个叶子节点(叶子节点插入从左到右))

def up_adjust(array=[]):
    child_index = len(array) - 1 # 左孩子节点
    parent_index = (child_index - 1) // 2 # 父节点
    # temp保存插入的叶子节点值,用于最后的赋值
    temp = array[child_index]
    i = 0
    while child_index > 0 and temp < array[parent_index]: #孩子节点索引大于0和孩子节点的值小于父节点的值
        # 无需真正交换,单向赋值即可
        array[child_index] = array[parent_index]
        child_index = parent_index
        parent_index = (parent_index - 1) // 2 # 在继续寻找父节点的父节点
        i += 1 # while就可以用,函数就不能
    array[child_index] = temp #把原来孩子节点的值赋值给孩子节点上浮索引的位置 
    print('上浮了多少%d次'%i)

代码优化的点:

这里的无需真正交换,单向赋值意思是:这里实际变化的是孩子节点的值,而最终退出循环的那个孩子节点的值又被最开始插入的值赋值。并没改变相对于那个孩子节点父节点的值。这样其实降低了时间复杂度和空间复杂度。

二叉堆的下沉

二叉堆的节点下沉操作;
下沉是父节点与左右孩子节点中较小的那个互换;可先假定左孩子小设定左孩子索引为child_index,
在再假设中假设右孩子比左孩子小,成立的话就执行child_index+1操作.不成立则没有操作
接着判断父节点小于任何一个孩子的值,直接跳出
接着进行父节点与最小的孩子节点之间的互换
这里要做的删除要删除的节点,然后把最后一个节点的数据放在该删除的节点位置,然后进行下沉操作
所以这里仅仅是下沉,并没有删除节点的操作

def down_adjust(parent_index, length, array=[]):
   
    # temp保存父节点值,用于最后的赋值
    temp = array[parent_index]
    child_index = 2 * parent_index + 1
    i = 1
    while child_index < length: # 循环条件是孩子节点索引小于数组长度
        # 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
        # 没有的话,下面这条if语句就不执行
        if child_index + 1 < length and array[child_index + 1] < array[child_index]:
            child_index += 1
        # 如果父节点小于任何一个孩子的值,直接跳出
        if temp <= array[child_index]:
            break
        # 无需真正交换,单向赋值即可
        array[parent_index] = array[child_index] # 父节点的值变为孩子节点的值,这里并没有改变孩子节点的值
        parent_index = child_index # 父节点的索引变为孩子节点,这一步才是最重要的,但他省略了孩子节点的值变为父节点的值
        child_index = 2 * child_index + 1 # 孩子节点再找出孩子节点
        i+=1
    array[parent_index] = temp #把父节点赋值给最后定位到的孩子节点 
    print('下沉了多少%d次'%i)

二叉堆的删除

二叉堆的删除是建立再二叉堆的下称之上的,只是下沉的父节点变为了那个最后一个叶子节点

删除操作的进行:

这里要做的是把要删除的节点删除,然后把最后一个节点的数据放在该删除的节点位置,然后进行下沉操作
下沉操作的临时变量temp也是该值,然后下沉

def down_adjust1(parent_index, length, array=[]):
    temp = array[length-1]
    # 这里要删除最后一个元素,并把最后一个元素的值赋值给要堆顶
    array[parent_index]=array[length-1]
    array.pop()
    child_index = 2 * parent_index + 1
    i = 1
    while child_index < length: # 循环条件是孩子节点索引小于数组长度
        # 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
        # 没有的话,下面这条if语句就不执行
        if child_index + 1 < length and array[child_index + 1] < array[child_index]:
            child_index += 1
        # 如果父节点小于任何一个孩子的值,直接跳出
        if temp <= array[child_index]:
            break
        # 无需真正交换,单向赋值即可;孩子节点索引变为父节点,父节点的值自然变了
        array[parent_index] = array[child_index] # 父节点的值变为孩子节点的值,这里并没有改变孩子节点的值
        parent_index = child_index # 父节点的索引变为孩子节点,这一步才是最重要的,但他省略了孩子节点的值变为父节点的值
        child_index = 2 * child_index + 1 # 孩子节点再找出孩子节点
        i+=1
    array[parent_index] = temp #把父节点赋值给最后定位到的孩子节点 
    print('下沉了多少%d次'%i)

二叉堆的构建

构建二叉堆就是把一个无序的完全二叉树调整为二叉堆,本质就是让所有非叶子节点依次下称
首先找到最后一个非叶子节点len(array)-2) // 2,然后按照顺序把其他非叶子节点依次下沉

def build_heap(array=[]):
    """
    二叉堆的构建操作
    :param array: 原数组
    """
    # 从最后一个非叶子节点开始,依次下沉调整
    for i in range((len(array)-2) // 2, -1, -1):
        down_adjust(i, len(array), array) # 这里调用下沉操作

二叉堆索引的技巧

二叉堆虽然是完全二叉树,但它的存储方式并不是链式存储,而是顺序存储,也就是二叉堆的所有节点都存储在数组中
这里假设父节点的下标是p,那么它左孩子节点就是2p+1.右孩子节点就是2p+2(节点均指索引)
同理假设左孩子节点是c,那么它的父节点是(c-1)//2

还有获取一个列表中所有父节点的索引

len(array)-2) // 2

posted @ 2021-09-26 17:39  索匣  阅读(276)  评论(0编辑  收藏  举报