zoukankan      html  css  js  c++  java
  • 【数据结构】堆

    定义


      (1)堆是一个完全二叉树。(除了叶子节点,其他节点的值都是满的)

      (2)堆中每一个节点的值都必须大于等于(或者小于等于)其左右节点的值。 对于每个节点的值大于等于子树的值,称为大顶堆。反之称之为小顶堆。

      图示(大顶堆):

    堆的实现


       因为堆的执行(完全二叉树),他适合用数组直接进行存储。数组中下标为i的节点的左右节点为为i*2, i*2+1。而任意一个节点i的父节点是i/2。这里我们在平时可以选择下边1作为第一个节点,有助于理解和代码的书写。如果我们用下标0作为第一个节点的话, 其子节点为(i+1)/2 ,(i+1)/2 +1。

    堆的操作


       插入操作 :在向堆中插入一个数据时,我们还是需要继续满足堆的特性。我们先将数据插入到数组的尾部,然后进行调整,让其重新满足堆的特性,这个过程就叫作堆化。

        堆化过程:从下往上堆化。

        从下往上堆化过程:先将数据插入到数组尾部,然后比较其父节点关系,然后继续进行交换。直到最后满足堆的性质。

        实现代码      

     1 class Heap():
     2     def __init__(self, capacity):        # 初始化
     3         self.a = [0] * (capacity+1)
     4         self.n = capacity
     5         self.count = 0
     6     
     7     def insert(self, data):               # 插入操作,自下而上进行堆化
     8         if self.count >= self.n:           # 如果满了直接返回
     9             return 
    10         self.count += 1                   # 数量加一
    11         self.a[self.count] = data         # 将插入的数据添加到尾部
    12         index = self.count                # 记录当前下标
    13         while index//2 >0 and self.a[index] > self.a[index//2]: # 堆化调整, 因为时大顶堆,所以如果子节点大于父节点应该交换,比记录父节点的下标。
    14             self.a[index], self.a[index//2] = self.a[index//2],  self.a[index]
    15             index = index // 2
    16         

      删除操作:在堆中我们一般都是删除堆顶的元素,很少删除指定下标节点的情况。所以我们还是只讨论对于堆顶元素的删除情况。

        堆化过程:从上往下堆化。

        从上到下进行堆化过程:在堆化的图示过程中我们可以看待,这种情况会导致最后完成之后不能满足完全二叉树的情况。从而也就破坏了堆的性质。

         鉴于此情况,我们可以将待删除的元素和数组中最后一个元素进行对调,然后从堆顶从一次向下进行堆化,直到最后满足定义。这样完毕之后他还是满足完全二叉树的定义。图示如下:

        代码实现   

     1     def delete(self):
     2         if self.count == 0:
     3             return
     4         self.a[1], self.a[self.count] = self.a[self.count], self.a[1]        # 先交换最后一个和起始元素的位置
     5         self.count -= 1                      # 数量减一
     6         tem = 1                   # 第一个节点下标
     7         while True:
     8             index = tem                    # 记录下标
     9             if tem*2 <= self.count and self.a[tem] < self.a[tem*2]:        # 下面这两个判断主要是在tem的左右节点中选择一个最小的继续交换。
    10                 index = tem *2
    11             if tem*2+1 <= self.count and self.a[index] < self.a[tem*2+1]:
    12                 index = tem * 2+1
    13             if index == tem:                       # 没有发生交换,循环结束
    14                 break
    15             self.a[tem], self.a[index] = self.a[index], self.a[tem]      # 交换位置
    16             tem = index                          # 从当前下标继续开始

      复杂度分析:在对对进行操作时都不需要申请辅助空间,所以其空间复杂度为O(1),而对于时间复杂度,由于是以完全二叉树的性质进行存储的,从根节点到叶子节点的高度为log n  ,因此在堆化的过程中时间,无论是插入操作还是删除操作其时间复杂度为O(log n)。  

  • 相关阅读:
    独家首发Java品优购项目课程,20天课程,430个知识点!视频+资料全部免费领!
    Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合
    你还在 if...else?代码这样写才好看!
    关于破解
    菜鸟上路
    4、udtf、udf、udaf
    【模板】快速排序
    最短路问题:迪杰斯特拉算法(Dijsktra)
    最短路径问题:弗洛伊德算法(Floyd)
    栈的基本概念
  • 原文地址:https://www.cnblogs.com/GoodRnne/p/10692970.html
Copyright © 2011-2022 走看看