【C++习题集】-- 堆

(用于复习)

目录

树概念及结构

名词概念

二叉树概念及结构

特殊的二叉树

满二叉树

完全二叉树

运算性质

二叉树存储结构

顺序存储

链式存储

堆 - 顺序存储

堆的性质

堆的实现

堆的应用

堆排序

直接建堆法


树概念及结构

        概念非线性的数据结构(形成的倒挂似树的结构 - 根朝上,叶朝下,子树之间不能有交集)。

名词概念

  • 节点的度一个节点含有的子树的个数称为该节点的度。
  • 叶节点或终端节点:度为0的节点称为叶节点。
  • 非终端节点或分支节点:度不为0的节点。
  • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点。
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点。
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点。
  • 树的度一棵树中,最大的节点的度称为树的度。
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。
  • 树的高度或深度树中节点的最大层次。
  • 堂兄弟节点:双亲在同一层的节点互为堂兄弟。
  • 节点的祖先:从根到该节点所经分支上的所有节点。
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  • 森林:由m(m>0)棵互不相交的树的集合称为森林。

二叉树概念及结构

        由一个根节点加上两棵别称为左子树和右子树的二叉树组成 - 子树可为空。

  • 不存在度大于2的结点。

特殊的二叉树

满二叉树

        每一个层的结点数都达到最大值,则结点总数:2^k - 1(K层数)

完全二叉树

        特殊的完全二叉树 - 最后一层不满,但是是左到右是连续的

        (满二叉树是特殊的完全二叉树)

运算性质

  • 根节点的层数为1,则第i层上最多有2^(i - 1)个结点
  • 根节点的层数为1,则深度h的最大结点数是2^h - 1
  • 根节点的层数为1,n个结点的满二叉树的深度h = log2(n + 1)
  • 如果度为0其叶结点个数为n,度为2的分支结点个数为m,则有:n = m + 1
  • n个结点的完全二叉树,以数组顺序对所有节点开始编号:
  1. 若i>0,i位置节点的双亲序号:(i - 1) / 2
  2. 若2i + 1 < n,左孩子序号:2i + 1,2i + 1 >= n否则无左孩子
  3. 若2i + 2 < n,右孩子序号:2i + 2,2i + 2 >= n否则无右孩子

一个具有767个节点的完全二叉树,其叶子节点个数为()

A、383
B、384
C、385
D、386
------------------------------------------
正确答案:B
------------------------------------------
解析:
        不要只想最后一层,倒数第二层也是会有叶子节点的。
首先以:

        可以推算出是第1 ~ 9层为满二叉树,对应节点数:511。可以知道最后一层一定为叶子节点:256个。

        然后根据完全二叉树是最后一层不满,但是是左到右是连续的,于是256 / 2 = 128,所以倒数第二层有128个是最后一层的父节点。

 再根据:

        可知倒数第二层有256个节点,于是叶子节点:256 + 256 - 128 = 384。

二叉树存储结构

顺序存储

        用数组来存储,适合表示完全二叉树。

  • 物理上:数组
  • 逻辑上:二叉树

链式存储

        链表来表示一棵二叉树。

  • 二叉链:数据域和左右指针域
  • 三叉链:数据域和左右上指针域

堆 - 顺序存储

        堆是一种特殊的完全二叉树,只不过父亲与儿子节点间有关系。顺序存储的完全二叉树典型的就是堆。(普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储)

堆的性质

  • 堆中某个节点的值总是不大于或不小于其父节点的值
    • 小堆:父亲位,比孩子位,要小
    • 大堆:父亲位,比孩子位,要大
  • 堆总是一棵完全二叉树

堆的实现

#include <iostream>
#include <cassert>namespace qcr_heap
{typedef int HeapType;struct Heap{int64_t _capacity; // 动态开辟可用大小int64_t _size;     // 实际数据占用大小HeapType *_array;  // 动态开辟一维数组};/********** 初始化堆*********/void HeapInit(Heap *heap){assert(heap);heap->_capacity = 0;heap->_size = 0;heap->_array = 0;}/********** 销毁堆*********/void HeapDestory(Heap *heap){assert(heap);heap->_capacity = 0;heap->_size = 0;free(heap->_array);heap->_array = nullptr;}/********** 小根堆*********/bool less(HeapType element_1, HeapType element_2){return element_1 < element_2;}/********** 大根堆*********/bool greater(HeapType element_1, HeapType element_2){return element_1 > element_2;}/********** 交换数据*********/void swap(HeapType *element_1, HeapType *element_2){HeapType tmp = *element_1;*element_1 = *element_2;*element_2 = tmp;}/****************************** 向上调整*   heap: 输入型参数,堆地址*   child: 输入型参数,排序的插入节点*   Func: 输入型参数,大小堆*****************************/void AdjustUp(Heap *heap, int64_t child, bool (*Func)(HeapType, HeapType)){assert(heap);int64_t parent = (child - 1) / 2;while (child > 0){if (Func(heap->_array[child], heap->_array[parent])){swap(&(heap->_array[child]), &(heap->_array[parent]));child = parent;parent = (child - 1) / 2;}elsebreak;}}/****************************** 向下调整*   heap: 输入型参数,堆地址*   root: 输入型参数,排序的根节点*   Func: 输入型参数,大小堆*****************************/void AdjustDown(Heap *heap, int64_t root, bool (*Func)(HeapType, HeapType)){assert(heap);int64_t parent = root;int64_t child = parent * 2 + 1;while (child < heap->_size){if (child + 1 < heap->_size && Func(heap->_array[child + 1], heap->_array[child])){child++;}if (Func(heap->_array[child], heap->_array[parent])){swap(&(heap->_array[child]), &(heap->_array[parent]));parent = child;child = parent * 2 + 1;}else{break; // 符合堆就成立了,就没必要进行交换了。}}}/****************************** 存入数据*   heap: 输入型参数,堆地址*   data: 输入型参数,插入的数据*   Func: 输入型参数,大小堆*****************************/void HeapPush(Heap *heap, HeapType data, bool (*Func)(HeapType, HeapType)){assert(heap);if (heap->_capacity == heap->_size){int64_t newcapacity = heap->_capacity == 0 ? 5 : heap->_capacity * 2;HeapType * tmp = (HeapType *)realloc(heap->_array, heap->_capacity*sizeof(HeapType);if (tmp == nullptr){printf("Capacuty Get Error!\n");exit(-1);}heap->_array = tmp;heap->_capacity = newcapacity;}heap->_array[heap->_size] = data;AdjustUp(heap, heap->_size, Func);(heap->_size)++;}/****************************** 按顺序全部输出*   heap: 输入型参数,堆地址*****************************/void HeapPrint(Heap *heap){assert(heap);for (uint64_t i = 0; i < heap->_size; i++){std::cout << heap->_array[i] << " ";}std::cout << '\n';}/****************************** 首元素*   heap: 输入型参数,堆地址*****************************/HeapType HeapTop(Heap *heap){assert(heap);assert(heap->_size > 0);return heap->_array[0];}/****************************** 判空*   heap: 输入型参数,堆地址*****************************/bool HeapEmpty(Heap *heap){assert(heap);return heap->_size == 0;}/****************************** 有效数据个数*   heap: 输入型参数,堆地址*****************************/int HeapSize(Heap *heap){assert(heap);return heap->_size;}/****************************** 判空*   heap: 输入型参数,堆地址*   Func: 输入型参数,大小堆*****************************/void HeapPop(Heap *heap, bool (*Func)(HeapType, HeapType)){assert(heap);assert(heap->_size > 0);heap->_array[0] = heap->_array[heap->_size - 1];(heap->_size)--;AdjustDown(heap, 0, Func);}
}
已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是()
A、1
B、2
C、3
D、4
------------------------------------------
正确答案:B
------------------------------------------
解析:
        首先我们需要知道,删除对应的调整算法是向下调整,所以其实在比较中有一个很重要的一项就是左右节点的比较,于是此处本质上的比较是需要在加上一次左右节点的比较。

堆的应用

堆排序

        利用堆删除思想来进行排序。

TOP-K问题

1. 用数据集合中前K个元素来建堆

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆
2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
面试题 17.14. 最小K个数 - 力扣(LeetCode)

class Solution
{
public:// 向上建堆void adjustUp(vector<int> &nums, int child){int parent = (child - 1) / 2;while (child > 0){if (nums[child] > nums[parent]){swap(nums[child], nums[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}}// 向下建堆void adjustDown(vector<int> &nums, int parent){int child = parent * 2 + 1;while (child < nums.size()){if (child + 1 < nums.size() && nums[child + 1] > nums[child]){child++;}if (nums[child] > nums[parent]){swap(nums[child], nums[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}// 堆排序的TOP-k问题vector<int> smallestK(vector<int> &arr, int k){vector<int> nums;nums.reserve(k);// 前K个元素来建堆for (int i = 0; i < k; i++){nums.push_back(arr[i]);adjustUp(nums, nums.size() - 1);}// 对比堆顶元素if (k != 0){for (int i = k; i < arr.size(); i++){if (arr[i] < nums[0]){nums[0] = arr[i];adjustDown(nums, 0);}}}return nums;}
};

        并不是最优的,并且还实现了两个堆算法,编码效率过低。

直接建堆法

        原本利用向上建堆的方式,是并不够完美的,建堆的时间复杂度为O(N)。

        而直接建堆法时间复杂度O(logn),其根本是利用向下建堆实现。

for (int i = (size - 1 - 1) / 2; i >= 0; i--)
{ADjustDown(nums, i);
}
class Solution
{
public:// 向下建堆void adjustDown(vector<int> &nums, int parent){int child = parent * 2 + 1;while (child < nums.size()){if (child + 1 < nums.size() && nums[child + 1] > nums[child]){child++;}if (nums[child] > nums[parent]){swap(nums[child], nums[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}// 堆排序的TOP-k问题vector<int> smallestK(vector<int> &arr, int k){vector<int> nums;nums.reserve(k);// 前K个元素来建堆for (int i = 0; i < k; i++){nums.push_back(arr[i]);}for(int i = (k - 1 - 1) / 2; i >= 0; i--){adjustDown(nums, i);}// 对比堆顶元素if (k != 0){for (int i = k; i < arr.size(); i++){if (arr[i] < nums[0]){nums[0] = arr[i];adjustDown(nums, 0);}}}return nums;}
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/46519.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编写Dockerfile制作自己的镜像并推送到私有仓库

说明&#xff1a;我将用到的私有仓库是Harbor&#xff0c;安装教程参考我的这一篇文章&#xff1a; 安装搭建私有仓库Harbor_Word_Smith_的博客-CSDN博客 一、案例1 1、要求 编写Dockerfile制作Web应用系统nginx镜像&#xff0c;生成镜像nginx:v1.1&#xff0c;并推送其到私…

【算法题】7004. 判别首字母缩略词

题目&#xff1a; 给你一个字符串数组 words 和一个字符串 s &#xff0c;请你判断 s 是不是 words 的 首字母缩略词 。 如果可以按顺序串联 words 中每个字符串的第一个字符形成字符串 s &#xff0c;则认为 s 是 words 的首字母缩略词。例如&#xff0c;“ab” 可以由 [“a…

【2023】LeetCode HOT 100——哈希

目录 1. 两数之和1.1 C++实现1.2 Python实现1.3 时空分析2. 字母异位词分组2.1 C++实现2.2 Python实现2.3 时空分析3. 最长连续序列3.1 C++实现3.2 Python实现3.3 时空分析1. 两数之和 🔗 原题链接:1. 两数之和 不妨设 i

电子商务防火墙的作用

1.作为网络安全的屏障 只有经过精心选择的应用协议才能通过防火墙&#xff0c;可使网络环境变得更安全。如 防火墙可以禁止 NFS 协议进出受保护的网络&#xff0c;这样外部的攻击者就不可能利用这些 脆弱的协议来攻击内部网络。防火墙同时可以保护网络免受基于路由的攻击&am…

【高级IO】- 五种 IO 模型 | 多路转接 - select

目录 IO的基本概念 什么是高效的IO&#xff1f; 五种IO模型 阻塞IO 非阻塞IO 信号驱动IO IO多路转接 异步IO 同步通信VS异步通信&#xff08;synchronous communication / asynchronous communication&#xff09; 同步通信VS同步与互斥 阻塞VS非阻塞 其他高级IO …

JavaScript高级

1、JavaScript面向对象 1.1、面向对象介绍 ​ 在 Java 中我们学习过面向对象&#xff0c;核心思想是万物皆对象。在 JavaScript 中同样也有面向对象。思想类似。 1.2、类的定义和使用 结构说明 代码实现 <!DOCTYPE html> <html lang"en"> <head>…

十问华为云 Toolkit:开发插件如何提升云上开发效能

众所周知&#xff0c;桌面集成开发环境&#xff08;IDE&#xff09;已经融入到开发的各个环节&#xff0c;对开发者的重要性和广泛度是不言而喻的&#xff0c;而开发插件更是建立在IDE基础上的功能Buff。 Huawei Cloud ToolKit作为华为云围绕其产品能力向开发者桌面上的延伸&a…

市值缩水80%,从光学到声学,瑞声科技押宝汽车赛道?

进入智能汽车赛道&#xff0c;并非上市公司的「良药」。通过海外收购的走捷径模式&#xff0c;也并非「及时雨」。 本周&#xff0c;评级机构穆迪宣布&#xff0c;确认瑞声科技(02018.HK)的评级从「稳定」调整为「负面」。穆迪表示&#xff0c;瑞声科技拟收购Premium Sounds S…

传统视觉方法识别指针仪表

对于一些特殊场景和仪表数量较少时比较适合。 一 找出目标区 通过labelme 将仪表范围框出来,并进行转换: data = json.load(open(mask_path, encoding=utf-8)) shapes = data[shapes]target_label = 1for shape in shapes:label = shape[label]if label == target_label:p…

快速实现SAP的移动化和流程优化

热门议题&#xff1a; 1、企业如何快速解决人员移动办公的需求&#xff0c;比如在苹果安卓手机&#xff0c;平板电脑&#xff0c;MAC登录SAP。2、企业如何解决用户经常抱怨的流程复杂&#xff0c;操作繁琐&#xff0c;难以使用等问题 公司介绍&#xff1a; Synactive,Inc. 是…

vim基本使用方法

VIM 1.vim介绍2.vim基本操作2.1 模式切换2.2 命令模式2.3 底行模式 1.vim介绍 vim是linux上一个有多个编辑模式的编辑器。 这里主要介绍三种模式&#xff1a; 命令模式&#xff08;Normal mode&#xff09; 执行命令的模式&#xff0c;主要任务就是控制光标移动、复制和删除。…

8. 实现业务功能--用户注册

目录 1. 顺序图 2. 参数要求 3. 接口规范 4. 创建扩展 Mapper.xml 5. 修改 DAO 6. 创建 Service 接口 7. 实现接口 8. 测试接口 9. 实现 Controller 9.1 密码加密处理 10. 实现前端界面 业务实现过程中主要的包和目录及主要功能&#xff1a; model 包&#xff1a;实体对象 d…

动态规划入门之01背包变形嗑药

P1802 5 倍经验日 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 嗑药固然可耻&#xff0c;但是能让你快速变强 --鲁迅 手动滑稽&#xff0c;话归正题 动态规划之背包入门01背包模板_爱莉我老婆的博客-CSDN博客 这是01背包的模板&#xff0c;没看的可以去看看。 我们把…

vue 弹出框 引入另一个vue页面

为什么要这么做,适用于在一个页面逻辑比较多的时候,可以搞多个页面,防止出错 index页面点击解约按钮,弹出框 进入jieyue.vue 核心代码 <el-buttonsize"mini"type"text"icon"el-icon-edit"v-if"scope.row.delFlag 0"click"j…

守护网络安全:深入了解DDOS攻击防护手段

ddos攻击防护手段有哪些?在数字化快速发展的时代&#xff0c;网络安全问题日益凸显&#xff0c;其中分布式拒绝服务(DDOS)攻击尤为引人关注。这种攻击通过向目标网站或服务器发送大量合法或非法的请求&#xff0c;旨在使目标资源无法正常处理其他用户的请求&#xff0c;从而达…

【复盘】第 111 场力扣夜喵双周赛

又是7分选手&#xff0c;好好复盘&#xff01; 第一题&#xff08;签到题&#xff09; 给你一个下标从 0 开始长度为 n 的整数数组 nums 和一个整数 target &#xff0c;请你返回满足 0 < i < j < n 且 nums[i] nums[j] < target 的下标对 (i, j) 的数目。 示例 …

探索人工智能 | 模型训练 使用算法和数据对机器学习模型进行参数调整和优化

前言 模型训练是指使用算法和数据对机器学习模型进行参数调整和优化的过程。模型训练一般包含以下步骤&#xff1a;数据收集、数据预处理、模型选择、模型训练、模型评估、超参数调优、模型部署、持续优化。 文章目录 前言数据收集数据预处理模型选择模型训练模型评估超参数调…

websocker无法注入依赖

在公司中准备用websocker统计在线人数&#xff0c;在WebSocketServer使用StringRedisTemplate保存数据到redis中去&#xff0c;但是在保存的时候显示 StringRedisTemplate变量为null 详细问题 2023-08-20 10:37:14.109 ERROR 28240 --- [nio-7125-exec-1] o.a.t.websocket.po…

linux面试题整理

目录标题 基础篇1.说下企业为什么用linux而不用windows&#xff1f;2.linux学过什么&#xff0c;怎么学习的&#xff1f;3.linux基本命令4.linux查看端口、进程、文件类型、挂载5.使用top命令之后前五行会显示什么内容&#xff1f;6.linux怎么查找一个文件7.vim进去后的各种操作…

NAS个人云存储 - 手把手教你搭建Nextcloud个人云盘并实现公网远程访问

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…