算法40:线段树 + 懒更新

线段树:一种支持范围整体修改和范围整体查询的数据结构

解决的问题范畴:

大范围信息可以只由左、右两侧信息加工出, 而不必遍历左右两个子范围的具体状况。

白话版解释就是:针对数组可以范围进行修改和查询。

假设,一个数组为 {5,1,3,7,8}。我们利用二叉树的性质可以过数组进行加工,按照区间的方式进行分类。

下标:012345
数组:0忽略51378

这样的话,我们就可以利用二叉树的性质,构建一个新的结构,来进行范围统计了。如下:

假设,此时我们需要统计的是区间的累加和信息,那么按照这个结构就可以推算出来如下信息:

如果,把这些累加和信息存储在数组里面,得到如下信息:

利用二叉树层序遍历结构存储:

下标:0123456789
忽略24915637851

如果查询1-5范围的话,直接把下标为1的24返回即可,如果查询1-3次话,直接返回下标为2的9.

也就是说,线段树查询范围越大,性能越高。

懒更新:

就是对区间范围的数据进行更新操作,比如对1-5区间每个数组都加2. 那么我们直接统计出子元素只有5个,24 + 5*2 = 34;即直接把数组下标为1的24变更为34即可。这种只更新区间,而并没有对子元素进行更新,这就是懒更新。

那可能有人会说,你更新了1-5区间,我此时要查询1-3区间怎么办?

所谓的懒更新,只是在你使用到的时候更新,并不是不更新。你更新了1-5区间范围的累加和,我此时查询1-3范围,我们在查询的时候就需要知道1-5范围是否有懒更新情况,如果有就下放给左、右子节点,把左右子节点也给更新了。

理论说了这么多,代码到底该如何写呢?下面贴出3个版本。分别是新增功能、添加查询、

添加重置功能。比较起来更直观:

新增:

package code04.线段树_02;

public class SegmentTree1_add {

    //构建从下标1开始的新数组,为了使用二叉树的性质
    int[] arr;
    //汇总区间值
    int[] sum;
    //懒更新数组
    int[] lazy;
    public SegmentTree1_add(int[] origin)
    {
        int size = origin.length;
        arr = new int[size+1];
        sum = new int[size*4];
        lazy = new int[size*4];

        //为了使用二叉树的性质,需要构建一个从1开始的新数组
        for (int i = 1; i <= size; i++) {
            arr[i] = origin[i-1];
        }
    }

    //根据业务需求,需要构建线段树范围值(可以是累加和、也可以是max,视业务而定)
    public void build (int left, int right, int curIndex)
    {
        //只剩1个数
        if (left == right) {
            sum[curIndex] = arr[left];
            return;
        }

        int mid = (left + right)/2;
        //左子树, curIndex代表当前节点的下标。 curIndex*2代表当前节点curIndex的左孩子
        build(left, mid, curIndex*2);
        //右子树, curIndex*2 + 1代表当前节点curIndex的右孩子
        build(mid + 1, right, curIndex*2+1);

        //汇总
        collect(curIndex);
    }

    //汇总左、右孩子的累加和
    public void collect(int curIndex) {
        sum[curIndex] =  sum[curIndex * 2] + sum[curIndex * 2 + 1];
    }

    /**
     * @param left       代表数组范围区间的开始位置
     * @param right      代表数组范围区间的结束位置
     * @param start      任务开始位置
     * @param end        任务结束位置
     * @param value      从start到end范围之间,每个数加value
     */
    public void add (int left, int right, int curIndex, int start, int end, int value)
    {
        //代表任务需要更新 start 到 end区间
        //而 left 到 right 完全被任务的start到end包含。
        // 直接更新汇总数据, 具体的子数组暂不更新
        if (start <= left && right <= end) {
            //统计left到right之间数字个数,每个数都增加 value
            //区间更新,需要原有的sum[curIndex] 加上 新增的数值
            sum[curIndex] += (right - left + 1) * value;

            //此时,只是更新了区间汇总数据; 提升了时间复杂度;但是,
            //区间内的实际数字并没有实际更新。因此需要记录下这一次操作
            lazy[curIndex] = value;

            return;
        }

        //假设数组长度为10,当前区间为1-5,我们需要更新2-6,也就是说上方逻辑包
        //包不住;此时,原有的懒更新的数组记录的数据需要下放的子区间
        int mid = (left + right)/2;
        //下放之前lazy记录的数据, 有可能lazy没有数据
        pushDown(curIndex, mid - left + 1, right - mid);

        //更新左孩子区间
        if (start <= mid) {
            add(left, mid, curIndex*2, start, end, value);
        }
        //更新右孩子区间
        if (end > mid) {
            add(mid + 1, right, curIndex*2 + 1, start, end, value);
        }

        //左、右孩子区间都更新过了,需要汇总当前curIndex对应的区间汇总数据了
        collect(curIndex);
    }

    public void pushDown(int curIndex, int leftChildren, int righChildren)
    {
        //如果之前有懒更新, 那么把懒更新数组到下一层区间去更新
        if (lazy[curIndex] != 0) {
            //左孩子区间值更新
            sum[curIndex * 2] += leftChildren * lazy[curIndex];
            //左孩子并没有全部更新,需要记录懒更新信息
            lazy[curIndex*2] += lazy[curIndex];

            //右孩子区间值更新
            sum[curIndex * 2 + 1] += righChildren * lazy[curIndex];
            lazy[curIndex*2+1] += lazy[curIndex];

            //原有父区间重置
            lazy[curIndex] = 0;
        }
    }

    public static void main(String[] args) {
        int[] aa = {5,1,3,7,8};
        SegmentTree1_add ss = new SegmentTree1_add(aa);

        int left = 1;
        int right = aa.length;
        int root = 1;
        ss.build(left, right, root);

        //1-5区间全部加2
        ss.add(left, right, root, 1, 5, 2);
        System.out.println("over");
    }
}

增添查询:

package code04.线段树_02;

public class SegmentTree2_query {

    //构建从下标1开始的新数组,为了使用二叉树的性质
    int[] arr;
    //汇总区间值
    int[] sum;
    //懒更新数组
    int[] lazy;

    public SegmentTree2_query(int[] origin)
    {
        int size = origin.length;
        arr = new int[size+1];
        sum = new int[size*4];
        lazy = new int[size*4];

        //为了使用二叉树的性质,需要构建一个从1开始的新数组
        for (int i = 1; i <= size; i++) {
            arr[i] = origin[i-1];
        }
    }

    //根据业务需求,需要构建线段树范围值(可以是累加和、也可以是max,视业务而定)
    public void build (int left, int right, int curIndex)
    {
        //只剩1个数
        if (left == right) {
            sum[curIndex] = arr[left];
            return;
        }

        int mid = (left + right)/2;
        //左子树, curIndex代表当前节点的下标。 curIndex*2代表当前节点curIndex的左孩子
        build(left, mid, curIndex*2);
        //右子树, curIndex*2 + 1代表当前节点curIndex的右孩子
        build(mid + 1, right, curIndex*2+1);

        //汇总
        collect(curIndex);
    }

    //汇总左、右孩子的累加和
    public void collect(int curIndex) {
        sum[curIndex] =  sum[curIndex * 2] + sum[curIndex * 2 + 1];
    }

    /**
     * @param left       代表数组范围区间的开始位置
     * @param right      代表数组范围区间的结束位置
     * @param start      任务开始位置
     * @param end        任务结束位置
     * @param value      从start到end范围之间,每个数加value
     *
     * 区间范围新增、删除 value
     */
    public void add (int left, int right, int curIndex, int start, int end, int value)
    {
        //代表任务需要更新 start 到 end区间
        //而 left 到 right 完全被任务的start到end包含。
        // 直接更新汇总数据, 具体的子数组暂不更新
        if (start <= left && right <= end) {
            //统计left到right之间数字个数,每个数都增加 value
            //区间更新,需要原有的sum[curIndex] 加上 新增的数值
            sum[curIndex] += (right - left + 1) * value;

            //此时,只是更新了区间汇总数据; 提升了时间复杂度;但是,
            //区间内的实际数字并没有实际更新。因此需要记录下这一次操作
            lazy[curIndex] = value;

            return;
        }

        //假设数组长度为10,当前区间为1-5,我们需要更新2-6,也就是说上方逻辑包
        //包不住;此时,原有的懒更新的数组记录的数据需要下放的子区间
        int mid = (left + right)/2;
        //下放之前lazy记录的数据, 有可能lazy没有数据
        pushDown(curIndex, mid - left + 1, right - mid);

        //更新左孩子区间
        if (start <= mid) {
            add(left, mid, curIndex*2, start, end, value);
        }
        //更新右孩子区间
        if (end > mid) {
            add(mid + 1, right, curIndex*2 + 1, start, end, value);
        }

        //左、右孩子区间都更新过了,需要汇总当前curIndex对应的区间汇总数据了
        collect(curIndex);
    }

    public void pushDown(int curIndex, int leftChildren, int righChildren)
    {
        //如果之前有懒更新, 那么把懒更新数组到下一层区间去更新
        if (lazy[curIndex] != 0) {
            //左孩子区间值更新
            sum[curIndex * 2] += leftChildren * lazy[curIndex];
            //左孩子并没有全部更新,需要记录懒更新信息
            lazy[curIndex*2] += lazy[curIndex];

            //右孩子区间值更新
            sum[curIndex * 2 + 1] += righChildren * lazy[curIndex];
            lazy[curIndex*2+1] += lazy[curIndex];

            //原有父区间重置
            lazy[curIndex] = 0;
        }
    }



    //新增查询功能
    /**
     * @param left       代表数组范围区间的开始位置
     * @param right      代表数组范围区间的结束位置
     * @param start      任务开始位置
     * @param end        任务结束位置
     *
     * 在 left 到 right之间,查询 start到end 区间数据和
     */
    public int query(int left, int right, int curIndex, int start, int end)
    {
        if (start <= left && end >= right) {
            return sum[curIndex];
        }

        int ans = 0;
        int mid = (left + right)/2;
        //防止部分区间懒更新。 比如1-5范围懒更新记录了数据,但是查询1-4范围。此时数据不正确
        pushDown(curIndex, mid - left + 1, right - mid);
        if (left < mid) {
            ans += query(left, mid, curIndex * 2, start, end);
        }

        if (right > mid) {
            ans += query(mid + 1, right, curIndex * 2 + 1, start, end);
        }

        return ans;
    }


    public static void main(String[] args) {
        int[] aa = {5,1,3,7,8};
        SegmentTree2_query ss = new SegmentTree2_query(aa);

        int left = 1;
        int right = aa.length;
        int root = 1;
        ss.build(left, right, root);

        int ans = ss.query(left, right, root, 1, 4);
        System.out.println("添加数据之前: " + ans);

        //1-5区间全部加2
        ss.add(left, right, root, 1, 5, 2);

        //查询1-4区间
        int ans2 = ss.query(left, right, root, 1, 4);
        System.out.println("添加数据以后: " + ans2);
    }
}

重置:

package code04.线段树_02;

import java.sql.Struct;

public class SegmentTree2_reset {

    //构建从下标1开始的新数组,为了使用二叉树的性质
    int[] arr;
    //汇总区间值
    int[] sum;
    //懒更新数组
    int[] lazy;

    //记录是否重置
    boolean[] reset;
    //记录重置的值
    int[] change;

    public SegmentTree2_reset(int[] origin)
    {
        int size = origin.length;
        arr = new int[size+1];
        sum = new int[size*4];
        lazy = new int[size*4];

        reset = new boolean[size*4];
        change = new int[size*4];

        //为了使用二叉树的性质,需要构建一个从1开始的新数组
        for (int i = 1; i <= size; i++) {
            arr[i] = origin[i-1];
        }
    }

    //根据业务需求,需要构建线段树范围值(可以是累加和、也可以是max,视业务而定)
    public void build (int left, int right, int curIndex)
    {
        //只剩1个数
        if (left == right) {
            sum[curIndex] = arr[left];
            return;
        }

        int mid = (left + right)/2;
        //左子树, curIndex代表当前节点的下标。 curIndex*2代表当前节点curIndex的左孩子
        build(left, mid, curIndex*2);
        //右子树, curIndex*2 + 1代表当前节点curIndex的右孩子
        build(mid + 1, right, curIndex*2+1);

        //汇总
        collect(curIndex);
    }

    //汇总左、右孩子的累加和
    public void collect(int curIndex) {
        sum[curIndex] =  sum[curIndex * 2] + sum[curIndex * 2 + 1];
    }

    /**
     * @param left       代表数组范围区间的开始位置
     * @param right      代表数组范围区间的结束位置
     * @param start      任务开始位置
     * @param end        任务结束位置
     * @param value      从start到end范围之间,每个数加value
     *
     * 区间范围新增、删除 value
     */
    public void add (int left, int right, int curIndex, int start, int end, int value)
    {
        //代表任务需要更新 start 到 end区间
        //而 left 到 right 完全被任务的start到end包含。
        // 直接更新汇总数据, 具体的子数组暂不更新
        if (start <= left && right <= end) {
            //统计left到right之间数字个数,每个数都增加 value
            //区间更新,需要原有的sum[curIndex] 加上 新增的数值
            sum[curIndex] += (right - left + 1) * value;

            //此时,只是更新了区间汇总数据; 提升了时间复杂度;但是,
            //区间内的实际数字并没有实际更新。因此需要记录下这一次操作
            lazy[curIndex] = value;

            return;
        }

        //假设数组长度为10,当前区间为1-5,我们需要更新2-6,也就是说上方逻辑包
        //包不住;此时,原有的懒更新的数组记录的数据需要下放的子区间
        int mid = (left + right)/2;
        //下放之前lazy记录的数据, 有可能lazy没有数据
        pushDown(curIndex, mid - left + 1, right - mid);

        //更新左孩子区间
        if (start <= mid) {
            add(left, mid, curIndex*2, start, end, value);
        }
        //更新右孩子区间
        if (end > mid) {
            add(mid + 1, right, curIndex*2 + 1, start, end, value);
        }

        //左、右孩子区间都更新过了,需要汇总当前curIndex对应的区间汇总数据了
        collect(curIndex);
    }

    public void pushDown(int curIndex, int leftChildren, int righChildren)
    {
        //重置
        if (reset[curIndex]) {
            //左、右孩子重置
            reset[curIndex * 2] = true;
            reset[curIndex * 2 + 1] = true;

            //左右孩子重置的值
            change[curIndex * 2] = change[curIndex];
            change[curIndex * 2 + 1] = change[curIndex];

            //左右孩子的区间值也需要重置
            sum[curIndex * 2] = change[curIndex] * leftChildren;
            sum[curIndex * 2 + 1] = change[curIndex] * righChildren;

            //全部都重置了,之前懒更新数据就不关注了;就算是更新完再重置,还是一样的
            lazy[curIndex * 2] = 0;
            lazy[curIndex * 2 + 1] = 0;

            //更新curIndex节点
            reset[curIndex] = false;
        }

        //如果之前有懒更新, 那么把懒更新数组到下一层区间去更新
        if (lazy[curIndex] != 0) {
            //左孩子区间值更新
            sum[curIndex * 2] += leftChildren * lazy[curIndex];
            //左孩子并没有全部更新,需要记录懒更新信息
            lazy[curIndex*2] += lazy[curIndex];

            //右孩子区间值更新
            sum[curIndex * 2 + 1] += righChildren * lazy[curIndex];
            lazy[curIndex*2+1] += lazy[curIndex];

            //原有父区间重置
            lazy[curIndex] = 0;
        }
    }



    //新增查询功能
    /**
     * @param left       代表数组范围区间的开始位置
     * @param right      代表数组范围区间的结束位置
     * @param start      任务开始位置
     * @param end        任务结束位置
     *
     * 在 left 到 right之间,查询 start到end 区间数据和
     */
    public int query(int left, int right, int curIndex, int start, int end)
    {
        if (start <= left && end >= right) {
            return sum[curIndex];
        }

        int ans = 0;
        int mid = (left + right)/2;
        //防止部分区间懒更新。 比如1-5范围懒更新记录了数据,但是查询1-4范围。此时数据不正确
        pushDown(curIndex, mid - left + 1, right - mid);
        if (left < mid) {
            ans += query(left, mid, curIndex * 2, start, end);
        }

        if (right > mid) {
            ans += query(mid + 1, right, curIndex * 2 + 1, start, end);
        }

        return ans;
    }


    //重置start到end区域的值为 value
    public void resetZone(int left, int right, int curIndex, int start, int end, int value)
    {
        if (start <= left && end >= right) {
            reset[curIndex] = true;
            change[curIndex] = value;

            //重置,无论之前add是什么数据,一切回归默认值
            lazy[curIndex] = 0;
            //重置后,所有孩子都是默认值value
            sum[curIndex] = (right - left + 1) * value;
            return;
        }

        int mid = (left + right)/2;
        //防止部分区间懒更新。 比如1-5范围懒更新记录了数据,但是查询1-4范围。此时数据不正确
        pushDown(curIndex, mid - left + 1, right - mid);
        if (left < mid) {
            resetZone(left, mid, curIndex * 2, start, end, value);
        }

        if (right > mid) {
            resetZone(mid + 1, right, curIndex * 2 + 1, start, end, value);
        }

        collect(curIndex);
    }


    public static void main(String[] args) {
        int[] aa = {5,1,3,7,8};
        SegmentTree2_reset ss = new SegmentTree2_reset(aa);

        int left = 1;
        int right = aa.length;
        int root = 1;
        ss.build(left, right, root);

        int ans = ss.query(left, right, root, 1, 4);
        System.out.println("添加数据之前: " + ans);

        //1-5区间全部加2
        ss.add(left, right, root, 1, 5, 2);

        //查询1-4区间
        int ans2 = ss.query(left, right, root, 1, 4);
        System.out.println("添加数据以后: " + ans2);

        //重置2-4范围
        ss.resetZone(left, right, root, 2, 4, 2);
        //查询1-4区间
        int ans3 = ss.query(left, right, root, 1, 4);
        System.out.println("重置以后 :" + ans3);
    }
}

可能有人也会说,这种数组结构,算累加和。我用前缀数组技术也完全可以解决。是的,但是线段树解决的问题,并不是前缀树组一定能够解决的。而且线段树的时间复杂度也很低。

线段树的时间复杂度为 O(logN)。 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云原生 API 网关链路追踪能力重磅上线

云原生API网关介绍 云原生 API 网关是腾讯云基于开源网关推出的一款高性能高可用的云原生 API 网关产品&#xff0c;作为云上流量入口&#xff0c;集成请求分发、API 管理、流量监控、访问限制等功能&#xff0c;是微服务架构和容器架构中的重要组件。 TSE 云原生 API 网关提…

蓝桥杯-常用STL(二)

常用STL &#x1f388;1.集合&#x1f388;2.set的基础使用&#x1f52d;2.1引入库&#x1f52d;2.2插入元素&#x1f52d;2.3删除元素&#x1f52d;2.4判断元素是否存在&#x1f52d;2.5遍历元素&#x1f52d;2.6清空 &#x1f388;3.set与结构体 &#x1f388;1.集合 &#x…

Maven dependency中的scope

Maven的一个哲学是惯例优于配置(Convention Over Configuration), Maven默认的依赖配置项中&#xff0c;scope的默认值是compile。 scope的分类 compile&#xff08;默认&#xff09; 含义&#xff1a; compile 是默认值&#xff0c;如果没有指定 scope 值&#xff0c;该元素…

【C语言刷题系列】喝汽水问题

文章目录 一、文章简介 1.先买再换 1.1 代码逻辑&#xff1a; 1.2 完整代码 1.3 运行结果 1.4 根据方法一总结优化 2.边买边换 2.1 代码逻辑&#xff1a; 2.2 完整代码 2.3 运行结果 一、文章简介 本文所述专栏——C语言经典编程问题 C语言刷题_倔强的石头106的博客…

C#代码添加脚本头

目录 前言 代码展示 前言 创建脚本的时候添加脚本的介绍 代码展示 using System.IO;/// <summary> /// 创建脚本自动添加头注 /// </summary> public class CommentFirst : UnityEditor.AssetModificationProcessor {/// <summary>/// 在资源创建生成.me…

15个好的在线课程细分市场(+真实MemberPress网站案例)

开发和销售在线课程可能是一种很好的谋生方式。借助市场上的课程插件&#xff0c;您甚至不必成为网页设计或开发方面的专家即可创建高端虚拟学习体验。 为了让您的在线课程有一个良好的开端&#xff0c;您需要对其定位进行一些思考。这可能感觉像是一个压倒性的决定&#xff0…

IDEA 配置和缓存目录 设置

IDEA系列产品&#xff0c;一般会在用户目录创建 配置 和 缓存 目录&#xff1a; %APPDATA%\JetBrains%LOCALAPPDATA%\JetBrains 一般会展示为&#xff1a; C:\Users\<username>\AppData\Roaming\JetBrainsC:\Users\<username>\AppData\Local\JetBrains 一般占用…

在 Windows 10 上使用 Visual Studio 2022 进行 C++ 桌面开发

工具下载链接&#xff1a;https://pan.quark.cn/s/c70b23901ccb 环境介绍 在今天的快速发展的软件开发行业中&#xff0c;选择合适的开发环境是非常关键的一步。对于C开发人员来说&#xff0c;Visual Studio 2022&#xff08;VS2022&#xff09;是一个强大的集成开发环境&…

手机云控制发电机组 有网络随时随地操控监控运行

GenCloudTM 发电机组云控系统简介 Ver2.0 目录 公司简介…… …………………………… ………………………………………………1概 述…… …………………………… ………………………………………………1主要功能及特点………… …………… ………… ………………………………

卡密社区SUP系统总控源码+主站分销系统功能源码

卡密社区SUP系统总控源码主站分销系统功能源码 跟以前的卡盟那种控制端差不多总控可以给别人开通&#xff0c;分销&#xff0c;主站&#xff0c;类似自己做系统商一样&#xff0c;自助发卡&#xff0c;卡密交易系统。 搭建环境Nginx1.22 mysql 5.7 php8.1 rids 7.2 安装方法…

深入理解 Golang 的 crypto/elliptic:椭圆曲线密码学的实践指南

深入理解 Golang 的 crypto/elliptic&#xff1a;椭圆曲线密码学的实践指南 引言crypto/elliptic 库概览基本使用教程高级应用案例性能与安全考量结论 引言 在当今数字时代&#xff0c;数据安全和加密技术成为了信息技术领域的重中之重。特别是在网络通信和数据存储领域&#…

unity3d的海盗王白银城演示

这是一个外网上的下载的海盗王unity3d制作的白银城演示场景。 地图只含有白银城区&#xff0c;没有野外和怪物。 当然也没有服务器端的。 我对灯光、摄像头、天空背景等做过调整&#xff0c;使它显示起来比较鲜丽。 它的模型和贴图是直接拿了海盗的&#xff0c;没有做过优化调整…

04 模块基础 隐藏模块

文章目录 PRE.内核中的内核模块&#xff1a;module 结构体Step-I. /proc/modules 信息隐藏Step-II. /sys/module/ 信息隐藏 当我们将一个 LKM 装载到内核模块中之后&#xff0c;用户尤其是服务器管理员可以使用 lsmod 命令 发现你在服务器上留下的rootkit arttnba3ubuntu:~/D…

【数据结构】单向链表实现 超详细

目录 一. 单链表的实现 1.准备工作及其注意事项 1.1 先创建三个文件 1.2 注意事项&#xff1a;帮助高效记忆和理解 2.链表的基本功能接口 2.0 创建一个 链表 2.1 链表的打印 3.链表的创建新节点接口 4.链表的节点插入功能接口 4.1 尾插接口 4.2 头插接口 4.3 指定位…

AI监控+智能充电桩系统如何缓解新能源汽车充电难问题

在新能源汽车行业的快速发展中&#xff0c;充电桩作为重要的配套设施&#xff0c;其建设和发展至关重要。随着新能源汽车销量的增长&#xff0c;补能需求也日益迫切&#xff0c;这为充电桩行业的发展提供了巨大的机遇。然而&#xff0c;充电桩行业在快速发展的同时&#xff0c;…

02-Java抽象工厂模式 ( Abstract Factory Pattern )

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是围绕一个超级工厂创建其他工厂 该超级工厂又称为其他工厂的工厂 在抽象工厂模式中&#xff0c;接口是负责创建一个相关对象的工厂&#xff0c;不需要显式指定它们的类 每个生成的工厂都能按照工厂模式提供对象 …

Cocos XR的WebBox实现流程

1. 正常3D场景下的webview 1.1 组件角色 Cocos Creator正常3D场景下只有在UI组件才支持webview&#xff0c;即作为下图中的UI Nodes(Canvas Node)的子节点&#xff0c;和3D组件是隔离开的&#xff0c;不能显示在3D空间中&#xff0c;UI Nodes(Canvas Node)是一个平面内的矩形…

RDBMS-MySQL高级

数据操作语句&#xff08;DML&#xff09;多表/关联查询Mysql中的函数事务执行流程数据库的备份与还原数据库表设计三范式 一、数据操作语句&#xff08;DML&#xff09; 插入数据 语法&#xff1a; 1.1插入&#xff08;insert [into]&#xff09;或添加一条数据 -- 指定列…

Python||五城P.M.2.5数据分析与可视化_使用华夫图分析各个城市的情况(上)

目录 五城P.M.2.5数据分析与可视化——北京市、上海市、广州市、沈阳市、成都市&#xff0c;使用华夫图分析各个城市的情况 1.北京市的空气质量 2.广州市的空气质量 【上海市和成都市空气质量情况详见下期】 五城P.M.2.5数据分析与可视化——北京市、上海市、广州市、沈阳市、成…

【Go语言成长之路】引入外部包

文章目录 引入外部包一、查找需要引用的包二、引入需要导入的包三、运行程序 引入外部包 ​ 实现Demo: 引用rsc.io/quote包来实现打印输出 一、查找需要引用的包 ​ 比如说我现在想要找一个quote的包&#xff0c;那么可以通过如下步骤进行操作&#xff1a; 访问pkg.go.dev,并…