【数据结构与算法】从完全二叉树到堆再到优先队列

完全二叉树 CBT

设二叉树的深度为 h , 若非最底层的其他各层的节点数都达到最大个数 , 最底层 h 的所有节点都连续集中在左侧的二叉树叫做 完全二叉树 .

特点

  1. 对任意节点 , 其右分支下的叶子节点的最底层为 L , 则其左分支下的叶子节点的最低层一定是 L 或 L + 1 .
  2. 完全二叉树度为 1 的点只有 1 个或 0 个 .
  3. 按层序遍历的顺序访问 , 度为 1 或 0 的节点的后续节点的度均为 0 .

二叉树的层序遍历

层序遍历是一种广度优先搜索 , 要借助 队列 数据结构实现 , 核心逻辑如下 :

  1. 初始化队列 , 把根节点加入队列 .
  2. 从队列取出一个节点 , 将该节点的左右节点加入队列 , 重复处理至队列为空 .
class TreeNode{int val;TreeNode left;TreeNode right;TreeNode(int val){this.val = val;}public static List<Integer> levelOrderTraversal(TreeNode root){Queue<TreeNode> queue = new LinkedList<>();List<Integer> list = new ArrayList<>();if (root == null) {return list;}queue.offer(root);while(!queue.isEmpty()){TreeNode node = queue.poll();list.add(node.val);if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}return list;}
}

Queue 的 offer 方法会将 null 加入队列 , 因此我们要加入条件语句避免产生空指针异常 .

判断二叉树是否为完全二叉树

因为完全二叉树最底层 h 的所有节点都连续集中在左侧 , 且按层序遍历的顺序访问 , 度为 1 或 0 的节点的后续节点的度均为 0 ( 特点 3 ) , 判断是否为完全二叉树较常用的方法要借助 二叉树的层序遍历 , 核心逻辑如下 :

  1. 对二叉树进行层序遍历 , 使用队列保存节点 .
  2. 遍历过程中维护标识符 end , 其表示是否遇到过度为 1 或 0 的节点 .
  3. end 标识符翻转后如果再次遇到有左右任一子节点的节点 , 直接返回 .
class TreeNode{int val;TreeNode left;TreeNode right;TreeNode(int val){this.val = val;}public static Boolean isCBTorNot(TreeNode root){Queue<TreeNode> queue = new LinkedList<>();if (root == null) {return true;}queue.offer(root);boolean end = false;while(!queue.isEmpty()){TreeNode node = queue.poll();if(node.left != null){if(end) return false;queue.offer(node.left);}else end = true;if(node.right != null){if(end) return false;queue.offer(node.right);}else end = true;}return true;}
}

完全二叉树的数组表示

为什么可以使用数组表示 ?

完全二叉树非最底层的其他各层的节点数都达到最大个数 , 最底层 h 的所有节点都连续集中在左侧 , 使得使用数组存储时能够紧密排列 , 避免空间浪费 .

父子节点的索引关系

推导过程如图 , 结论 :

  • 父节点的数组索引为 n , 则左子节点的数组索引为 2 * n + 1 , 右子节点的数组索引为 2 * n + 2 , 祖父节点的数组索引为 ( n - 1 ) / 2 .
    在这里插入图片描述

堆 Heap

堆是满足特定条件的完全二叉树 , 主要分为 大顶堆小顶堆 两种类型 , 大顶堆指 任意节点值 >= 其子节点值 , 小顶堆指 任意节点值 <= 其子节点值 .

堆化

堆化是将无序数组转化为堆的过程 . 添加元素入堆时 :

  1. 给定元素 val , 我们首先将该元素添加到堆底 .
  2. val 可能大于堆中其他元素 , 此时堆被暂时破坏 , 我们要从堆底至顶进行堆化 .
  3. 比较 val 节点与其节点的值 , 若插入节点更大则交换二者 , 重复执行操作直到节点上升到根节点或其父节点更大时结束 .

初始化无序数组为堆时 :

  1. 对于每个非叶子节点执行下沉操作 : 比较该节点与左右子节点 , 若该节点值小于子节点最大值 , 则交换该节点与最大值子节点 .
  2. 重复操作直到节点下沉到叶子节点或该节点值大于或等于左右子节点值的最大值 .

堆的节点总数为 n , 树的层数为 log n , 堆化操作的迭代次数至多为 log n , 知元素入堆操作的时间复杂度是 O(log n) .

class Heap {public static void heapify(int[] arr, int n, int i) {int largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest]) largest = left;if (right < n && arr[right] > arr[largest]) largest = right;if (largest != i) {int swap = arr[i];arr[i] = arr[largest];arr[largest] = swap;heapify(arr, n, largest);}}public static void buildMaxHeap(int[] arr) {int n = arr.length;for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}}

堆排序

堆排序基于顶堆的特性 , 重复将堆顶元素与堆尾元素交换 , 堆化非队尾元素的剩余元素 直至数组有序 . 堆的节点数为 n , 堆化操作的时间复杂度为 log n , 因此堆排序的时间复杂度是 O(n log n) , 堆排序基于原地交换 , 空间复杂度为常数级 , 是性能良好的排序方法 .

class Heap{public static void heapSort(int[] arr) {int n = arr.length;for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}for (int i = n - 1; i > 0; i--) {int temp = arr[0];arr[0] = arr[i];arr[i] = temp;heapify(arr, i, 0);}}
}

优先队列 PriorityQueue

优先队列是特殊的队列数据结构 , 在 Java PriorityQueue 类中 , 默认优先级为从小到大的自然排序 , 可以通过 lambda 表达式自定义比较器 Comparator 函数类型 .

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(); // 默认为自然顺序
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Comparator.reverseOrder()); // 更改比较器为降序

如果要储存自定义引用数据类型时 , 有两种方式定义元素优先级 :

  1. 引用类实现 Comparable 接口 : 在类中实现 compareTo() 方法 .
  2. 在初始化 PriorityQueue 时传入比较器对象 .
class Person{private String name;private int age;Person(String name, int age){this.name = name;this.age = age;}@Overridepublic int compareTo(Person person){retutn Integer.compare(this.age, person.age);}
}
public class Main{public static void main(String[] args){PriorityQueue<Person> pq = new PriorityQueue<>();// 或PriorityQueue<Person> pq = new PriorityQueue<>((p1, p2) -> Integer.compare(p2.getAge(), p1.getAge()));}
}

Java 优先队列的方法与队列类似 .

23. 合并 K 个升序链表

在这里插入图片描述

我们维护一个按节点值自然排序的优先队列 , 将链表数组内的所有非空数组加入队列 , 每次出堆堆顶节点 , 定义返回节点指向出堆节点 , 入堆出堆节点的后继节点 , 返回节点指针后移 , 重复操作直到堆为空即可 .

class Solution {public ListNode mergeKLists(ListNode[] lists) {PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);for (ListNode head : lists) {if (head != null) {pq.offer(head);}}ListNode dummy = new ListNode();ListNode cur = dummy;while (!pq.isEmpty()) {ListNode node = pq.poll();if (node.next != null) {pq.offer(node.next);}cur.next = node;cur = cur.next;}return dummy.next;}
}

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

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

相关文章

Leetcode:1. 两数之和

题目 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 示…

flume整合kafka

需求一&#xff1a; 启动flume 启动kafka消费者&#xff0c;验证数据写入成功 新增测试数据 需求二&#xff1a; 启动Kafka生产者 启动Flume 在生产者中写入数据

Hbase集群管理与实践

一、HBase集群搭建实战 1.1 环境规划建议 硬件配置基准(以10节点集群为例): 角色CPU内存磁盘网络HMaster4核16GBSSD 200GB(系统盘)10GbpsRegionServer16核64GB124TB HDD(JBOD)25GbpsZooKeeper4核8GBSSD 500GB10Gbps1.2 关键配置项示例(hbase-site.xml) <configu…

STM32 开发 - stm32f10x.h 头文件(内存映射、寄存器结构体与宏、寄存器位定义、实现点灯案例)

概述 STM32F10x.h 是 STM32F1 系列微控制器的核心头文件&#xff0c;提供了所有外设寄存器的定义和内存映射 一、内存映射 #define PERIPH_BASE ((uint32_t)0x40000000)#define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE 0x…

QEMU源码全解析 —— 块设备虚拟化(23)

接前一篇文章:QEMU源码全解析 —— 块设备虚拟化(22) 本文内容参考: 《趣谈Linux操作系统》 —— 刘超,极客时间 《QEMU/KVM源码解析与应用》 —— 李强,机械工业出版社 特此致谢! QEMU启动过程中的块设备虚拟化 上一回解析了qcow2格式对应的qcow2_open函数,本回解…

【PCB工艺】推挽电路及交越失真

推挽电路(Push-Pull Circuit) 推挽电路(Push-Pull Circuit) 是一种常用于功率放大、电机驱动、音频放大等场合的电路结构,具有输出对称、效率高、失真小等优点。 什么是推挽电路? 推挽是指:由两种极性相反的器件(如 NPN 和 PNP、NMOS 和 PMOS)交替导通,一个“推”电…

RD电子实验记录本选用贴士A-B-C

传统的实验记录本&#xff0c;令人又爱又恨本 如何挑选电子实验室记录本&#xff08;ELN&#xff09;的品牌/服务商/供应商&#xff1f; 电子实验记录本&#xff0c;又名为ELN&#xff0c;Electronic lab notebook&#xff0c;enotebook&#xff0c;研发电子管理系统&#xf…

Qt实战之将自定义插件(minGW)显示到Qt Creator列表的方法

Qt以其强大的跨平台特性和丰富的功能&#xff0c;成为众多开发者构建图形用户界面&#xff08;GUI&#xff09;应用程序的首选框架。而在Qt开发的过程中&#xff0c;自定义插件能够极大地拓展应用程序的功能边界&#xff0c;让开发者实现各种独特的、个性化的交互效果。想象一下…

java基础之枚举和注解

枚举 简介 枚举&#xff1a;enumeration&#xff0c;jdk1.5中引入的新特性&#xff0c;用于管理和使用常量 入门案例 第一步&#xff1a;定义枚举&#xff0c;这里定义一个动物类&#xff0c;里面枚举了多种动物 public enum AnimalEnum {CAT, // 猫DOG, // 狗PIG // …

2.3java运算符

运算符 1. 算术运算符 算术运算符用于执行基本的数学运算&#xff0c;像加、减、乘、除等。 运算符描述示例加法int a 5 3; // a 的值为 8-减法int b 5 - 3; // b 的值为 2*乘法int c 5 * 3; // c 的值为 15/除法int d 6 / 3; // d 的值为 2%取模&#xff08;取余&…

升级 Spring Boot CLI

&#x1f31f; 升级 Spring Boot CLI 1️⃣ &#x1f504; 通过包管理器升级 使用对应包管理器命令&#xff08;如 brew upgrade&#xff09; 2️⃣ &#x1f4e5; 手动安装升级 遵循 标准安装说明 注意更新 PATH 环境变量移除旧版本路径 &#x1f517; 链接原文&#xff1a…

如何轻松将RS232转为Profibus DP,提升PLC效率?

如何轻松将RS232转为Profibus DP&#xff0c;提升PLC效率&#xff1f; 今天&#xff0c;我们就来聊聊一个工业自动化中常见的应用场景&#xff1a;如何通过兴达易控RS232转Profibus DP网关&#xff0c;实现流量泵与PLC&#xff08;可编程逻辑控制器&#xff09;的通信。这个话…

QT 连接数据库操作(15)

文章目录 一、本章说明二、QT连接云端数据库实现2.1 ODBC软件安装及参数设置2.2 软件代码实现三、项目源码文件一、本章说明 注:本节为【基于STM的环境监测系统(节点+云服务器存储+QT界面设计)】项目第15篇文章,前面已经创建了监测软件的登录窗口,接下来我们将在主窗口实…

linux系统之----命令行参数和环境变量

一、命令行参数 1.main()函数的参数 在C语言中&#xff0c;main函数可以接收命令行参数&#xff0c;其标准形式为&#xff1a; int main(int argc, char *argv[]) {// 程序代码return 0; } 这里我们解释一下&#xff1a; argc&#xff1a;参数个数计数器&#xff08;Argum…

解析excel中的图片

解析excel中的图片 前言一、pom依赖二、使用步骤1.示例数据2.代码如下&#xff08;示例&#xff09;&#xff1a; 总结 前言 初始化数据是&#xff0c;需要将excel中的数据解析并插入数据库。 但是某几列存放的是图片&#xff0c;这时候怎么办呢。 主要解决的是&#xff1a;获…

Unity任务系统笔记

数据结构设计 任务基类包括的字段&#xff1a; string 任务内容&#xff1b; Transform 任务目的地&#xff1b; MyCharacter 任务开启后要更新对话的NPC&#xff1b; MyTalkData 任务开启后相关NPC要说的对话数据&#xff1b; 共同方法&#xff1a;开启任务、完成任务。…

STM32的开发环境介绍

目录 STM32软件环境 Keil软件在线安装 其他软件环境安装 STM32开发的几种方式 STM32寄存器版本和库函数版本 标准外设库的作用&#xff1a; STM32软件环境 STM32 的集成开发环境&#xff08;IDE&#xff09;&#xff1a;编辑编译软件 常见的环境&#xff1a; (1)KEIL&a…

【特殊场景应对9】视频简历的适用场景与风险分析

写在最前 作为一个中古程序猿,我有很多自己想做的事情,比如埋头苦干手搓一个低代码数据库设计平台(目前只针对写java的朋友),比如很喜欢帮身边的朋友看看简历,讲讲面试技巧,毕竟工作这么多年,也做到过高管,有很多面人经历,意见还算有用,大家基本都能拿到想要的offe…

Linux系统性能调优技巧分享

在数字化时代,Linux 系统以其开源、稳定、高效的特性,成为服务器、云计算、物联网等领域的核心支撑。然而,随着业务规模的扩大和负载的增加,系统性能问题逐渐凸显。掌握 Linux 系统性能调优技巧,不仅能提升系统运行效率,还能降低运维成本。下面从多个方面介绍实用的性能调…

关于Code_流苏:商务合作、产品开发、计算机科普、自媒体运营,一起见证科技与艺术的交融!

Code_流苏 &#x1f33f; 名人说&#xff1a;路漫漫其修远兮&#xff0c;吾将上下而求索。—— 屈原《离骚》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; &#x1f31f; 欢迎来到Code_流苏的CSDN主页 —— 与我一起&…