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

news2024/10/5 20:21:30

目录

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 [][]int

    if 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.Val
            if sum == targetSum{
                path = append(path,node.Val)
                array = append(array,path)
                //打印的值,下图中的标准输出
                fmt.Println(path,array)
            }
            return
        }

        sum += node.Val
        path = 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 [][]int

    if 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.Val
            if sum == targetSum{
                path = append(path, node.Val)
				//这里对path的值进行复制
				copyPath := make([]int, len(path))
				copy(copyPath, path)
				array = append(array, copyPath)
            }
            return
        }

        sum += node.Val
        path = 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 main

import "fmt"

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *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 [][]int

	if 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.Val
			if sum == targetSum {
				//未修改哈!
				path = append(path, node.Val)
				array = append(array, path)
				fmt.Println(path, array)
			}
			return
		}

		sum += node.Val
		path = 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.coloradmin.cn/o/1509956.html

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

相关文章

vue学习笔记23-组件事件⭐

组件事件 在组件的模板表达式中,可以直接使用$emit方法触发自定义事件;触发自定义事件的目的是组件之间传递数据 好好好今天又碰到问题了,来吧来吧 测试发现其他项目都可以 正常的run ,就它不行 搜索发现新建项目并进入以后,用指…

【刷题日志3.4--3.10】

绕过flag关键字od读取&#xff08;脚本&#xff09;空格过滤 [广东强网杯 2021 团队组]love_Pokemon <?php error_reporting(0); highlight_file(__FILE__); $dir sandbox/ . md5($_SERVER[REMOTE_ADDR]) . /;if(!file_exists($dir)){mkdir($dir); }function DefenderBon…

(vb-asp.net)lw-学生信息管理系统(学生成绩管理,补考考场分配)-158-(代码+说明)

转载地址: http://www.3q2008.com/soft/search.asp?keywordasp.net&#xff09;lw 非常不错! 有兴趣的可以咨询客服, 或下载演示查看 目 录 ABSTRACT 3 1&#xff0e; 系统规划 6 1&#xff0e;3 需求分析 6 1&#xff0e;3&#xff0e;1 功能需求 6 通过了解学生管理系统…

HTTP/2、HTTP/3对HTTP/1.1的性能改进和优化

HTTP/1.1 相比 HTTP/1.0 提高了什么性能&#xff1f; 性能上的改进&#xff1a; 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。 支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等其回来&#xff0c;就可以…

C++14之std::index_sequence和std::make_index_sequence

相关文章系列 std::apply源码分析 C之std::tuple(一) : 使用精讲(全) 目录 1.std::integer_sequence 2.std::index_sequence 3.std::make_index_sequence 4.运用 4.1.打印序列的值 4.2.编译时求值 4.3.std::tuple访问值 5.总结 1.std::integer_sequence 运行时定义一个…

OSI七层模型TCP四层模型横向对比

OSI 理论模型&#xff08;Open Systems Interconnection Model&#xff09;和TCP/IP模型 七层每一层对应英文 应用层&#xff08;Application Layer&#xff09; 表示层&#xff08;Presentation Layer&#xff09; 会话层&#xff08;Session Layer&#xff09; 传输层&#x…

【JavaScript】面试手撕深拷贝

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深拷贝的作用深浅拷贝的区别浅拷贝深拷贝 深拷贝实现方式JSON.parse(JSON.stringi…

开发知识点-python-Tornado框架

介绍 Tornado是一个基于Python语言的高性能Web框架和异步网络库&#xff0c;它专注于提供快速、可扩展和易于使用的网络服务。由于其出色的性能和灵活的设计&#xff0c;Tornado被广泛用于构建高性能的Web应用程序、实时Web服务、长连接的实时通信以及网络爬虫等领域。 Torna…

【物联网设备端开发】FastBee Arduino固件开发指南

目录 一、收集数据 二、打开FastBeeArduino 源码 三、修改 Config.cpp 文件 四、修改物模型数据 五、小程序配网 本文以 WeMOS D1 R1&#xff08;8266WIFI 模块&#xff09;固件开发为例&#xff0c;实现以下功能&#xff1a; 设备认证设备 Mqtt 交互Wifi 类设备配网 一…

ffmpeg解码和渲染理解

ffmpeg解码和渲染理解 ffmpeg视频解码步骤 FFmpeg 是一个功能强大的跨平台多媒体处理工具&#xff0c;包含了音视频编解码、封装/解封装、过滤器等功能。下面是一般情况下使用 FFmpeg 进行视频解码的步骤&#xff1a; 初始化 FFmpeg 库&#xff1a;首先需要初始化 FFmpeg 库&a…

SAM(Segment Anything Model)大模型使用--point prompt

概述 本系列将做一个专题&#xff0c;主要关于介绍如何在代码上运行并使用SAM模型以及如何用自己的数据集微调SAM模型&#xff0c;也是本人的毕设内容&#xff0c;这是一个持续更新系列&#xff0c;欢迎大家关注~ SAM&#xff08;Segment Anything Model&#xff09; SAM基于…

自然语言处理的概念及发展介绍

自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;是计算机科学、人工智能和语言学的交叉领域&#xff0c;旨在使计算机能够理解、解释和生成人类语言。自然语言处理的发展对于实现人机交互、信息检索、机器翻译、情感分析等应用至关重要。 概念…

【Java设计模式】十五、命令模式

文章目录 1、命令模式2、案例3、总结 1、命令模式 餐厅点餐&#xff1a; 创建一个厨师对象&#xff0c;让服务员对象调用厨师对象中的方法进行点餐通知&#xff0c;当后面厨师换人&#xff0c;服务员类的代码也要修改&#xff0c;耦合 不符合开闭。理想状态&#xff1a;服务员…

JVM 垃圾回收相关

一、什么是垃圾 目录 一、什么是垃圾回收 二、 死亡对象的判断算法 a) 引用计数算法 b)可达性分析算法 三、垃圾回收算法 a) 标记-清除算法 b) 复制算法 c) 标记-整理算法 d) 分代算法 回收 垃圾回收&#xff08;Garbage Collection&#xff0c;简称GC&#xff09;是…

考研C语言复习初阶(5)

目录 一.表达式求值 1.1隐式类型转换 1.2 算术转换 12.3 操作符的属性 二. 指针是什么&#xff1f; 三 指针和指针类型 3.1 指针-整数 3.2 指针的解引用 3.3 野指针 四.指针运算 4.1 指针-整数 4.2 指针-指针 4.3 指针的关系运算 5. 指针和数组 6. 二级指针 …

使用IAD电话交换机(语音网关)将电话外线对接到FreeSWITCH SIP服务器

在我们初步了解了FreeSWITCH这样的SIP服务器之后&#xff0c;常见的一个需求就是把真实的电信世界&#xff08;比如固话、手机&#xff09;对接到SIP服务器里。 今天我们就介绍一个简单的方法&#xff0c;在3分钟内就把电信局和你的SIP软交换机融合通信起来。 IAD和SIP服务器环…

解决arco-design路由跳转,menu不激活的问题

问题 点击【返回】&#xff0c;路由跳转上一层至首页。左侧菜单栏没有实时更新&#xff0c;激活状态有问题。 解决方法如下&#xff0c;不闪白屏 Main.vue <template><div class"main"><a-layout class"main-layout"><a-layout-…

CH343 使用USB转串口发送CAN报文

文章目录 原启UART 走CAN收发器CH343 模拟CAN发送CPP ASIO SocketCANVXCANGithub Link 原启 早些年自动驾驶激光雷达还不支持PTP之类的时间同步, 很多都是用PPS时间同步, 激光雷达一般装的离控制器或者GNSS天线较远, 车上的线束一般数据电源各种都包在一起的, 如果3.3V直接从域…

私立医院的革命者:大数据解决方案全面解析

第一部分&#xff1a;背景 在信息化飞速发展的今天&#xff0c;医疗行业正经历着一场深刻的数字化转型。特别是对于私立医院来说&#xff0c;要在这个变革的浪潮中立于不败之地&#xff0c;就必须拥抱新技术&#xff0c;优化服务流程&#xff0c;提高医疗质量。大数据技术&…

Python教程-SchemDraw绘制电路图

电路图是电子工程师和电子爱好者的重要工具&#xff0c;用于图形化表示电子元件之间的连接关系。在Python中&#xff0c;有许多库可以用于绘制电路图&#xff0c;其中之一就是SchemDraw。本文将介绍如何使用SchemDraw库&#xff0c;通过简单的Python代码绘制出清晰、美观的电路…