LeetCode 637, 67, 399

news2024/9/24 23:24:27

文章目录

  • 637. 二叉树的层平均值
    • 题目链接
    • 标签
    • 思路
    • 代码
  • 67. 二进制求和
    • 题目链接
    • 标签
    • 思路
    • 代码
  • 399. 除法求值
    • 题目链接
    • 标签
    • 思路
      • 导入
      • value 属性
      • find() 方法
      • union() 方法
      • query() 方法
    • 代码


637. 二叉树的层平均值

题目链接

637. 二叉树的层平均值

标签

树 深度优先搜索 广度优先搜索 二叉树

思路

本题考查了二叉树的 层序遍历,层序遍历的思想是 广度优先搜索,广度优先搜索需要使用 队列,操作如下:

  1. 创建一个队列,用于保存每层节点。
  2. 先将根节点放入队列,代表遍历第一层。
  3. 只要队列不为空,就进行如下操作:
    1. 获取本层节点的个数。
    2. 遍历本层的所有节点,遍历操作如下:
      1. 取出当前节点。
      2. 如果当前节点的左子节点不为 null,则将其放到队列中,等待下一层节点的遍历。
      3. 如果当前节点的右子节点不为 null,则将其放到队列中,等待下一层节点的遍历。
      4. 针对当前节点进行某种操作:使用一个变量统计本层节点之和。
    3. 针对本层节点进行某种 统计 操作:计算本层节点的平均值,并将其放到结果链表中。
  4. 遍历完整个二叉树,返回结果。

代码

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res = new ArrayList<>(); // 存储结果(每层节点的平均值)的链表

        LinkedList<TreeNode> queue = new LinkedList<>(); // 保存每层节点的队列
        queue.offer(root); // 先将根节点放入队列,即遍历第一层
        while (!queue.isEmpty()) { // 直到队列为空,才退出循环
            double sum = 0; // 计算本层节点的和

            int size = queue.size(); // 获取本层节点的数量
            for (int i = 0; i < size; i++) { // 遍历本层所有节点
                TreeNode curr = queue.poll(); // 取出 当前节点
                
                TreeNode left = curr.left; // 获取当前节点的 左子节点
                if (left != null) { // 如果 左子节点 不为 null
                    queue.offer(left); // 则将其放到队列中,等待下一层遍历
                }
                
                TreeNode right = curr.right; // 获取当前节点的 右子节点
                if (right != null) { // 如果 右子节点 不为 null
                    queue.offer(right); // 则将其放到队列中,等待下一层遍历
                }

                sum += curr.val; // 求和
            }

            res.add(sum / size); // 计算平均值
        }
        return res;
    }
}

67. 二进制求和

题目链接

67. 二进制求和

标签

位运算 数学 字符串 模拟

思路

本题是一道 模拟题,如果是十进制,则本题就是 高精度 问题,以下写出一种模版,只需要改变 base 进制,就可以使用不同的进制进行计算。

可以想一想小学一年级学的 列竖式计算两数之和,高精度的解决方案就是它:

  1. 将两个数的个位对齐,写出来。
  2. 对每一位,有如下操作:
    1. 将两个数的相同位加在一起。此外,还要加上 进位 carry,进位是通过 n 进一 得到的,这里的 n 就是进制。如果有一个数的位数不够,则认为这个数的这一位为 0
    2. 对于加起来的和,如果它大于进制,则进一,并将剩余的数(和 对 进制 取余)写在结果的这一位上;否则直接将和写在结果的这一位上。
  3. 最后,看是否还应该进位,如果需要,则再添加一位 1

由于 题目传入的字符串和要求返回的字符串都是从高位到低位的,而 在计算时是从低位到高位的,所以要注意在计算时颠倒字符串,并将计算结果反转之后再返回。

代码

class Solution {
    public String addBinary(String a, String b) {
        final int base = 2; // 进制,本题为 二进制

        StringBuilder builder = new StringBuilder(); // 拼接数字的拼接器
        char[] aC = a.toCharArray(), bC = b.toCharArray();
        int aLen = aC.length, bLen = bC.length;
        int len = Math.max(aLen, bLen); // 获取较长字符串的长度

        int carry = 0; // 是否要进一,如果要进一,则为 1;否则为 0
        for (int i = 0; i < len; i++) {
        	// 注意 aC/bC[i] 是字符,需要通过 减去 '0' 来将其转化为 数字
            int operand1 = i < aLen ? (aC[aLen - i - 1] - '0') : 0;
            int operand2 = i < bLen ? (bC[bLen - i - 1] - '0') : 0;
            int sum = operand1 + operand2 + carry;
            carry = sum / base; // 查看是否需要进一
            sum %= base; // 获取剩余的数
            builder.append(sum);
        }
        
        if (carry > 0) { // 如果还需要进位
            builder.append(1); // 则再添加一位 1
        }
        
        builder.reverse(); // 先将结果反转
        return builder.toString(); // 再返回
    }
}

399. 除法求值

题目链接

399. 除法求值

标签

深度优先搜索 广度优先搜索 并查集 图 数组 字符串 最短路

思路

导入

本题不算中等题,理应为困难题,主要与 并查集数学 有关,操作十分复杂。可以先看看 990. 等式方程的可满足性,990 题是本题的简单版本,尽量理解 990 题中的并查集的使用。此外,希望大家看一看并查集的 路径压缩,这在本题中很重要。

value 属性

如果能够理解 990 题,那么离解决本题就不远了。本题的并查集中每个节点都比 990 题多了一个值 value,其保存了 本节点 与 其父节点 的比值,即有 v a l u e [ i ] = i p a r e n t [ i ] value[i] = \frac{i}{parent[i]} value[i]=parent[i]i 请记住这点,在 find()union() 中很重要。注意:其中的 i, parent[i] 不是索引,没有实际意义,只是充当有逻辑意义的占位符,这个比值是从 values 数组中获得的。

find() 方法

find() 时需要 压缩路径,因为这样可以避免一些计算。例如下面的并查集,如果想要查询 xrootX 的比值,就必须计算两次;但如果直接让 x 指向 rootX,这时只需要获取 value[x] 即可。具体的操作为先保存父节点,再递归获取根节点,最后更新本节点的 value,从 本节点与父节点的比值 变为 本节点与根节点的比值。等式如下: x r o o t X = x y ∗ y r o o t X \frac{x}{rootX} = \frac{x}{y} * \frac{y}{rootX} rootXx=yxrootXy ,即 value[x] = value[x] * value[y],其中的 y 为未变更之前 x 的父节点。
alt text

union() 方法

union() 方法中,不仅要传递 x, y 的索引,还要传递 value = x / y 的值。先获取 xy 的根节点(获取根节点的时候就让 x, y 指向其根节点 rootX, rootY 了,如下图),如果根节点一样,则直接返回;否则就需要让 rootX 指向 rootY,并更新 rootXvalue。等式如下: r o o t X r o o t Y = y r o o t Y x y r o o t X x \frac{rootX}{rootY} = \frac{y}{rootY} \frac{x}{y} \frac{rootX}{x} rootYrootX=rootYyyxxrootX ,即有 value[rootX] = value[y] * value / value[x]
alt text
本题还有一个比较麻烦的点:等式中的变量不一定只有一个小写字母。所以需要建立 等式中的变量(以下称作 操作元)与 其在并查集中的索引 的映射,具体请看代码。

query() 方法

由于需要查询某两个操作元之间的比值,还需要在并查集中实现一个方法 query(),如果两个操作元对应的索引在并查集中不连通,则返回 -1;否则返回比值 value[x] / value[y]。等式如下(如果 x, y 连通,则其根节点相同,假设为 root): x y = x r o o t r o o t y \frac{x}{y} =\frac{x}{root} \frac{root}{y} yx=rootxyroot ,即 x / y = value[x] / value[y]

代码

class Solution {
    public double[] calcEquation(List<List<String>> equations,
    							double[] values, List<List<String>> queries) {
        // 先初始化并查集
        init(equations, values);

        // 再进行查询
        int queriesSize = queries.size();
        double[] res = new double[queriesSize];
        for (int i = 0; i < queriesSize; i++) {
            List<String> query = queries.get(i);
            String operand1 = query.get(0); // 操作元1
            String operand2 = query.get(1); // 操作元2
            Integer idx1 = indexMapper.get(operand1); // 操作元1的索引
            Integer idx2 = indexMapper.get(operand2); // 操作元2的索引
            if (idx1 == null || idx2 == null) { // 如果两个操作元有一个找不到
                res[i] = -1; // 则本此查询返回 -1
            } else { // 否则查询 value[idx1] / value[idx2] 的结果
                res[i] = uf.query(idx1, idx2);
            }
        }
        return res;
    }

    private UnionFind uf; // 并查集
    private Map<String, Integer> indexMapper; // 操作元 与 其在并查集中的索引 的映射

    // 初始化并查集
    private void init(List<List<String>> equations, double[] values) {
        int equationsSize = equations.size(); // 获取等式的数量
        uf = new UnionFind(2 * equationsSize); // 创建一个等式数量二倍大小的并查集
        indexMapper = new HashMap<>(2 * equationsSize);
        int idx = 0; // 操作元在并查集中的索引
        for (int i = 0; i < equationsSize; i++) {
        	// 取出每个等式,建立 操作元 与 其在并查集中的索引 的关系
            List<String> equation = equations.get(i);
            String operand1 = equation.get(0); // 操作元1
            String operand2 = equation.get(1); // 操作元2
            if (!indexMapper.containsKey(operand1)) { // 如果 索引映射 中不存在 操作元
                indexMapper.put(operand1, idx++); // 则为其建立映射
            }
            if (!indexMapper.containsKey(operand2)) { // 如果 索引映射 中不存在 操作元
                indexMapper.put(operand2, idx++); // 则为其建立映射
            }
            uf.union(indexMapper.get(operand1), indexMapper.get(operand2), values[i]);
        }
    }

    private static class UnionFind {
        private final int[] parent; // parent[i] 表示 第 i 个元素的父节点
        private final double[] value; // value[i] 表示 第 i 个元素 / 其父节点 的商

        // 初始化并查集
        public UnionFind(int size) {
            parent = new int[size];
            value = new double[size];
            for (int i = 0; i < size; i++) {
                parent[i] = i; // 初始时,每个元素的父节点都是 自己
                value[i] = 1; // 初始时,每个元素与自己的商都是 1
            }
        }

        // 查找 x 的根节点,使用了路径压缩
        public int find(int x) {
            if (x != parent[x]) { // 如果 x 不是根节点
                int temp = parent[x]; // 保存 x 的父节点
                parent[x] = find(parent[x]); // 寻找 x 的根节点
                value[x] *= value[temp]; // 给 本节点与父节点的商 乘 父节点与父节点的父节点的商
            }
            return parent[x]; // 否则返回 parent[x]
        }

        // 合并两个节点所在的集合
        public void union(int x, int y, double value) {
            int rootX = find(x); // x 的根节点
            int rootY = find(y); // y 的根节点
            if (rootX == rootY) {
                return;
            }

            parent[rootX] = rootY; // 让 x 的根节点 指向 y 的根节点,y 的根节点 成为 父节点
            value[rootX] = value[y] * value / value[x];
        }

        // 查询 value[x] / value[y] 的结果
        public double query(int x, int y) {
            if (find(x) == find(y)) { // 如果连通
                return value[x] / value[y]; // 则返回比值
            } else { // 否则不连通
                return -1; // 返回 -1
            }
        }
    }
}

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

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

相关文章

nginx 启动 ssl 模块

文章目录 前言nginx 启动 ssl 模块1. 下载2. 启动 ssl 模块 步骤3. 验证前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!! nginx 启动 ssl 模块 1. 下载 下载…

STM32智能家居控制系统教程

目录 引言环境准备智能家居控制系统基础代码实现&#xff1a;实现智能家居控制系统 4.1 数据采集模块 4.2 数据处理与分析模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;家居监测与优化问题解决方案与优化收尾与总结 1. 引言 智能家居控制系统通…

PHP多场地预定小程序系统源码

一键畅游多地&#xff01;多场地预定小程序的超实用指南 段落一&#xff1a;【开篇&#xff1a;告别繁琐&#xff0c;预订新体验】 &#x1f389;&#x1f680; 还在为多个活动或会议的场地预订而头疼不已吗&#xff1f;多场地预定小程序来拯救你啦&#xff01;它像是一位贴心…

GPU虚拟化和池化技术解读

GPU虚拟化到池化技术深度分析 在大型模型的推动下&#xff0c;GPU算力的需求日益增长。然而&#xff0c;企业常常受限于有限的GPU卡资源&#xff0c;即使采用虚拟化技术&#xff0c;也难以充分利用或持续使用这些资源。为解决GPU算力资源的不均衡问题&#xff0c;同时推动国产…

【Qt 】JSON 数据格式详解

文章目录 1. JSON 有什么作用?2. JSON 的特点3. JSON 的两种数据格式3.1 JSON 数组3.2 JSON 对象 4. Qt 中如何使用 JSON 呢&#xff1f;4.1 QJsonObject4.2 QJsonArray4.3 QJsonValue4.4 QJsonDocument 5. 构建 JSON 字符串6. 解析 JSON 字符串 1. JSON 有什么作用? &#x…

C++中的继承与多态1

目录 C中的继承与多态1 1.继承的概念及定义 1.1继承的概念 1.2 继承定义 1.2.1定义格式 1.2.2继承关系和访问限定符 1.2.3继承基类成员访问方式的变化 2.基类和派生类对象赋值转换 3.继承中的作用域 4.派生类的默认成员函数 5.继承与友元 6.继承与静态成员 7.复杂…

农田环境监测系统—有助于维护农田生态平衡

TH-NQ8农田环境监测系统是一种专为农业领域设计的高科技设备&#xff0c;它通过实时监测农田环境的各项指标&#xff0c;为农业生产提供科学依据&#xff0c;有力地推动了农业的可持续发展。以下是对农田环境监测系统的详细介绍&#xff1a; 系统组成 农田环境监测系统主要由以…

前端学习7——自学习梳理

​​​​​​jQuery 教程 | 菜鸟教程jQuery 教程 jQuery 是一个 JavaScript 库。 jQuery 极大地简化了 JavaScript 编程。 jQuery 很容易学习。 本章节的每一篇都包含了在线实例 通过本站的在线编辑器&#xff0c;你可以在线运行修改后的代码&#xff0c;并查看运行结果。 实例…

手持式气象检测设备:便携科技,气象探测

一、手持式气象检测设备&#xff1a;小巧身躯&#xff0c;大能量 手持式气象检测设备&#xff0c;顾名思义&#xff0c;是一种可以手持操作的气象监测工具。它集成了温度、湿度、气压、风速风向等多种传感器&#xff0c;能够实时获取气象数据&#xff0c;并通过显示屏或手机APP…

聊聊最近很火的13.11和13.8到底谁大?

在最近我是歌手的排名统计中&#xff0c;出现了以下这一幕&#xff1a; 部分网友提出质疑&#xff0c;说是13.11大于13.8&#xff1a; 那么我们肉眼去看&#xff0c;根据我们12年义务教育去比对&#xff0c;肯定是13.8更大一些&#xff0c;但是这样一道简单的数学题还难倒了不少…

java数据结构之排序

前言&#xff1a; 排序在我们日常生活中随处可见&#xff0c;这里将介绍java数据结构里面常见的几种排序。 ps: swap函数的实现&#xff1a; public void swap(int[] arr, int i, int j) {int tmp arr[i];arr[i] arr[j];arr[j] tmp; } 1.直接插入排序 &#xff08;1&a…

如何使用内网穿透为本地部署的开源虚拟机平台Proxmox VE配置公网地址

文章目录 前言1. 局域网访问PVE2. 安装Cpolar 工具3. 创建PVE公网地址4. 远程访问PVE5. 设置固定域名6. 固定地址访问 前言 本文主要介绍如何在Windows环境安装内网穿透工具&#xff0c;实现公网环境远程访问本地局域网中的Proxmox VE平台WEB 管理界面。 Proxmox VE是一个完全…

什么是互联网?

什么是互联网&#xff1f;互联网是由什么组成的&#xff1f;我们身处一个怎样的网络环境&#xff1f;相信很多人其实都无法回答。互联网起始于1969年&#xff0c;至今已经发展为一个极其庞大的全球网络&#xff0c;没有人能够详细描述其全貌。 我觉得这是一个特别奇怪的现象&a…

Nestjs使用Redis的最佳实践

前几天在项目中有用到Redis JWT实现服务端对token的主动删除(退出登录功能)。故此介绍下如何在Nestjs中使用Redis&#xff0c;并做下总结。 知识准备 了解Redis - 网上很多简介。了解Nestjs如何使用jwt生成token - 可移步看下我之前的文章 效果展示 一、mac安装与使用 示…

生成式AI的双重路径:Chat与Agent的融合与竞争

文章目录 每日一句正能量前言整体介绍对话系统&#xff08;Chat&#xff09;自主代理&#xff08;Agent&#xff09;结论 技术对比技术差异优势与劣势技术挑战结论 未来展望发展趋势Chat与Agent的前景社会和经济影响结论 后记 每日一句正能量 在避风的港湾里&#xff0c;找不到…

若依ruoyi+AI项目二次开发

//------------------------- //定义口味名称和口味列表静态数据 const dishFlavorListSelectref([ {name:"辣度",value:["不辣","微辣","中辣","重辣"]}, {name:"忌口",value:["不要葱","不要…

ADG901解析

目录 一、特性二、增强产品特性三、应用四、一般描述五、极低功耗六、引脚描述七、尺寸参数八、电路连接一、特性 宽带开关:-3 dB 在 4.5 GHz吸收型开关高关断隔离度:在 1 GHz 时为 38 dB低插入损耗:在 1 GHz 时为 0.8 dB单一 1.65 V 至 2.75 V 电源CMOS/LVTTL 控制逻辑小巧…

AI无处不在,英特尔举办第十七届网络与边缘计算行业大会,推动边缘AI深度融合

AI正在成为全行业的技术热潮。CSDN 看到&#xff0c;AI正在引发计算、开发、交互三大范式的全面升级&#xff0c;技术开发或将迎来一次全新的科技变革周期。国际权威的分析机构数据也一致显示了AI的快速增长之势。据IDC数据&#xff0c;中国生成式AI的复合年增长率达到86.2%&am…

企业利用AI智能名片S2B2C商城小程序参与社区团购的风险与机遇分析

摘要 在新零售浪潮的推动下&#xff0c;社区团购以其独特的商业模式迅速崛起&#xff0c;成为连接消费者与供应商的重要桥梁。企业纷纷探索如何有效利用这一新兴渠道&#xff0c;以扩大市场份额、提升品牌影响力。AI智能名片S2B2C商城小程序的引入&#xff0c;为企业参与社区团…

Spring源码学习笔记之@Async源码

文章目录 一、简介二、异步任务Async的使用方法2.1、第一步、配置类上加EnableAsync注解2.2、第二步、自定义线程池2.2.1、方法一、不配置自定义线程池使用默认线程池2.2.2、方法二、使用AsyncConfigurer指定线程池2.2.3、方法三、使用自定义的线程池Excutor2.2.4、方法四、使用…