GO语言-切片底层探索(上)

目录

1.前言

2. 算法题目 + 错误代码

3. 错误分析

4.总结:

5.正确代码:

6.本地测试代码:


1.前言

今天在力扣上写算法,遇到了一个比较"奇怪"的错误。由于自己使用了递归+切片,导致一开始没有看明白,直到在自己电脑上进行debug的时候才反应过来,原因出在了哪里?下面会先进行错误的分析和纠正,之后进行切片底层原理的探索,由于篇幅较长,为了小伙伴们的阅读体验,分为上下两篇进行。

下篇:GO语言-切片底层探索(下)-CSDN博客

2. 算法题目 + 错误代码

力扣题目:113. 路径总和 II

自己的题解:

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/func pathSum(root *TreeNode, targetSum int) [][]int {var array [][]intif root == nil{return array}var addPath func(node *TreeNode,sum int,path []int)addPath = func(node *TreeNode,sum int,path []int){if node.Left == nil && node.Right == nil{sum += node.Valif sum == targetSum{path = append(path,node.Val)array = append(array,path)//打印的值,下图中的标准输出fmt.Println(path,array)}return}sum += node.Valpath = append(path,node.Val)if node.Left != nil{addPath(node.Left,sum,path)}if node.Right != nil{addPath(node.Right,sum,path)}}addPath(root,0,[]int{})return array
}

运行错误示例和图示:

3. 错误分析


我们可以发现在代码中 node节点左右都为空的判断中打印输出的明明是[[-6,-3,-6,-6]];但是最后输出却变成了[[-6,-3,-6,-5]],导致输出结果不符合预期。第一次看到这样的情况,感觉很奇怪,重新梳理一遍自己代码的逻辑,没有发现错误。最后将猜测可能是切片容器的问题,于是在自己本地进行debug,发现果然如此。

这其中隐藏着切片的两个底层实现:

  1. 切片是引用类型,会进行引用传递;
  2. 切片会随着元素数量的增加,进行扩容,改变其的底层数组的指向;

在我的算法实现中最终的返回值是一个二维切片,在二叉树遍历的过程中不断将路径上的节点加入
到一维切片path中。如果最后遍历的是叶子节点并且路径上的所有值的总和等于要求的值,就将一维切片path直接加入到二维切片中。而问题就出现在这里,我是直接将一维切片加入到二维切片中的,而切片是引用类型,后续对path切片的修改会影响到已加入二维切片中的切片(注意:这里的影响不是一定的,需要加一个条件,和加入二维切片中的path指向的是同一个底层数组的切片才会进行影响),这也是为什么我之前110个测试能够通过,而这一个无法通过的原因,是有一定的概率的。

因为切片是引用类型的,所以导致了已经加入二维切片中的一维切片被修改了,那么,为什么修改后的结果是[[-6,-3,-6,-5]]而不是[[-6,-3,-6,-5,1]]等具有其他的切片呢?

这是因为切片的扩容机制,导致了指向的底层数组发生了变更,具体如下图:

4.总结:

导致我们最后输出答案是[[-6,-3,-6,-5]]的原因如下:

  1. 切片是引用类型,会进行引用传递;
  2. 切片会随着元素数量的增加,进行扩容,改变其的底层数组的指向;

现在再看这两句话,是不是清晰明确了很多。

下一篇文章将介绍关于切片的底层扩容机制

5.正确代码:

我们不要将符合条件的path切片直接加入到二维切片中,而是要进行copy复制,防止发生引用传递;

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/func pathSum(root *TreeNode, targetSum int) [][]int {var array [][]intif root == nil{return array}var addPath func(node *TreeNode,sum int,path []int)addPath = func(node *TreeNode,sum int,path []int){if node.Left == nil && node.Right == nil{sum += node.Valif sum == targetSum{path = append(path, node.Val)//这里对path的值进行复制copyPath := make([]int, len(path))copy(copyPath, path)array = append(array, copyPath)}return}sum += node.Valpath = append(path,node.Val)if node.Left != nil{addPath(node.Left,sum,path)}if node.Right != nil{addPath(node.Right,sum,path)}}addPath(root,0,[]int{})return array
}

通过通过 

6.本地测试代码:

本地测试代码的示例做了一些变更,大家可以自行测试:

package mainimport "fmt"type TreeNode struct {Val   intLeft  *TreeNodeRight *TreeNode
}func Constructor(value int, leftNode *TreeNode, rightNode *TreeNode) *TreeNode {return &TreeNode{Val:   value,Left:  leftNode,Right: rightNode,}
}func main() {node_61 := Constructor(-6, nil, nil)node1 := Constructor(1, nil, nil)node7 := Constructor(7, nil, nil)node_5 := Constructor(-5, node1, node7)node_62 := Constructor(-6, node_61, node_5)node11 := Constructor(11, node_61, node_5)node4 := Constructor(-6, nil, nil)node0 := Constructor(-6, node4, node11)node_3 := Constructor(-3, node_62, node0)node_63 := Constructor(-6, nil, node_3)fmt.Println("result", pathSum(node_63, -21))
}func pathSum(root *TreeNode, targetSum int) [][]int {var array [][]intif root == nil {return array}var addPath func(node *TreeNode, sum int, path []int)addPath = func(node *TreeNode, sum int, path []int) {if node.Left == nil && node.Right == nil {sum += node.Valif sum == targetSum {//未修改哈!path = append(path, node.Val)array = append(array, path)fmt.Println(path, array)}return}sum += node.Valpath = append(path, node.Val)if node.Left != nil {addPath(node.Left, sum, path)}if node.Right != nil {addPath(node.Right, sum, path)}}addPath(root, 0, []int{})return array
}

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

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

相关文章

力扣最热100题——56.合并区间

吾日三省吾身 还记得梦想吗 正在努力实现它吗 可以坚持下去吗 目录 吾日三省吾身 力扣题号:56. 合并区间 - 力扣(LeetCode) 题目描述 Java解法一:排序然后原地操作 具体代码如下 Java解法二:new一个list&#xf…

(delphi11最新学习资料) Object Pascal 学习笔记---第7章第1节(引入类和对象)

第7章 对象 ​ 即使你没有面向对象编程(OOP)的详细知识,本章也将介绍每一个关键概念。如果你已经精通OOP,你可以相对快速地浏览材料,并将重点放在Object Pascal语言的细节上,与你可能已经掌握的其他语言进…

分布式ID(7):Zookeeper实现分布式ID生成

1 原理 实现方式有两种,一种通过节点,一种通过节点的版本号 节点的特性持久顺序节点(PERSISTENT_SEQUENTIAL) 他的基本特性和持久节点是一致的,额外的特性表现在顺序性上。在ZooKeeper中,每个父节点都会为他的第一级子节点维护一份顺序,用于记录下每个子节点创建的先后顺序…

虚拟机中安装Win98

文章目录 一、下载Win98二、制作可启动光盘三、VMware中安装Win98四、Qemu中安装Win981. Qemu的安装2. 安装Win98 Win98是微软于1998年发布的16位与32位混合的操作系统,也是一代经典的操作系统,期间出现了不少经典的软件与游戏,还是值得怀念的…

Kafka - This server does not host this topic-partition

问题描述 org.apache.kafka.common.errors.UnknownTopicOrPartitionException: This server does not host this topic-partition 原因分析 分区数据不在。producer 向不存在的 topic 发送消息 解决方案 用户可以检查 topic 是否存在设置 auto.create.topics.enable 参数

【LiveVideoStack】批量下载公众号文章

livevideostack暂停商业运行 官方建议关注文章保存,因此: y9000p 上运行: xiaoguyu / wechatDownload 大神太厉害了,工具软件很好很强大 windows 试试直接安装、编译、运行 zhangbin@LAPTOP-DFV9CMRA MINGW64 /g/ISOFT/livevideostack $ git clone https://github.com/xiao…

安卓多个listView拖动数据交换位置和拖动

注意这里只是给出大概思路&#xff0c;具体可以参考修改自己想要的 public class MainActivity extends AppCompatActivity {private ListView listView1;private ListView listView2;private ArrayAdapter<String> adapter1;private ArrayAdapter<String> adapter…

研发效能DevOps: OpenEuler 部署 drone 持续集成平台

目录 一、实验 1.环境 2.OpenEuler 部署 drone 持续集成平台 二、问题 1.drone登录失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统架构版本IP备注LinuxopenEuler22.03 LTS SP2 192.168.204.145&#xff08;动态&#xff09; 192.168.204.141&…

jenkins容器中安装python遇到问题

在Jenkins容器中安装配置Python时遇到问题 执行./configure --prefix/opt/python3/时遇到configure: error: no acceptable C compiler found in $PATH 这个问题就是缺少gcc编译环境。将gcc安装上即可&#xff1a; yum install -y gcc##前提是容器里的系统是cenos才可以&#…

`sh -c`命令——解决命令权限问题、一条命令中执行多个指令

sh -c在Shell中执行一个字符串作为命令&#xff0c;可以在一条命令中执行多个指令。 目录 语法如下&#xff1a;案例 使用sh -c与直接终端敲命令的区别使用sh -c解决命令权限问题 语法如下&#xff1a; sh -c command其中&#xff0c;command是要执行的命令或命令串。 案例 …

Excel下拉自动填充

1、选中需要下拉填充的单元格&#xff0c;按下Ctrl&#xff0c;然后再往下拖动填充。 下拉结果&#xff1a; 2、选中两个连续的单元格&#xff0c;往下拖动填充&#xff0c;可以填充增加两数差。 下拉结果&#xff1a; 本文为学习笔记&#xff0c;所参考文章均已附上链接&#…

【Node.js从基础到高级运用】六、创建第一个 Node.js 应用

创建第一个 Node.js 应用 在这一节中&#xff0c;我们将引导你创建你的第一个 Node.js 应用——一个简单的 “Hello World” 程序。这将帮助你熟悉 Node.js 项目的基本结构和模块化编程的概念。 步骤 1: 初始化项目 首先&#xff0c;创建一个新的目录作为项目的根目录&#…

设计模式前置了解uml图

在开发前&#xff0c;会进行系统的设计&#xff0c;而数据模型的设计大多通过 UML 类图实现。为了在 UML 类图中清晰地表达类之间的关系&#xff0c;需要对类之间的关系有一定的认识&#xff0c;并且了解相关的表达符号。 类之间的关系有以下几种&#xff1a; 组合 聚合 关联…

个人商城系统开源(配置支付宝支付!)

原文地址&#xff1a;个人商城系统开源&#xff08;配置支付宝支付&#xff01;&#xff09; - Pleasure的博客 下面是正文内容&#xff1a; 前言 由于近期实在没有什么话题可写和一些有趣的项目教程可以分享。所以我只能决定将我自己亲手编写的一个迷你迷你商城系统进行开源…

手机和电脑同步的好用记事本软件有哪些

我常常需要随手记录各种信息&#xff0c;以便随时查阅和使用。比如&#xff0c;在下班路上&#xff0c;我会用手机记录明天要处理的工作事项、购物清单&#xff0c;或是某个突然迸发的创意想法&#xff1b;而在办公室&#xff0c;我则需要在电脑上整理会议纪要、项目计划&#…

java学习(集合)

一.集合(主要是单列集合和双列集合) 1.集合的框架体系&#xff08;两大类&#xff09; 2.collection接口是实现类的特点&#xff1a; 1)collection实现子类可以存放多个元素&#xff0c;每个元素可以是Object 2)有效Collection的实现类&#xff0c;可以存放重复的元素&#…

案例分析篇04:数据库设计相关28个考点(1~8)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12601310.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

基于Llama 2家族的提示词工程:Llama 2 Chat, Code Llama, Llama Guard

Prompt Engineering with Llama 2 本文是学习 https://www.deeplearning.ai/short-courses/prompt-engineering-with-llama-2/ 的学习笔记。 文章目录 Prompt Engineering with Llama 2What you’ll learn in this course [1] Overview of Llama Models[2] Getting Started wi…

华为配置ISP选路实现报文按运营商转发

Web举例&#xff1a;配置ISP选路实现报文按运营商转发 介绍通过配置ISP选路实现报文按运营商转发的配置举例。 组网需求 如图1所示&#xff0c;FW作为安全网关部署在网络出口&#xff0c;企业分别从ISP1和ISP2租用一条链路。 企业希望访问Server 1的报文从ISP1链路转发&#…

大语言模型提示工程简介

提示工程是一个较新的学科&#xff0c;应用于开发和优化提示词&#xff08;Prompt&#xff09;&#xff0c;帮助用户有效地将语言模型用于各种应用场景和研究领域。掌握了提示工程相关技能将有助于用户更好地了解大型语言模型的能力和局限性。研究人员可利用提示工程来提高大语…