lift-splat-shoot(LSS)代码详解

news2024/11/26 11:57:34

原版lift-splat-shoot(LSS)代码详解

  • 自己想搞一些事情,搞什么呢?和自动驾驶相关的,先走视觉路线的又比较多,bev的话就搞开山之作lss,看有什么可以优化的东西,于是就开始做一做试试看。然后也做了一些尝试但是效果都不太行,也不太想说用换一些更强大的特征提取器来涨点,然后就改一改结构,也在尝试。
  • 不过新方向是搞分割大模型,后面等差不了也把sam注释一下。
  • 想着把lss优化一下,做个插件加到当前的sota上还能刷个点,但是也没人一起也没什么资源,然后就有一篇ealss把我要做的给做了。总之加油!
    在这里插入图片描述

# 这是lss中最为核心的部分,之前有尝试对其进行修改优化,现在将原始代码部分发布出来进行解析方便后来人
# 整体流程其实非常简单,先对图像提取特征拿深度和语义相乘,然后构造frusum作为几何点,根据几何点在坐标转换到bev空间后拍扁,再提取bev特征出结果
import torch
import random
import math
import einops
from torch import nn
from efficientnet_pytorch import EfficientNet
from torchvision.models.resnet import resnet18
from .tools import gen_dx_bx, cumsum_trick, QuickCumsum # 一个快速求和的技巧,在网上可以找到很多讲解

# 以下是所谓“棱台求和”技巧,没听过这个词不清楚是不是专业术语
class QuickCumsum(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, geom_feats, ranks):
        x = x.cumsum(0) # 这一步求解前缀和  eg:1 3 2 1 2 -> 1 4 6 7 9
        kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool) #建立了一个x长度的全一向量用来做区间索引
        kept[:-1] = (ranks[1:] != ranks[:-1]) #这一步是判断排序后的索引前后相邻是否一致 eg:0 1 1 2 3 -> 1 0 1 1 1 

        x, geom_feats = x[kept], geom_feats[kept] #根据上面的索引来取出对应的元素 x.shape [47345, 64] geom_feats.shape [47345, 4]
        x = torch.cat((x[:1], x[1:] - x[:-1])) #错位相减再加上第一个

        # save kept for backward
        ctx.save_for_backward(kept)

        # no gradient for geom_feats
        ctx.mark_non_differentiable(geom_feats)

        return x, geom_feats

    @staticmethod
    def backward(ctx, gradx, gradgeom):
        kept, = ctx.saved_tensors
        back = torch.cumsum(kept, 0)
        back[kept] -= 1

        val = gradx[back]

        return val, None, None

class LayerNormProxy(nn.Module):
    
    def __init__(self, dim):
        
        super().__init__()
        self.norm = nn.LayerNorm(dim)

    def forward(self, x):

        x = einops.rearrange(x, 'b c h w -> b h w c')
        x = self.norm(x)
        return einops.rearrange(x, 'b h w c -> b c h w')
    

class Up(nn.Module):
    def __init__(self, in_channels, out_channels, scale_factor=2):
        super().__init__()

        self.up = nn.Upsample(scale_factor=scale_factor, mode='bilinear',
                              align_corners=True)

        self.conv = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

    def forward(self, x1, x2):
        x1 = self.up(x1)
        x1 = torch.cat([x2, x1], dim=1)
        return self.conv(x1)


class CamEncode(nn.Module):
    def __init__(self, D, C, downsample):
        super(CamEncode, self).__init__()
        self.D = D
        self.C = C
        self.stride = stride = 1
        self.n_group_channels = 41

        self.trunk = EfficientNet.from_pretrained("efficientnet-b0")

        self.up1 = Up(320+112, 512)
        self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0)

    def get_depth_dist(self, x, eps=1e-20):
        # sparsemax = Sparsemax(dim=1)  # Specify the dimension along which to apply Sparsemax
        # return sparsemax(x)
        return x.softmax(dim=1)

    def get_depth_feat(self, x):
        x = self.get_eff_depth(x) #out [24, 512, 8, 22]

        x = self.depthnet(x) #[24, 512, 8, 22] -> [24, 105, 8, 22] 105维中前41维度是深度,后64维是语义
        
        
        depth = self.get_depth_dist(x[:, :self.D]) #[24,41,8,22] 用softmax取所谓深度分布


        new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2) #这里便是论文中所谓的深度分布的概率乘上图像的特征,经典操作

        return  new_x #[24, 64, 41, 8, 22]

    # 这里用的/EfficientNetb0用来提feature
    def get_eff_depth(self, x):
        # adapted from https://github.com/lukemelas/EfficientNet-PyTorch/blob/master/efficientnet_pytorch/model.py#L231
        endpoints = dict()

        # Stem
        x = self.trunk._swish(self.trunk._bn0(self.trunk._conv_stem(x)))
        prev_x = x

        # Blocks
        for idx, block in enumerate(self.trunk._blocks):
            drop_connect_rate = self.trunk._global_params.drop_connect_rate
            if drop_connect_rate:
                drop_connect_rate *= float(idx) / len(self.trunk._blocks) # scale drop connect_rate
            x = block(x, drop_connect_rate=drop_connect_rate)
            if prev_x.size(2) > x.size(2):
                endpoints['reduction_{}'.format(len(endpoints)+1)] = prev_x
            prev_x = x

        # Head
        endpoints['reduction_{}'.format(len(endpoints)+1)] = x
        x = self.up1(endpoints['reduction_5'], endpoints['reduction_4'])
        return x #[24, 512, 8, 22]

    def forward(self, x):
        x = self.get_depth_feat(x)

        return  x

# bevencode 用的resnet18的三层,具体的输入输出可以跟着代码看,没什么好细说的
class BevEncode(nn.Module):
    def __init__(self, inC, outC):
        super(BevEncode, self).__init__()

        trunk = resnet18(pretrained=False, zero_init_residual=True)
        self.conv1 = nn.Conv2d(inC, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = trunk.bn1
        self.relu = trunk.relu

        self.layer1 = trunk.layer1
        self.layer2 = trunk.layer2
        self.layer3 = trunk.layer3

        self.up1 = Up(64+256, 256, scale_factor=4)
        self.up2 = nn.Sequential(
            nn.Upsample(scale_factor=2, mode='bilinear',
                              align_corners=True),
            nn.Conv2d(256, 128, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),
            nn.Conv2d(128, outC, kernel_size=1, padding=0),
        )

    def forward(self, x):
        x = self.conv1(x)  # x: 4 x 64 x 200 x 200 
        x = self.bn1(x)    # x: 4 x 64 x 100 x 100
        x = self.relu(x) 

        x1 = self.layer1(x)  # x1: 4 x 64 x 100 x 100
        x = self.layer2(x1)  # x: 4 x 128 x 50 x 50
        x = self.layer3(x)   # x: 4 x 256 x 25 x 25

        x = self.up1(x, x1)  # 给x进行4倍上采样然后和x1 concat 在一起  x: 4 x 256 x 100 x 100
        x = self.up2(x)      # 2倍上采样->3x3卷积->1x1卷积  x: 4 x 1 x 200 x 200

        return x


class LiftSplatShoot(nn.Module):
    def __init__(self, grid_conf, data_aug_conf, outC):
        super(LiftSplatShoot, self).__init__()
        
        self.grid_conf = grid_conf #{'xbound': [-50.0, 50.0, 0.5], 'ybound': [-50.0, 50.0, 0.5], 'zbound': [-10.0, 10.0, 20.0], 'dbound': [4.0, 45.0, 1.0]}
        self.data_aug_conf = data_aug_conf

        dx, bx, nx = gen_dx_bx(self.grid_conf['xbound'],
                                              self.grid_conf['ybound'],
                                              self.grid_conf['zbound'],
                                              )
        self.dx = nn.Parameter(dx, requires_grad=False) #[ 0.5000,  0.5000, 20.0000
        self.bx = nn.Parameter(bx, requires_grad=False) #[-49.7500, -49.7500,   0.0000]
        self.nx = nn.Parameter(nx, requires_grad=False) #[200, 200,   1]

        self.downsample = 16
        self.camC = 64
        self.frustum = self.create_frustum()
        self.D, _, _, _ = self.frustum.shape
        self.camencode = CamEncode(self.D, self.camC, self.downsample)
        self.bevencode = BevEncode(inC=self.camC, outC=outC)
        

        # toggle using QuickCumsum vs. autograd
        self.use_quickcumsum = True
        

        
    def create_frustum(self):
        # make grid in image plane
        ogfH, ogfW = self.data_aug_conf['final_dim'] #158 352
        fH, fW = ogfH // self.downsample, ogfW // self.downsample #8 22
        ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW) #shape [41, 8, 22]
        D, _, _ = ds.shape #41
        xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW) #(0->351,分为22) shape [41, 8, 22]
        ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW) # [41, 8, 22]
        """
            1. torch.linspace(0, ogfW - 1, fW, dtype=torch.float)
            tensor([0.0000, 16.7143, 33.4286, 50.1429, 66.8571, 83.5714, 100.2857,
                    117.0000, 133.7143, 150.4286, 167.1429, 183.8571, 200.5714, 217.2857,
                    234.0000, 250.7143, 267.4286, 284.1429, 300.8571, 317.5714, 334.2857,
                    351.0000])
                    
            2. torch.linspace(0, ogfH - 1, fH, dtype=torch.float)
            tensor([0.0000, 18.1429, 36.2857, 54.4286, 72.5714, 90.7143, 108.8571,
                    127.0000])
            """
        # D x H x W x 3
        frustum = torch.stack((xs, ys, ds), -1) #shape [41, 8, 22, 3]
        return nn.Parameter(frustum, requires_grad=False)

    def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
        """Determine the (x,y,z) locations (in the ego frame)
        of the points in the point cloud.
        Returns B x N x D x H/downsample x W/downsample x 3

        rots:由相机坐标系->车身坐标系的旋转矩阵,rots = (bs, N, 3, 3);
        trans:由相机坐标系->车身坐标系的平移矩阵,trans=(bs, N, 3);
        intrinsic:相机内参,intrinsic = (bs, N, 3, 3);
        post_rots:由图像增强引起的旋转矩阵,post_rots = (bs, N, 3, 3);
        post_trans:由图像增强引起的平移矩阵,post_trans = (bs, N, 3)"""
        B, N, _ = trans.shape # shape [4,6,3]

        # undo post-transformation
        # B x N x D x H x W x 3
        points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3) #[4, 6, 41, 8, 22, 3]
        points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1)) #[4, 6, 41, 8, 22, 3, 1]
        
        # 这里也是全文操作中最重要的经典操作,这里的Point是uv像素坐标,下面把深度z给乘到xy上,在第五个通道山再进行拼接,就是赋值给了
        # 所有像素点可能的深度,从445间隔为一米的距离
        points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
                            points[:, :, :, :, :, 2:3]
                            ), 5)
        # cam_to_ego
        combine = rots.matmul(torch.inverse(intrins))
        points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
        points += trans.view(B, N, 1, 1, 1, 3)
        return points

    def get_cam_feats(self, x):
        """Return B x N x D x H/downsample x W/downsample x C
        """
        B, N, C, imH, imW = x.shape #[4, 6, 3, 128, 352]

        x = x.view(B*N, C, imH, imW)
        x = self.camencode(x) # [24, 3, 128, 352] -> [24, 64, 41, 8, 22]
        x = x.view(B, N, self.camC, self.D, imH//self.downsample, imW//self.downsample) # [24, 64, 41, 8, 22] -> [4, 6, 64, 41, 8, 22]
        x = x.permute(0, 1, 3, 4, 5, 2) #[4, 6, 41, 8, 22, 64]

        return x

    def voxel_pooling(self, geom_feats, x):  #[4, 6, 41, 8, 22, 3]) [4, 6, 41, 8, 22, 64]
        B, N, D, H, W, C = x.shape
        Nprime = B*N*D*H*W #4*6*41*8*22
        # pdb.set_trace()
        # flatten x
        x = x.reshape(Nprime, C)

        # flatten indices
                     # (               [-50., -50., -10.])  / [ 0.5000,  0.5000, 20.0000]
        geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long()# ego下的空间坐标转换到体素坐标(计算栅格坐标并取整)
        geom_feats = geom_feats.view(Nprime, 3)
        batch_ix = torch.cat([torch.full([Nprime//B, 1], ix, 
                             device=x.device, dtype=torch.long) for ix in range(B)])  #(173184 // 4) =43296
        geom_feats = torch.cat((geom_feats, batch_ix), 1)  #torch.Size([173184, 3])   torch.Size([173184, 1])

        # filter out points that are outside box 200 200 1
        kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
            & (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
            & (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
        
        x = x[kept]
        geom_feats = geom_feats[kept]

        # get tensors from the same voxel next to each other
        ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
            + geom_feats[:, 1] * (self.nx[2] * B)\
            + geom_feats[:, 2] * B\
            + geom_feats[:, 3]
        
        sorts = ranks.argsort()
        x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts]
        
        # cumsum trick
        if not self.use_quickcumsum:
            x, geom_feats = cumsum_trick(x, geom_feats, ranks) # geom is [29072, 4] ,x is [29072,64]
        else:
            x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks) # geom is [29072, 4] ,x is [29072,64]
        # griddify (B x C x Z x X x Y)
        final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device)
        final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # x is [29072, 64]
        """
        这一行代码的目的是将经过池化操作后的特征值 `x` 分配到最终的体素池化结果张量 `final` 中。
        1. `geom_feats[:, 3]`:这部分取出 `geom_feats` 中的第四列,也就是之前计算出的每个点所属的体素编号。
        2. `geom_feats[:, 2]`:这部分取出 `geom_feats` 中的第三列,对应每个点在 Z 方向上的体素坐标。
        3. `geom_feats[:, 0]`:这部分取出 `geom_feats` 中的第一列,对应每个点在 X 方向上的体素坐标。
        4. `geom_feats[:, 1]`:这部分取出 `geom_feats` 中的第二列,对应每个点在 Y 方向上的体素坐标。
        这些坐标信息一起用来索引 `final` 张量中的位置,从而将经过池化操作后的特征值 `x` 放入相应的体素位置。
        综合上述四个部分,`final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]]
        ` 这个表达式会将 `x` 中的值根据点的体素编号以及在体素内的坐标索引放入到 `final` 张量的相应位置,从而实现体素池化的效果。
        """
        # collapse Z
        final = torch.cat(final.unbind(dim=2), 1)

        return final

    
    def get_voxels(self, x, rots, trans, intrins, post_rots, post_trans):
        d,x = self.get_cam_feats(x) #[4, 6, 3, 128, 352] 拿图像feature
    
        geom = self.get_geometry(rots, trans, intrins, post_rots, post_trans) #构造所谓frustum
            
        x = self.voxel_pooling(geom, x) #([4, 6, 41, 8, 22, 3]) [4, 6, 41, 8, 22, 64] -> [4, 64, 200, 200]

        return x 

    # 这个类在初始化完成后进入forward,从这里一步一步跳转看就行了 
    def forward(self, x, rots, trans, intrins, post_rots, post_trans):
        
        x = self.get_voxels(x, rots, trans, intrins, post_rots, post_trans)
        x = self.bevencode(x) #input [4, 64, 200, 200] ,output  [4, 1, 200, 200] 最后这个1就是类别了
        return x

# 这是在主函数中调用的,是LSS单元的入口
def compile_model(grid_conf, data_aug_conf, outC):
    
    return LiftSplatShoot(grid_conf, data_aug_conf, outC)

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

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

相关文章

编译原理简介

编译原理简介 编译原理的研究对于理解和设计编程语言、编译器和解释器都非常重要。它不仅可以提高程序的执行效率&#xff0c;还可以帮助开发人员更好地理解程序的运行机制。编译原理是计算机科学中的一个重要分支&#xff0c;研究的是编译器的设计和实现。对于从事编译器开发…

人员重识别:Person Re-Identification without Identification via Event Anonymization

论文作者&#xff1a;Shafiq Ahmad,Pietro Morerio,Alessio Del Bue 作者单位&#xff1a;Istituto Italino di Tecnologia;Universita degli Studi di Genova 论文链接&#xff1a;http://arxiv.org/abs/2308.04402v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;…

欧拉公式推导网格中点线面估计数量关系

欢迎关注更多精彩 关注我&#xff0c;学习常用算法与数据结构&#xff0c;一题多解&#xff0c;降维打击。 背景 之前面试网格算法工程师时被问到三角网格中点和面的数量关系。delaunay 三角剖分要估计边的数量来事先申请内存。 通过查找资料了解原理和推导过程。 欧拉公式…

重生奇迹海魔是亚特兰蒂斯的最后一站

当重生奇迹MU玩家开始打算挑战海魔希特拉时&#xff0c;通常就意味着这一次亚特兰蒂斯的旅程已经走到了尾声&#xff0c;因为这个海魔便是海洋的最后一站&#xff0c;成功抵达后就能顺利地通关这片海域。同时&#xff0c;如果此刻玩家等级已经到达130级&#xff0c;那么沙漠地图…

方法:Ubuntu配置网络源-apt源、pip源、conda源

apt源 方法1 编辑/etc/apt/sources.list: sudo gedit /etc/apt/sources.list 写入&#xff0c;如阿里源 deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe mu…

最简单的RNN预测股票收盘价

1.首先&#xff0c;导入必要的库&#xff1a; import torch import torch.nn as nn import numpy as np2.准备数据。需要准备好包含历史股票收盘价的一维时间序列数据。在这个例子中&#xff0c;我们将使用NumPy模拟一些示例数据 # 示例的股票收盘价时间序列数据 # 假设数据点…

Linux面试题汇总1

MySQL数据库 1、MySQL和Oracle的区别 1.Oracle是大型数据库&#xff0c;而MySQL是中小型数据库。但是MySQL是开源的&#xff0c;但是Oracle是收费的&#xff0c;而且比较贵。 2. Oracle的内存占有量非常大&#xff0c;而mysql非常小 3. MySQL支持主键自增长&#xff0c;指定主…

MySQL explain SQL分析工具详解与最佳实践

目录 一、explain工具介绍二、添加示例表和数据用于后续演示三、explain中的列3.1、id列3.2、select_type列3.3、table列3.4、partitions列3.5、type列NULLsystemconsteq_refrefrangeindexALL 3.6、possible_keys列3.7、key列3.8、key_len列3.9、ref列3.10、rows列3.11、filter…

《视觉 SLAM 十四讲》V2 第 11 讲 回环检测【消除累积误差】

待做: 习题整理 相关文献【新的综述】等 P283 文章目录 11.2 词袋 模型11.3.2 Code&#xff1a; 创建字典11.4.2 Code&#xff1a; 相似度 计算训练 自己的字典 报错 习题√ 题1√ 题2题3 DBoW3库题4题5 基于 词袋 的外观式 回环检测 SLAM主体(前端后端)&#xff1a; 估计相机…

图片处理后再保存为图片到文件夹中,文件夹下文件名不变改格式保存

首先读取图片&#xff1b; 然后处理&#xff0c;得到cv:Mat类型&#xff1b; 对cv:Mat类型图片写入文件夹&#xff0c;保存到指定路径。 像raw图等不能直接读取显示&#xff0c;需要先进行解码&#xff0c;转换为可以显示的图片。 下面举例读入本来可以显示的图。以下代码加…

哈弗猛龙实力登场,「方盒子猛改派对」掀起越野改装新热潮

9月22日-24日&#xff0c;哈弗猛龙“方盒子猛改派对”在北京751 D-PARK 火车头广场成功举办。活动现场盛况空前&#xff0c;不仅有官方展出的11台不同风格的猛改车型&#xff0c;更吸引了不同领域的博主大咖及越野达人前来参与活动。 与此同时&#xff0c;哈弗猛龙用户大定权益…

【EI会议征稿】第三届信号处理与通信技术国际学术会议(SPCT 2023)

第三届信号处理与通信技术国际学术会议&#xff08;SPCT 2023&#xff09; 2023 3rd International Conference on Signal Processing and Communication Technology 第三届信号处理与通信技术国际学术会议&#xff08;SPCT 2023&#xff09;将于2023年12月1-3日在长春召开。S…

【AIPOD案例操作教程】斜流风扇轮毂优化

AIPOD是由天洑软件自主研发的一款通用的智能优化设计软件&#xff0c;致力于解决能耗更少、成本更低、重量更轻、散热更好、速度更快等目标的工程设计寻优问题。针对工业设计领域的自动化程度低、数值模拟计算成本高等痛点&#xff0c;基于人工智能技术、自研先进的智能代理学习…

MySQL存储引擎以及InnoDB、MyISAM、Memory特点介绍

存储引擎介绍和基本使用 基本介绍&#xff1a; 存储引擎是数据库的核心&#xff0c;存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的&#xff0c;而不是基于库的&#xff0c;所以存储引擎也可被称为表类型。我们可以在创建表的时候&…

U盘植马之基于arduino的badusb实现及思考

引言 曾经有这么一段传说&#xff0c;在某次攻防演练时&#xff0c;某攻击队准备了一口袋U盘前往了目标单位的工作园区&#xff0c;在园区围墙外停下了脚步&#xff0c;然后开始不停扔U盘进去&#xff0c;最后发现有大量的“猎奇者”上线。 U盘植马是常见的近源渗透方式之一&am…

若依不分离+Thymeleaf select选中多个回显

项目中遇到的场景&#xff0c;亲测实用 表单添加时&#xff0c;select选中多个&#xff0c;编辑表单时&#xff0c;select多选回显&#xff0c;如图 代码&#xff1a; // 新增代码 <label class"col-sm-3 control-label">通道&#xff1a;</label><…

再学C++ | std::set 的原理

std::set 是C标准库中的容器之一&#xff0c;它基于红黑树实现。std::set 利用红黑树的特性来实现有序的插入、查找和删除操作&#xff0c;并且具有较好的平均和最坏情况下的时间复杂度。 当向 std::set 插入元素时&#xff0c;它会按照特定的比较函数&#xff08;bool less<…

软件可靠性基础

软件可靠性基础 软件可靠性基本概念串并联系统可靠性计算软件可靠性测试软件可靠性建模软件可靠性管理软件可靠性设计容错&#xff0c;检错的技术 选择题考基本概念&#xff08;MTBF&#xff09;&#xff0c;很少考 非重点 软件可靠性基本概念 这个章节中&#xff0c;第一个…

Leetcode算法题练习(一)

目录 一、前言 二、移动零 三、复写零 四、快乐数 五、电话号码的字母组合 六、字符串相加 一、前言 大家好&#xff0c;我是dbln&#xff0c;从本篇文章开始我就会记录我在练习算法题时的思路和想法。如果有错误&#xff0c;还请大家指出&#xff0c;帮助我进步。谢谢&…

2023-9-27 JZ55 二叉树的深度

题目链接&#xff1a;二叉树的深度 import java.util.*; /** public class TreeNode {int val 0;TreeNode left null;TreeNode right null;public TreeNode(int val) {this.val val;}} */ public class Solution {public int TreeDepth(TreeNode root) {if(root null) ret…