堆排序与树的缘分

堆排序与树的缘分

Created: March 9, 2022 7:14 PM
Introduction: 介绍堆排序与二叉树那斩不断的缘分
Source: 原创
Tags: 算法专栏


堆排序前传 - 树与二叉树

  • 树是一种数据结构 比如:目录结构
  • 树是一种可以递归定义的数据结构
  • 树是由n个节点组成的集合:
    • 如果n=0,那这是一棵空树;
    • 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mY0gA5NJ-1647415802999)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled.png)]

树的一些概念:

  • 根节点、叶子节点

    上图中,A就是整棵树的根节点,叶子节点就是不能分叉的节点,已经到了树的最末端了,上图中的叶子节点有B、C、H、I、P、Q、K、L、M、N;

  • 树的深度(高度)

    即树的层数,上图的树就有4层;

  • 树的度

    树的度是最大的节点的度,节点的度就是节点的分叉数;

  • 孩子节点/父节点

    举例说明:E是I的父节点,I是E的子节点;

  • 子树

    在整个树中取一部分出来,那部分即为子树。

二叉树

  • 二叉树:度不超过2的树;
  • 每个节点最多有两个孩子节点;
  • 两个孩子节点被区分为左孩子节点和右孩子节点
  • 满二叉树:一个二叉树,如果每一层的节点数都达到了最大值,则这个二叉树就是满二叉树;
  • 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qmZ7H61f-1647415803006)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled%201.png)]

如下图:(a)满二叉树 (b)完全二叉树 (c)和(d)非完全二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n34rJXV3-1647415803007)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled%202.png)]

堆的前传 - 二叉树的存储方式

二叉树的储存方式(表示方式)

  • 链式存储方式
  • 顺序储存方式(一般适用于完全二叉树)

使用二叉树的顺序存储细节与规律

二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点。因此,必须把二叉树的所有结点安排成为一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系,用编号的方法从树根起,自上层至下层,每层自左至右地给所有结点编号,缺点是有可能对存储空间造成极大的浪费,在最坏的情况下,一个深度为k且只有k个结点的右单支树需要2k-1个结点存储空间。

依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。

对于一般的二叉树,如果仍按从上至下和从左到右的顺序将树中的结点顺序存储在一维数组中,则数组元素下标之间的关系不能够反映二叉树中结点之间的逻辑关系,只有增添一些并不存在的空结点,使之成为一棵完全二叉树的形式,然后再用一维数组顺序存储。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8aU7Bl8-1647415803008)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled%203.png)]

                            (a) 一棵二叉树                                     (b) 改造后的完全二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lCvsjzXa-1647415803009)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled%204.png)]

                                               (c) 改造后完全二叉树顺序存储状态

显然,这种存储对于需增加许多空结点才能将一棵二叉树改造成为一棵完全二叉树的存储时,会造成空间的大量浪费,不宜用顺序存储结构。

那完全二叉树顺序存储规律又是什么呢?以右图为例:

  • 父节点和左孩子节点的编号下标有什么关系?

    0-1 1-3 2-5 3-7 4-9

    i → 2i+1

  • 父节点和右孩子节点的编号下标有什么关系

    0-2 1-4 2-6 3-8

    i → 2i+2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VjbrsXp5-1647415803010)(%E5%A0%86%E6%8E%92%E5%BA%8F%E4%B8%8E%E6%A0%91%E7%9A%84%E7%BC%98%E5%88%86%2078b12/Untitled%205.png)]


相关参考

清华大学博士讲解Python数据结构与算法:https://www.bilibili.com/video/BV1uA411N7c5?p=20

二叉树的存储方式【顺序储存(数组)、链式存储、邻接表存储等】:https://blog.csdn.net/qq_21989927/article/details/108666433

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
堆和二叉排序都是基于二叉数据结构,但它们有以下几点不同: 1. 堆和二叉排序的排序方式不同。堆是一种特殊的数据结构,它可以被看做是一棵完全二叉,且满足堆的性质:父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值。而二叉排序是一种二叉,它的每个节点的左子都比该节点小,右子都比该节点大。 2. 堆和二叉排序的插入和删除操作不同。堆的插入操作是将新元素插入到堆的末尾,然后通过上滤操作将其调整到合适的位置。删除操作是将堆顶元素删除,然后将堆的最后一个元素移到堆顶,再通过下滤操作将其调整到合适的位置。而二叉排序的插入和删除操作是通过比较节点的大小来进行的。 3. 堆和二叉排序的查找操作不同。堆只能查找堆顶元素,而不能像二叉排序那样进行中序遍历等操作。 4. 堆和二叉排序的应用场景不同。堆主要用于实现优先队列等数据结构,而二叉排序主要用于实现动态查找表等数据结构。 下面是一个堆的Python实现: ```python import heapq # 创建一个空堆 heap = [] # 插入元素 heapq.heappush(heap, 3) heapq.heappush(heap, 1) heapq.heappush(heap, 4) heapq.heappush(heap, 2) # 弹出堆顶元素 print(heapq.heappop(heap)) # 输出:1 # 查看堆顶元素 print(heap[0]) # 输出:2 ``` 下面是一个二叉排序的Python实现: ```python class TreeNode: def __init__(self, val): self.val = val self.left = None self.right = None class BST: def __init__(self): self.root = None # 插入节点 def insert(self, val): if not self.root: self.root = TreeNode(val) else: self._insert(val, self.root) def _insert(self, val, node): if val < node.val: if not node.left: node.left = TreeNode(val) else: self._insert(val, node.left) else: if not node.right: node.right = TreeNode(val) else: self._insert(val, node.right) # 查找节点 def search(self, val): return self._search(val, self.root) def _search(self, val, node): if not node: return False elif val == node.val: return True elif val < node.val: return self._search(val, node.left) else: return self._search(val, node.right) # 创建一个二叉排序 bst = BST() # 插入节点 bst.insert(3) bst.insert(1) bst.insert(4) bst.insert(2) # 查找节点 print(bst.search(2)) # 输出:True print(bst.search(5)) # 输出:False ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YuZou 邹宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值