当前位置: 首页 > news >正文

二叉树深度解析:从基础概念到算法实现与应用

一、二叉树的本质定义与核心特性

(一)递归定义与逻辑结构

二叉树是一种 严格有序的树结构,其递归定义为:

  1. 空树:不含任何结点的集合,是二叉树的特殊形态。
  2. 非空二叉树:由以下三部分组成:
  • 根结点(Root):唯一的顶层结点,没有父结点。
  • 左子树(Left Subtree):以根结点左子结点为根的二叉树。
  • 右子树(Right Subtree):以根结点右子结点为根的二叉树。

    关键特性

    • 度的限制:每个结点最多有 2 个子结点(度为 0、1 或 2),不存在度大于 2 的结点。
    • 有序性:左右子树顺序严格区分,例如仅有左子结点的树与仅有右子结点的树视为不同结构。
    • 五种基本形态

    (二)结点关系与术语

    • 父结点与子结点:若结点 A 的子结点为 B,则 A 是 B 的父结点,B 是 A 的左 / 右子结点。
    • 兄弟结点:具有相同父结点的结点互称兄弟,如 B 和 C 是兄弟。
    • 叶子结点与分支结点
      • 叶子结点:度为 0 的结点(无任何子结点)。
      • 分支结点:度为 1 或 2 的结点(至少有一个子结点)。
    • 层次与高度
      • 层次:根结点为第 1 层,子结点为第 2 层,依此类推。
      • 高度:树中结点的最大层次,如高度为 3 的二叉树最多有 7 个结点(满二叉树)。

    二、特殊二叉树:满二叉树与完全二叉树的对比分析

    (一)满二叉树(Perfect Binary Tree)

    • 数学定义:高度为 h 的满二叉树,每层结点数达到最大值 2h−1,结点总数为 2h−1。
    • 结构特点
      • 所有叶子结点位于最后一层(第 h 层)。
      • 不存在度为 1 的结点,每个分支结点都有左右两个子结点。
    • 示例(高度 3):


    结点数:2*3−1=7,每层结点数分别为 1、2、4。

    (二)完全二叉树(Complete Binary Tree)

    • 定义:高度为 h、结点数为 n 的二叉树,其结点与高度为 h 的满二叉树中前 n 个结点一一对应(按层序编号)。
    • 结构特点
      • 前 h−1 层是满二叉树,第 h 层结点从左到右连续排列,不允许中间有空缺。
      • 满二叉树是特殊的完全二叉树,但完全二叉树不一定是满二叉树。
    • 示例(高度 3,结点数 5):


      与满二叉树前 5 个结点(1,2,3,4,5)位置一致,第 3 层仅有左子结点。

    (三)核心区别对比

    特性满二叉树完全二叉树
    结点分布每层满结点前 h−1 层满,第 h 层左连续
    度为 1 的结点不存在(必为 0 或 2)最多一个,且只能在最后一层
    结点数公式2^h-1

    n∈[2^(h−1),2^h−1]

    三、存储结构:顺序存储 vs 链式存储的适用场景与实现细节

    (一)顺序存储(数组实现,适合完全二叉树)

    • 存储逻辑:用一维数组按层序存储结点,下标对应结点编号:
      • 根结点:下标 0
      • 结点 i 的左子结点:2i+1(若存在)
      • 结点 i 的右子结点:2i+2(若存在)
      • 结点 i 的父结点:(i−1)/2(i>0 时)
    • 示例(完全二叉树)


      数组存储:[1, 2, 3, 4, 5]
    • 非完全二叉树的空间浪费
      若树结构为单链(仅有左子结点),高度为 h 的树需 2h−1 个存储空间,实际仅用 h 个,空间利用率低。

    (二)链式存储(二叉链表,适合任意二叉树)

    • 结点结构
      typedef int BTDataType;
      typedef struct BinaryTreeNode {
          struct BinaryTreeNode* left;  // 左子结点指针
          struct BinaryTreeNode* right; // 右子结点指针
          BTDataType val;              // 数据域
      } BTNode;
      
    • 创建与连接结点
      // 创建新结点
      BTNode* BuyBTNode(BTDataType val) {
          BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
          if (!newNode) { 
              perror("malloc failed"); 
              return NULL; 
          }
          newNode->val = val;
          newNode->left = newNode->right = NULL; // 初始左右子结点为空
          return newNode;
      }
      
      // 构建二叉树示例:1为根,左子2,右子4,2的左子3,4的左子5、右子6
      BTNode* CreateTree() {
          BTNode* n1 = BuyBTNode(1);
          BTNode* n2 = BuyBTNode(2);
          BTNode* n3 = BuyBTNode(3);
          BTNode* n4 = BuyBTNode(4);
          BTNode* n5 = BuyBTNode(5);
          BTNode* n6 = BuyBTNode(6);
          n1->left = n2; 
          n1->right = n4; // 根结点连接左右子
          n2->left = n3; // 2号结点仅有左子
          n4->left = n5; n4->right = n6; // 4号结点有左右子
          return n1; // 返回根结点
      }
      
    • 优缺点
      • 优点:动态灵活,无空间浪费,适合频繁插入 / 删除。
      • 缺点:需额外指针空间(每个结点两个指针),实现复杂度高于顺序存储。

    四、遍历方式:递归遍历与层序遍历的实现与原理

    (一)深度优先遍历(DFS,递归实现)

    1. 前序遍历(根→左→右)
    • 访问顺序:先访问根结点,再递归前序遍历左子树,最后递归前序遍历右子树。
    • 代码实现
      void PreOrder(BTNode* root) {
          if (root == NULL) { 
              printf("N "); // 空结点标记为"N"
              return; 
          }
          printf("%d ", root->val); // 访问根
          PreOrder(root->left);     // 递归左子树
          PreOrder(root->right);    // 递归右子树
      }
      
    • 示例输出(树结构同上):1 2 3 N N N 4 5 N N 6 N N
      (注:N 表示空结点,实际应用中可省略,此处为展示结构)
    2. 中序遍历(左→根→右)
    • 访问顺序:先递归中序遍历左子树,再访问根结点,最后递归中序遍历右子树。
    • 代码实现
      void InOrder(BTNode* root) {
          if (root == NULL) { 
              printf("N "); 
              return; 
          }
          InOrder(root->left);    // 递归左子树
          printf("%d ", root->val); // 访问根
          InOrder(root->right);   // 递归右子树
      }
      
    • 示例输出N 3 N 2 N 1 N 5 N 4 N 6 N
      (实际非空结点中序:3 2 1 5 4 6)
    3. 后序遍历(左→右→根)
    • 访问顺序:先递归后序遍历左子树,再递归后序遍历右子树,最后访问根结点。
    • 代码实现
      void PostOrder(BTNode* root) {
          if (root == NULL) { 
              printf("N "); 
              return; 
          }
          PostOrder(root->left);   // 递归左子树
          PostOrder(root->right);  // 递归右子树
          printf("%d ", root->val); // 访问根
      }
      
    • 示例输出N N 3 N N 2 N N 5 N 6 4 1
      (实际非空结点后序:3 2 5 6 4 1)

    (二)广度优先遍历(BFS,层序遍历)

    • 实现原理:借助队列,按层从上到下、每层从左到右访问结点。
    • 代码步骤
      1. 根结点入队。
      2. 循环取出队首结点,访问后将其左右子结点(若存在)入队。
      3. 直到队列为空。
    • 代码实现(假设队列已实现):
      void LevelOrder(BTNode* root) {
          Queue q;
          QueueInit(&q); // 初始化队列
          if (root) QueuePush(&q, root); // 根结点入队(非空时)
          
          while (!QueueEmpty(&q)) {
              BTNode* node = QueueFront(&q); // 取队首结点
              QueuePop(&q);                  // 出队
              printf("%d ", node->val);      // 访问结点
              
              // 左右子结点入队
              if (node->left)  
                  QueuePush(&q, node->left);
              if (node->right) 
                  QueuePush(&q, node->right);
          }
          
          QueueDestroy(&q); // 销毁队列,释放内存
      }
      
    • 示例输出(树结构同上):1 2 4 3 5 6

    五、堆:基于完全二叉树的高效数据结构

    (一)堆的定义与性质

    • 定义:满足 “堆性质” 的完全二叉树,分为两种类型:
      • 大根堆:父结点值 ≥ 子结点值(根结点为最大值)。
      • 小根堆:父结点值 ≤ 子结点值(根结点为最小值)。
    • 存储结构:用数组实现,下标对应完全二叉树结点编号。
    • 堆性质:对于数组下标 i,有:
      • 大根堆:a[i]≥a[2i+1] 且 a[i]≥a[2i+2](若子结点存在)。
      • 小根堆:a[i]≤a[2i+1] 且 a[i]≤a[2i+2]。

    (二)核心操作:向上调整与向下调整

    1. 向上调整(插入新结点时恢复堆性质)
    • 场景:新结点插入堆尾(数组末尾),可能破坏堆性质,需与父结点比较并交换。
    • 算法步骤
      1. 计算新结点下标 child,父结点 parent=(child−1)/2。
      2. 若 child>0 且新结点值大于父结点(大根堆),交换两者,更新 child=parent,重复直至堆性质满足。
    • 代码实现(大根堆)
      void AdjustUp(HPDataType* a, int child) {
          int parent = (child - 1) / 2;
          while (child > 0) { // 未到根结点
              if (a[child] > a[parent]) { // 若子结点大于父结点(大根堆条件)
                  Swap(&a[child], &a[parent]); // 交换
                  child = parent;             // 继续向上检查
                  parent = (child - 1) / 2;
              } else {
                  break; // 已满足堆性质,停止
              }
          }
      }
      
    2. 向下调整(删除堆顶时恢复堆性质)
    • 场景:堆顶元素删除后,将堆尾元素移至堆顶,需与子结点比较并交换。
    • 算法前提:左右子树已满足堆性质。
    • 算法步骤
      1. 设当前结点为 parent,左子结点 child=2parent+1。
      2. 找到左右子结点中较大者(大根堆),若较大子结点值大于父结点,交换两者,更新 parent=child,重复直至堆性质满足。
    • 代码实现(大根堆)
      void AdjustDown(HPDataType* a, int n, int parent) {
          int child = 2 * parent + 1; // 左子结点下标
          while (child < n) { // 子结点存在
              // 若右子结点存在且更大,选右子结点
              if (child + 1 < n && a[child + 1] > a[child]) {
                  child++;
              }
              // 若子结点大于父结点,交换
              if (a[child] > a[parent]) {
                  Swap(&a[child], &a[parent]);
                  parent = child;
                  child = 2 * parent + 1;
              } else {
                  break; // 已满足堆性质,停止
              }
          }
      }
      

    (三)堆排序:高效的原地排序算法

    • 步骤(升序排序,构建大根堆)
      1. 建堆:对数组从最后一个分支结点开始,自底向上调用 AdjustDown,时间复杂度 O(n)。
      2. 排序:每次将堆顶(最大值)与堆尾元素交换,堆大小减 1,对新堆顶调用 AdjustDown,重复直至堆空。
    • 代码框架
      void HeapSort(int* a, int n) {
          // 1. 建大根堆
          for (int i = (n-1-1)/2; i >= 0; i--) { // 最后一个分支结点的父结点
              AdjustDown(a, n, i);
          }
          
          // 2. 排序:交换堆顶与堆尾,调整堆
          int end = n - 1;
          while (end > 0) {
              Swap(&a[0], &a[end]); // 最大值放到末尾
              AdjustDown(a, end, 0); // 调整前end个元素为堆
              end--;
          }
      }
      
    • 时间复杂度:建堆 O(n) + 排序 O(nlogn),总复杂度 O(nlogn)。

    六、二叉树的重要性质与经典问题求解

    (一)核心性质推导

    1. 叶子结点数公式:n0​=n2​+1
    • 推导过程
      设二叉树中度为 0、1、2 的结点数分别为 n0​、n1​、n2​,总结点数 n=n0​+n1​+n2​。
      二叉树边数 E=n−1(每个结点除根外有且仅有一个父结点),同时边数等于各结点度之和:E=0⋅n0​+1⋅n1​+2⋅n2​。
      联立得:n0​+n1​+n2​−1=n1​+2n2​,化简得 n0​=n2​+1。
    2. 完全二叉树高度计算
    • 公式:高度 h=⌊log2​n⌋+1,其中 n 为结点数。
    • 推导:高度为 h 的完全二叉树结点数范围:2h−1≤n≤2h−1,取对数得 h−1≤log2​n<h,故 h=⌊log2​n⌋+1。

    (二)经典例题解析

    例题 1:某二叉树有 399 个结点,其中 199 个度为 2 的结点,求叶子结点数。

    • 解答:根据 n0​=n2​+1=199+1=200

    例题 2:判断完全二叉树(层序序列 ABCDEFGH)的前序序列。

    • 分析:完全二叉树结构如下:


    前序遍历顺序:A → B → D → H → E → C → F → G

    七、实战应用:判断完全二叉树与销毁二叉树

    (一)判断完全二叉树(层序遍历法)

    • 算法思路
      1. 层序遍历二叉树,遇到第一个空结点后,后续结点必须全为空。
      2. 用队列记录结点,当某结点为空时,标记 “已进入空结点阶段”,后续若再遇到非空结点,返回 false。
    • 代码实现
      bool IsCompleteBinaryTree(BTNode* root) {
          Queue q;
          QueueInit(&q);
          if (root) QueuePush(&q, root); // 根结点入队(非空时)
          
          bool hasNull = false; // 是否已遇到空结点
          
          while (!QueueEmpty(&q)) {
              BTNode* node = QueueFront(&q);
              QueuePop(&q);
              
              if (node == NULL) {
                  hasNull = true; // 标记进入空结点阶段
              } else {
                  if (hasNull) {
                      // 空结点之后出现非空结点,非完全二叉树
                      QueueDestroy(&q);
                      return false;
                  }
                  // 非空结点,左右子结点入队(空结点也需入队,用于标记)
                  QueuePush(&q, node->left);
                  QueuePush(&q, node->right);
              }
          }
          
          QueueDestroy(&q);
          return true;
      }
      

    (二)销毁二叉树(递归释放内存)

    • 注意事项:二叉树结点通过动态内存分配创建,需递归释放每个结点及其子树。
    • 代码实现
      void DestroyBinaryTree(BTNode** root) {
          if (*root == NULL) return; // 空树直接返回
          
          // 先销毁左子树和右子树
          DestroyBinaryTree(&(*root)->left);
          DestroyBinaryTree(&(*root)->right);
          
          // 释放当前结点内存
          free(*root);
          *root = NULL; // 防止野指针
      }
      

    八、总结与拓展方向

    (一)核心价值

    二叉树是理解树结构的基础,其递归定义和层次特性支撑了大量算法:

    • 理论层面:满二叉树与完全二叉树的数学性质为算法分析提供依据。
    • 应用层面:堆排序、TOP-K 问题、文件系统目录结构、编译器语法树等均依赖二叉树思想。

    (二)拓展学习建议

    1. 算法题实践

                    对称二叉树、二叉树的中序遍历 、 二叉树的前序遍历 、相同的树​​​​​ 、 另一棵树的子树

    1. 数据结构进阶
      • 二叉搜索树(BST)、AVL 树、红黑树(自平衡二叉树)。
      • 哈夫曼树(带权路径最短的二叉树,用于压缩算法)。
    2. 工程实现
      • 用 C++ 模板实现链式二叉树,支持动态插入、删除、遍历。
      • 用 Python 实现堆结构,解决实际场景中的 TOP-K 问题。

    附录

    二叉树

    //Queue.h
    #pragma once
    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    #include<stdbool.h>
    
    //typedef int QDataType;
    typedef struct BinaryTreeNode* QDataType;
    typedef struct QListNode
    {
    	struct QListNode* next;
    	QDataType data;
    }QNode;
    
    // 队列的结构 
    typedef struct Queue
    {
    	QNode* front;
    	QNode* rear;
    }Queue;
    
    // 初始化队列 
    void QueueInit(Queue* q);
    // 队尾入队列 
    void QueuePush(Queue* q, QDataType data);
    // 队头出队列 
    void QueuePop(Queue* q);
    // 获取队列头部元素 
    QDataType QueueFront(Queue* q);
    // 获取队列队尾元素 
    QDataType QueueBack(Queue* q);
    // 获取队列中有效元素个数 
    int QueueSize(Queue* q);
    // 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
    bool QueueEmpty(Queue* q);
    // 销毁队列 
    void QueueDestroy(Queue* q);
    
    //Tree.h
    #pragma once
    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    #include<stdbool.h>
    #include<math.h>
    typedef char BTDataType;
    typedef struct BinaryTreeNode
    {
    	BTDataType data;
    	struct BinaryTreeNode* left;
    	struct BinaryTreeNode* right;
    }BTNode;
    BTNode* BuyBTNode(BTDataType x);
    void PreOrder(BTNode* root);
    void InOrder(BTNode* root);
    void PostOrder(BTNode* root);
    int BTNSize(BTNode* root);
    int BTLSize(BTNode* root);
    int BTLKSize(BTNode* root, int k);
    int BTDepth(BTNode* root);
    BTNode* BTFind(BTNode* root, BTDataType x);
    void BTDestroy(BTNode**root);
    void LevelOrder(BTNode* root);
    bool BTComplete(BTNode* root);
    //Queue.c
    #define _CRT_SECURE_NO_WARNINGS 1
    #include"Queue.h"
    // 初始化队列 
    void QueueInit(Queue* q)
    {
    	q->front = q->rear = NULL;
    }
    // 队尾入队列 
    void QueuePush(Queue* q, QDataType data)
    {
    	QNode* newnode = (QNode*)malloc(sizeof(QNode));
    	if (newnode == NULL)
    	{
    		perror("malloc fail1");
    		exit(1);
    	}
    	newnode->data = data;
    	newnode->next = NULL;
    	if (q->front == NULL)
    	{
    		q->front = q->rear = newnode;
    	}
    	else
    	{
    		q->rear->next = newnode;
    		q->rear = newnode;
    	}
    }
    // 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
    bool QueueEmpty(Queue* q)
    {
    	assert(q);
    	return q->front == NULL;
    }
    // 队头出队列 
    void QueuePop(Queue* q)
    {
    	assert(!QueueEmpty(q));
    	if (q->front == q->rear)
    	{
    		free(q->front);
    		q->front = q->rear = NULL;
    	}
    	else
    	{
    		QNode* tmp = q->front->next;
    		free(q->front);
    		q->front = tmp;
    	}
    }
    // 获取队列头部元素 
    QDataType QueueFront(Queue* q)
    {
    	assert(!QueueEmpty(q));
    	return q->front->data;
    }
    // 获取队列队尾元素 
    QDataType QueueBack(Queue* q)
    {
    	assert(!QueueEmpty(q));
    	return q->rear->data;
    
    }
    // 获取队列中有效元素个数 
    int QueueSize(Queue* q)
    {
    	assert(q);
    	int count = 0;
    	QNode* tmp = q->front;
    	while (tmp != NULL)
    	{
    		count++;
    		tmp = tmp->next;
    	}
    	return count;
    }
    // 销毁队列 
    void QueueDestroy(Queue* q)
    {
    	assert(q);
    	QNode* pcur = q->front;
    	while (q->front)
    	{
    		QNode* tmp = q->front->next;
    		free(q->front);
    		q->front = tmp;
    	}
    	q->front = q->rear = NULL;
    }
    
    //Tree.c
    #define _CRT_SECURE_NO_WARNINGS 1
    #include"Tree.h"
    #include"Queue.h"
    BTNode* BuyBTNode(BTDataType x)
    {
    	BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
    	if (tmp == NULL)
    	{
    		perror("malloc fail");
    		exit(1);
    	}
    	tmp->data = x;
    	tmp->left = tmp->right = NULL;
    	return tmp;
    }
    void PreOrder(BTNode* root)
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return ;
    	}
    	printf("%c ", root->data);
    	PreOrder(root->left);
    	PreOrder(root->right);
    }
    void InOrder(BTNode* root)
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return ;
    	}
    	InOrder(root->left);
    	printf("%c ", root->data);
    	InOrder(root->right);
    }
    void PostOrder(BTNode* root)
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return ;
    	}
    	PostOrder(root->left);
    	PostOrder(root->right);
    	printf("%c ", root->data);
    }
    int BTNSize(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    
    	return 1 + BTNSize(root->left) + BTNSize(root->right);
    }
    int BTLSize(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    	if ((root->left == NULL) && (root->right == NULL))
    	{
    		return 1;
    	}
    	return BTLSize(root->left) + BTLSize(root->right);
    }
    int BTLKSize(BTNode* root, int k)
    
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    	if (k == 1)
    	{
    		return 1;
    	}
    	return BTLKSize(root->left, k--) + BTLKSize(root->right, k--);
    }
    int BTDepth(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    	int ld = BTDepth(root->left);
    	int rd = BTDepth(root->right);
    	return 1 + (ld > rd ? ld : rd);
    }
    BTNode* BTFind(BTNode* root, BTDataType x)
    {
    	if (root == NULL)
    	{
    		return NULL;
    	}
    	if (root->data == x)
    	{
    		return root;
    	}
    	BTNode* left=BTFind(root->left,x);
    	if (left)
    	{
    		return left;
    	}
    	BTNode* right=BTFind(root->right,x);
    	if (right)
    	{
    		return right;
    	}
    	return NULL;
    }
    void BTDestroy(BTNode** root)
    {
    	if (root == NULL)
    	{
    		return;
    	}
    	BTDestroy(&((*root)->left));
    	BTDestroy(&((*root)->right));
    	free(*root);
    	*root = NULL;
    }
    void LevelOrder(BTNode* root)
    {
    	Queue q;
    	QueueInit(&q);
    	QueuePush(&q, root);
    	while (!QueueEmpty(&q))
    	{
    		BTNode* top = QueueFront(&q);
    		QueuePop(&q);
    		printf("%c ", top->data);
    		if (top->left)
    			QueuePush(&q, top->left);
    		if (top->right)
    			QueuePush(&q, top->right);
    	}
    	QueueDestroy(&q);
    }
    bool BTComplete(BTNode* root)
    {
    	Queue q;
    	QueueInit(&q);
    	QueuePush(&q, root);
    	while (!QueueEmpty(&q))
    	{
    		BTNode* top = QueueFront(&q);
    		QueuePop(&q);
    		if (top == NULL)
    		{
    			break;
    		}
    		QueuePush(&q, top->left);
    		QueuePush(&q, top->right);
    	}
    	while (!QueueEmpty(&q))
    	{
    		BTNode* top = QueueFront(&q);
    		QueuePop(&q);
    		if (top != NULL)
    		{
    			QueueDestroy(&q);
    			return false;
    		}
    	}
    	QueueDestroy(&q);
    	return true;
    }

    //Heap.h
    #pragma once
    #include<stdio.h>
    #include<stdlib.h>
    #include<assert.h>
    typedef int HPDataType;
    typedef struct Heap
    {
    	HPDataType* arr;
    	int size;
    	int capacity;
    }HP;
    // 堆的初始
    void HPInit(HP* ph);
    // 堆的销毁
    void HPDestroy(HP* ph);
    // 堆的插入
    void HPPush(HP* ph, HPDataType x);
    // 堆的删除
    void HPPop(HP* ph);
    // 取堆顶的数据
    HPDataType HeapTop(HP* ph);
    // 堆的数据个数
    int HPSize(HP* ph);
    // 堆的判空
    int HPEmpty(HP* ph);
    //void HPPrint(HP* ph);
    void HPPrint(HPDataType* arr, int size);
    void HPjustUp(HPDataType* arr, int child);
    //void HPjustDown(HPDataType* arr, int n);
    void HPjustDown(HPDataType* arr, int parent, int n);
    void Swap(HPDataType* a, HPDataType* b);
    //Heap.c
    #define _CRT_SECURE_NO_WARNINGS 1
    #include"Heap.h"
    void HPInit(HP* ph)
    {
    	assert(ph);
    	ph->arr = NULL;
    	ph->capacity = ph->size = 0;
    }
    void HPDestroy(HP* ph)
    {
    	assert(ph);
    	if (ph->arr == NULL)
    		free(ph->arr);
    	ph->arr = NULL;
    	ph->capacity = ph->size = 0;
    }
    void Swap(HPDataType* a, HPDataType* b)
    {
    	HPDataType tmp = *a;
    	*a = *b;
    	*b = tmp;
    }
    void HPjustUp(HPDataType* arr,int child)
    {
    	int parent = (child - 1) / 2;
    	while (child>0)
    	{
    		if (arr[child] < arr[parent])
    		{
    			Swap(&arr[child], &arr[parent]);
    			child = parent;
    			parent = (child - 1) / 2;
    		}
    		else
    		{
    			break;
    		}
    	}
    }
    void HPPush(HP* ph, HPDataType x)
    {
    	assert(ph);
    	if (ph->capacity == ph->size)
    	{
    		int newcapacity = ph->capacity == 0 ? 1 : 2 * ph->capacity;
    		HPDataType* tmp = (HPDataType*)realloc(ph->arr, newcapacity * sizeof(HPDataType));
    		if (tmp == NULL)
    		{
    			perror("relloc fail");
    			exit(1);
    		}
    		ph->arr = tmp;
    		ph->capacity = newcapacity;
    	}
    	ph->arr[ph->size] = x;
    	HPjustUp(ph->arr, ph->size);
    	++ph->size;
    }
    int HPEmpty(HP* ph)
    {
    	assert(ph);
    	return ph->size==0;
    }
    void HPjustDown(HPDataType* arr,int parent, int n)
    {
    	int child = parent * 2 + 1;
    	while (child < n)
    	{
    		if (child + 1 < n && arr[child] > arr[child + 1])
    		{
    			child++;
    		}
    		if (arr[child] < arr[parent])
    		{
    			Swap(&arr[child], &arr[parent]);
    			parent = child;
    			child = parent * 2 + 1;
    		}
    		else
    		{
    			break;
    		}
    	}
    }
    void HPPop(HP* ph)
    {
    	assert(!HPEmpty(ph));
    	Swap(&ph->arr[0], &ph->arr[ph->size-1]);
    	ph->size--;
    	HPjustDown(ph->arr,0 , ph->size);
    }
    HPDataType HeapTop(HP* ph)
    {
    	assert(!HPEmpty(ph));
    	return ph->arr[0];
    }
    int HPSize(HP* ph)
    {
    	assert(!HPEmpty(ph));
    	return ph->size;
    }
    //void HPPrint(HP* ph)
    //{
    //	assert(!HPEmpty(ph));
    //	int tmp = 0;
    //	while (tmp<ph->size)
    //	{
    //		printf("%d ", ph->arr[tmp]);
    //		tmp++;
    //	}
    //	printf("\n");
    //}
    void HPPrint(HPDataType* arr, int size)
    {
    	int tmp = 0;
    	while (tmp < size)
    	{
    		printf("%d ", arr[tmp]);
    		tmp++;
    	}
    	printf("\n");
    }

    相关文章:

  • 04--网络属性设置与多路复用
  • 【HD-RK3576-PI】VNC 远程桌面连接
  • Spark RDD算子详解:从入门到精通
  • Cygwin中链接非标准名动态库
  • 05--MQTT物联网协议
  • hyper-v server服务器部署远程访问(我目前环境:hyper-v服务器+路由器+公网ip)
  • Cesium.js(6):Cesium相机系统
  • 揭开人工智能与机器学习的神秘面纱:开发者的视角
  • 19【动手学深度学习】卷积层
  • Grafana将弃用AngularJS-我们该如何迁移
  • Raymarching Textures In Depth
  • Android Jetpack Compose 高级开发核心技术
  • 如何优化多线程上下文切换?
  • 【AM2634】启动和调试
  • C++标识符:检查是否和保留字冲突
  • SQL 语句说明
  • 从竞速到巡检:不同无人机如何匹配最佳PCB方案?
  • 基于若依和elementui实现文件上传(导入Excel表)
  • Cygwin中使用其它平台生成的动态库
  • 【连载3】基础智能体的进展与挑战综述
  • 德州万企网站建设/手机如何创建网站
  • 视频网站建站免费/软文推广怎么做
  • 口碑好的网站设计制作价格/百度霸屏推广
  • 中国城乡建中国城乡建设部网站/长尾关键词爱站网
  • 哪个网站可以做卖房/网络产品运营与推广
  • 防伪码做网站的还能没导入吗/网络市场调研的方法