遇到字符串拼接用它就对啦!什么你居然不知道Java中对象作为方法参数和基本数据类型作为参数的区别?有巨坑!

news2024/11/20 6:14:22

今天刷代码随想录,在使用字符串拼接时,发现String类确实比StringBuilder慢了不是,总结了StringBuilder类(详见下面文章内容,点击可跳转),还有在做后两题时,发现了Java中集合作为参数基本数据类型作为参数在底层的逻辑是不一样的,集合名(对象引用)作为参数,方法内部与外界公用一个对象,而基本数据类则不同(详见下面文章内容,点击可跳转)。

110.平衡二叉树

题目链接:110. 平衡二叉树

递归法(后序遍历):

与二叉树最大深度有点像,也是后序遍历,理解为后序遍历的应用吧,注意平衡二叉树的定义:是每个节点的左右子树高度差不能超过1。所以需要每个节点都判断,而如何修改求高度的后序遍历使得能够做出这道题呢,其实就是在“左右中”的中这里,加一个判断,如果左右子树高度差大于1那么返回 -1 这个特殊值,然后在求左右子树高度这里,也加一个判断,如果左/右子树高度为 -1 说明次树一定不是平衡二叉树,那么一路返回-1即可。下面是代码:

代码: 

public boolean isBalanced(TreeNode root) {
        // 1. 后序遍历法
        if(getHeight(root) == -1) {
            return false;
        }else{
            return true;
        }
        
    }
    // 1. 参数和返回值:
    // 根据左右子树高度差超过1返回-1作为异常值,然后一路返回制根节点,在主函数中判断是否存在-1即可
    public int getHeight(TreeNode root) {
        // 2. 循环终止条件:只关心什么时候停止,而不管什么时候返回-1,什么时候返回-1交给“单层循环逻辑”来判断
        if(root == null) {
            return 0;
        }
        // 3. 单层循环逻辑
        int leftHeight = getHeight(root.left);  // 左
        if(leftHeight == -1){
            return -1;
        }
        int rightHeight = getHeight(root.right);// 右
        if(rightHeight == -1){
            return -1;
        }
        // 中:
        int height = 0;
        if(Math.abs(leftHeight - rightHeight) > 1) {
            height = -1;            // 真正控制-1产生的地方
        }else {
            height = 1 + Math.max(leftHeight, rightHeight); // 控制高度产生的地方
        }
        return height;
    }

257. 二叉树的所有路径

题目链接:257. 二叉树的所有路径

一开始也是没有什么思路,没想到前序遍历还能这么操作,确实符合前序遍历往回退可以保存到之前变丽果的元素,重在理解回溯。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList<>();
        if(root == null) {
            return result;
        }
        List<String> path = new ArrayList<>();
        getPaths(root, path, result);
        return result;
    }
    public void getPaths(TreeNode node, List<String> path, List<String> result) {
        path.add(new String().valueOf(node.val));                             // 中
        if(node.left == null && node.right == null) {   // 如果遇到叶子结点,则证明需要把路径加入到结果集中
            String temp = "";
            for(int i = 0; i < path.size() - 1; i++) {  // 除了最后一个元素,其他每个元素之后都要加上->
                temp += path.get(i);
                temp += "->";
            }
            temp += path.get(path.size() - 1);          // 加上最后一个
            result.add(temp);
        }
        if(node.left != null) {
            getPaths(node.left, path, result);
            path.remove(path.size() - 1);               // 回溯,把刚刚加入过结果集里的节点值出栈,是,例如,path:[1,2,5],到了叶子节点5,此时回溯,删除path里的5,然后由于递归,最终会回到1这个根节点位置,然后此时path里的元素只有[1],这样,就达到了记录路径的目的
        }
        if(node.right != null) {
            getPaths(node.right, path, result);
            path.remove(path.size() - 1);
        }
    }
}

 小结:

1. 关于path,也是可以一开始就定义为String类型,后面要加入的时候,new一个新的String类型,然后调用String的valueOf();方法,将int型转化为String类型

2. 对于要频繁用到字符串拼接,可以用 StringBuilder的append方法 这样代码更快(亲测在lettcode上快1ms)

总结StringBuilder
  1. append(Object obj): 追加任意对象的字符串表示形式到当前字符串构建器中。
  2. insert(int index, Object obj): 在指定位置插入任意对象的字符串表示形式。
  3. delete(int start, int end): 删除从指定位置开始的特定长度的字符序列。
  4. deleteCharAt(int index): 删除指定位置的字符。
  5. replace(int start, int end, String str): 替换从指定位置开始的特定长度的字符序列为新字符串。
  6. setCharAt(int index, char ch): 设置指定位置的字符。
  7. substring(int start, int end): 返回一个新字符串,包含当前字符串构建器中从指定位置开始的特定长度的字符序列。
  8. reverse(): 反转当前字符串构建器中的字符序列。
  9. length(): 返回当前字符串构建器中的字符序列的长度。
  10. toString(): 返回表示当前字符串构建器中字符序列的字符串。

 404.左叶子之和 

题目链接:404. 左叶子之和

BFS(迭代)法

就是正常层序遍历各个节点(我的一篇文章中详细讲过层序遍历法遍历二叉树),当遇到一个节点的左节点左孩子和右孩子都为空,则这个节点的左节点左叶子。(哈哈哈哈自己看测试用例挤出来的,显然做出来还是有点丑陋的)

代码:

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        // 1. BFS层序遍历法
        if(root == null) {
            return 0;
        }
        Queue<TreeNode> queue = new ArrayDeque<>();
        int result = 0;
        queue.add(root);
        // 正常层序遍历各个节点
        while(!queue.isEmpty()) {
            int len = queue.size();
            while(len > 0) {
                TreeNode temp = queue.poll();
                
                if(temp.left != null) {
                    if(temp.left.left == null && temp.left.right == null) {    // 如果左节点的左节点和右节点为空,说明此节点为左叶子
                        result += temp.left.val;
                    }
                    queue.add(temp.left);
                }
                if(temp.right != null) {
                    queue.add(temp.right);
                }
                len--;
            }
        }
        return result;
    }
}

DFS(递归)法:

关键是理解什么是左叶子,上面用BFS解决的时候也说了判断方法,下面引用代码随想录里的概括:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。

用递归时的bug

理解了什么是左叶子节点后,自己写了下面这段代码:

class Solution {
        // 2. 递归法
    public int sumOfLeftLeaves(TreeNode root) {
        int result = 0;
        if(root == null) {
            return result;
        }
        getLeftSum(root, result);
        return result;
    }
    public void getLeftSum(TreeNode node, int result) {
        if(node == null) {
            return;
        }
        if(node.left!=null && (node.left.left == null && node.left.right == null)){
            result += node.left.val;
        }   // 中:判断此节点的左孩子是否为左叶子
        getLeftSum(node.left, result);
        getLeftSum(node.right, result);
    }
 
}

 但是测试结果如下图:

很容易能发现错误,就是getLeftSum中result累加的结果没有映射到主方法sumOfLeftLeaves中的result中去

哎~这时我心中就有疑惑了,在上面257. 二叉树的所有路径的题解中,明明也是类似的方法,在主方法binaryTreePaths中创建了List集合result,调用getPaths,最终返回的result,就是结果啊,说明:在调用方法中result的结果映射到外界去了,这涉及到Java的一个小知识点了。

关于Java中方法的参数问题:

就是关于对象的引用(引用指result集合名,是List类的实例对象)作为方法的参数,传入是按值传入,而且是这个值对应的是对象在堆中位置的引用,所以通过这个引用,在方法中可以访问并修改对象(result)的状态。

而关于这一点,我说的一直是“对象的引用”,对于基本数据类型(如int、double等)和String。它们也是按值传递的,但是传递的是值的拷贝,而不是引用的拷贝。因此,在方法内部修改这些类型的参数不会影响到原始变量

总结:

基本数据类型和String在外传参给方法,方法内部的修改不会影响外面。而对象引用(对象实例的名字)作为方法的参数,在方法内部修改的是与外界相同的一个引用(如果试图在方法内部让这个引用指向一个新的对象,将不会影响到外界引用,因为引用值的拷贝是局部的。) 

OK,废话一大堆,代码解释:

public class PassByValueExample {  
  
    static class MutableObject {  
        int value;  
  
        MutableObject(int value) {  
            this.value = value;  
        }  
  
        void changeValue(int newValue) {  
            this.value = newValue;  
        }  
    }  
  
    public static void main(String[] args) {  
        // 对象引用传递示例  
        MutableObject obj = new MutableObject(10);  
        System.out.println("修改对象前:" + obj.value);  
        modifyObject(obj);  
        System.out.println("修改对象后:" + obj.value); // 对象状态已被修改  
  
        // 基本数据类型传递示例  
        int primitive = 5;  
        System.out.println("修改基本数据类型前:" + primitive);  
        modifyPrimitive(primitive);  
        System.out.println("修改基本数据类型后:" + primitive); // 基本数据类型值未改变  
  
        // String传递示例  
        String str = "原始字符串";  
        System.out.println("修改字符串前:" + str);  
        modifyString(str);  
        System.out.println("修改字符串后:" + str); // 字符串值未改变  
    }  
  
    public static void modifyObject(MutableObject obj) {  
        obj.changeValue(20); // 修改对象状态  
        // 注意:以下代码不会影响到main方法中的obj引用  
        // obj = new MutableObject(30);  
    }  
  
    public static void modifyPrimitive(int value) {  
        value = 10; // 修改局部变量的值,不影响外部变量  
    }  
  
    public static void modifyString(String str) {  
        str = "已修改的字符串"; // 修改局部变量的引用,不影响外部变量  
        // 注意:String是不可变的,我们不能修改String对象的内容,只能改变引用  
    }  
}

 输出结果:

修改对象前:10  
修改对象后:20  
修改基本数据类型前:5  
修改基本数据类型后:5  
修改字符串前:原始字符串  
修改字符串后:原始字符串

 真正的题解代码:

class Solution {
        // 2. 递归法
        int result = 0;
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null) {
            return result;
        }
        getLeftSum(root);
        return result;
    }
    private void getLeftSum(TreeNode node) {
        if(node == null) {
            return;
        }
        if(node.left!=null && (node.left.left == null && node.left.right == null)){
            result += node.left.val;
        }   // 中:判断此节点的左孩子是否为左叶子
        getLeftSum(node.left);// 左
        getLeftSum(node.right);// 右
    }
}

但其实,遇到这种,要返回基本数据类型的递归,往往会让方法返回累加值作为结果,修改后代码如下:

class Solution {  
    public int sumOfLeftLeaves(TreeNode root) {  
        return getLeftSum(root);  
    }  
      
    public int getLeftSum(TreeNode node) {  
        if (node == null) {  
            return 0;  
        }  
        int sum = 0;  
        if (node.left != null && node.left.left == null && node.left.right == null) {  
            sum += node.left.val;  
        }  
        sum += getLeftSum(node.left);  
        sum += getLeftSum(node.right);  
        return sum;  
    }  
}

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

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

相关文章

二刷代码随想录|Java版|回溯算法1|回溯基础理论+组合问题

理论 写链表之类的真的很痛苦&#xff0c;赶紧跳到回溯&#xff01;这次我想结合算法设计这本书&#xff0c;把java版写出来。放在第三部分吧。希望能够在研一完成这项工作&#xff01; 从一刷总结以下的几个要点&#xff1a; 回溯方法模板性非常强&#xff01;&#xff01;可…

redis报错:WRONGTYPE Operation against a key holding the wrong kind of value

这个是在redis存取的数据时&#xff0c;存数据时的数据类型和取数据时的数据类型不一致导致的 原因分析 首先需要明白的是&#xff0c;出现这种错误的原因是因为我们在取值的时候&#xff0c;使用的命令不对&#xff0c;比如你用获取string类型的get命令去取列表list类…

前端工程化之上cdn

一、cdn介绍 cdn的使用还是和前端打包相关&#xff0c;我们都希望前端最后的打包页面越小越好。那么可不可以把一些包不pack进去&#xff0c;让用户的流浪器自行下载呢&#xff1f;答案是可以的&#xff0c;那这些包就会被托管到分发站点上&#xff0c;就是在全国都有服务器&a…

Vue3探索编辑部——关于Pinia(1)

目录 什么是Pinia&#xff1f; Vue3中的Pinia 创建项目 数据准备和引入Pinia 使用Pinia 采用action修改数据 总结 什么是Pinia&#xff1f; Pinia是Vue3的专属的状态管理工具&#xff0c;什么是状态呢&#xff1f;其实我们可以把状态理解为数据&#xff0c;或者一个业务…

(七)for循环控制

文章目录 用法while的用法for的用法两者之间的联系可以相互等价用for改写while示例for和while的死循环怎么写for循环见怪不怪表达式1省略第一.三个表达式省略&#xff08;for 改 while&#xff09;全省略即死循环&#xff08;上面已介绍&#xff09; 用法 类比学习while语句 …

Linux:命名管道及其实现原理

文章目录 命名管道指令级命名管道代码级命名管道 本篇要引入的内容是命名管道 命名管道 前面的总结中已经搞定了匿名管道&#xff0c;但是匿名管道有一个很严重的问题&#xff0c;它只允许具有血缘关系的进程进行通信&#xff0c;那如果是两个不相关的进程进行通信&#xff0…

C#,计算几何,二维贝塞尔拟合曲线(Bézier Curve)参数点的计算代码

Pierre Bzier Bzier 算法用于曲线的拟合与插值。 插值是一个或一组函数计算的数值完全经过给定的点。 拟合是一个或一组函数计算的数值尽量路过给定的点。 这里给出 二维 Bzier 曲线拟合的参数点计算代码。 区别于另外一种读音接近的贝塞耳插值算法&#xff08;Bessels int…

市场复盘总结 20240123

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票&#xff0c;只有10%的时间是可以操作&#xff0c;90%的时候都应该空仓 昨日主题投资 连板进级率 7/1…

前端实现转盘抽奖 - 使用 lucky-canvas 插件

目录 需求背景需求实现实现过程图片示意实现代码 页面效果lucky-canvas 插件官方文档 需求背景 要求实现转盘转动抽奖的功能&#xff1a; 只有正确率大于等于 80% 才可以进行抽奖&#xff1b;“谢谢参与”概率为 90%&#xff0c;“恭喜中奖”概率为 10%&#xff1b; 需求实现 实…

鸿蒙入门学习的一些总结

前言 刚开始接触鸿蒙是从2023年开始的&#xff0c;当时公司在调研鸿蒙开发板能否在实际项目中使用。我们当时使用的是OpenHarmony的&#xff0c;基于DAYU/rk3568开发板&#xff0c;最开始系统是3.2的&#xff0c;API最高是API9&#xff0c;DevecoStudio 版本3.1的。 鸿…

国考省考行测:分析推理,形式逻辑,所有有的分析

国考省考行测&#xff1a; 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到寒冬&am…

2024三掌柜赠书活动第七期:一本书读懂AIGC:探索AI商业化新时代

目录 前言AI商业化的背景和挑战关于《一本书读懂AIGC&#xff1a;探索AI商业化新时代》编辑推荐内容简介作者简介图书目录书中前言/序言《一本书读懂AIGC&#xff1a;探索AI商业化新时代》全书速览结束语 前言 不用多讲&#xff0c;想必大家也都知道&#xff0c;人工智能在过…

LLM大语言模型(五):用streamlit开发LLM应用

目录 背景准备工作切记streamlit开发LLM demo开一个新页面初始化session先渲染历史消息接收用户输入模拟调用LLM 参考 背景 Streamlit是一个开源Python库&#xff0c;可以轻松创建和共享用于机器学习和数据科学的漂亮的自定义web应用程序&#xff0c;用户可以在几分钟内构建一…

optee编译调试

编译运行 使用的是 ubuntu22.04 需要提前设置好网络 optee运行环境搭建&#xff1a;https://optee.readthedocs.io/en/latest/building/prerequisites.html 安装必要的库 sudo apt install -y \adb \acpica-tools \autoconf \automake \bc \bison \build-essential \ccach…

MySQL十部曲之六:数据操作语句(DML)

文章目录 前言语法约定DELETEINSERTSELECT查询列表SELECT 选项子句FROMWHEREORDER BYGROUP BYHAVINGWINDOWLIMITFOR SELECT ... INTO连接查询CROSS JOIN和INNER JOINON和USINGOUTER JOINNATURE JOIN 子查询标量子查询使用子查询进行比较带有ANY、IN或SOME的子查询带有ALL的子查…

C++ //练习 3.5 编写一段程序从标准输入中读入多个字符串并将它们连接在一起,输出连接成的大字符串。然后修改上述程序,用空格把输入的多个字符串分隔开来。

C Primer&#xff08;第5版&#xff09; 练习 3.5 练习 3.5 编写一段程序从标准输入中读入多个字符串并将它们连接在一起&#xff0c;输出连接成的大字符串。然后修改上述程序&#xff0c;用空格把输入的多个字符串分隔开来。 环境&#xff1a;Linux Ubuntu&#xff08;云服务…

网络安全B模块(笔记详解)- 越权与下载

1.使用渗透机场景kali中工具扫描服务器场景&#xff0c;将web端口号当作Flag提交&#xff1b; 2.使用渗透机场景windows7访问服务器场景mingling.php,将页面中的Flag提交&#xff1b; 3.使用渗透机场景windows7访问服务器场景mingling.php&#xff0c;分析页面内容&#xff0…

SpringBoot引入 liteflow 规则引擎,yyds!

1前言 在日常的开发过程中&#xff0c;经常会遇到一些串行或者并行的业务流程问题&#xff0c;而业务之间不必存在相关性。 在这样的场景下&#xff0c;使用策略和模板模式的结合可以很好的解决这个问题&#xff0c;但是使用编码的方式会使得文件太多,在业务的部分环节可以这…

Apache Shiro <= 1.2.4反序列化漏洞攻击 CVE-2016-4437 已亲自复现

Apache Shiro < 1.2.4反序列化漏洞攻击 CVE-2016-4437 已亲自复现 漏洞名称漏洞描述影响版本 漏洞复现环境搭建漏洞利用 修复建议总结 漏洞名称 漏洞描述 在 1.2.5 之前的 Apache Shiro 中&#xff0c;当未为“记住我”功能配置密钥时&#xff0c;远程攻击者可以通过未指定…

去创造,不要消费

最近&#xff0c;想必许多人的社交圈&#xff0c;都被B站的五四视频《后浪》刷屏了。 如果你没看过&#xff0c;也没关系&#xff0c;因为我并不打算讲它。 &#xff08;熟悉我的老读者会知道&#xff0c;我文章最开头的东西一般只是个引子&#xff0c;并不重要&#xff09; 言…