当前位置: 首页 > news >正文

Clickhouse数据去重

1. Hive去重

先以两个简单的sql启发我们的话题

select count(distinct id)from order_combine;select count(id) 
from (select id from order_combine group by id
) t;

从执行日志当中我们可以看到二者的差异(只摘取关键部分)

# distinct
+ Stage-Stage-1:Map: 396 Reduce: 1 ...Time taken: 200.192 seconds#group byStage-Stage-1: Map: 396 Reduce: 457 ...Time taken: 87.191 seconds

二者关键的区别就在于使用distinct会将id都shuffle到一个reducer里面,当数据量大了之后,不可避免的就会出现数据倾斜。

group by在reducer阶段会将数据分布到多台机器上执行,在处理大数据时在执行性能上自然相比于distinct提高很多。

其实一般面试中,这种程度的回答会考量到MapReduce阶段数据的分区分组以及流向是否真正理解。

但是我们今天的主题主要是去重,写到这里就此打住很显然是不行的。

让我们换个角度思考一下

针对一个常用的order表去重就需要消耗87s的时间,如果我们在日常工作中经常需要针对order表进行数据分析,可想而知,这样的速度是难以忍受的。

老兵也常常就遇到这样的问题,两年前公司就开始慢慢的用Spark引擎替换Hive,在速度上加快了很多,但是随着业务的发展,Spark的实时查询能力还是显得有些力不从心,在去重方面的性能自然不必多说。

能不能找到一个方案让去重更快?

说到这里,ClickHouse就呼之欲出了。接下来会详细介绍ClickHouse的主要原理以及他是如何去重的。

2. Clickhouse

ClickHouse 是 Yandex(俄罗斯最大的搜索引擎)开源的一个用于实时数据分析的基于列存储的数据库。

ClickHouse的性能超过了目前市场上可比的面向列的 DBMS,每秒钟每台服务器每秒处理数亿十亿多行和数十千兆字节的数据。

2.1 MergeTree存储结构

说得这么神乎其神,很多人可能会有疑问,ClickHouse真的有这么强大吗?

要了解Clickhouse的强大,首先要提的绝对是合并树MergeTree引擎,因为Clickhouse的核心原理就是依赖于它,而所有的其他引擎都是基于它来实现的。

我将以MergeTree为切入点揭开ClickHouse神秘面纱。

从图中可以看出,一张数据表的完整物理结构分为3个层级,依次是数据表目录分区目录及各分区下具体的数据文件。接下来逐一介绍它们的作用。

  • partition:分区目录,属于相同分区的数据最终会被合并到同一个分区目录,而不同分区的数据,永远不会被合并到一起。
  • checksums.txt:校验文件,使用二进制格式存储。它保存了各类文件(primary.idx、count.txt等)的size大小及size的哈希值,用于快速校验文件的完整性和正确性。
  • columns.txt:列信息文件,使用明文格式存储。用于存储此数据文件下的列字段信息。
  • count.txt:计数文件,使用明文格式存储。用于记录当前数据分区目录下数据的总行数。所以,利用Clickhouse我们经常算的分区总行数又何必反复扫描计数。
  • primary.idx:一级索引文件,使用二进制格式存储。用于存放稀疏索引。借助稀疏索引有效减少数据扫描范围,加快查询速度。
  • [Column].bin:数据文件,使用压缩格式存储,默认为LZ4压缩格式。由于MergeTree采用列式存储,所以每一个列都拥有独立的.bin数据文件,并以列字段名称命名(例如impdate.bin,age.bin等)。
  • [Column].mrk:列字段标记文件,使用二进制格式存储。用以保存.bin数据的偏移量。因此,MergeTree就是通过标记文件建立了primary.idx稀疏索引与.bin数据文件之间的映射关系。
  • [Column].mrk2:如果使用了自使用大小的索引间隔,则标记文件会以.mrk2命名。
  • partition.dat与minmax_[Column].id:如果使用了分区键,例如Parition By ImpTime,则会额外生产partition.datminmax索引文件,它们均用二进制格式存储。partition.dat用于保存当前分区下分区表达式最终生成的值;而minmax索引用于记录当前分区下分区字段对应原始数据的最小和最大值。在这些分区索引的作用下,进行数据查询能够快速跳过不必要的数据分区目录,从而减少最终需要扫描的数据范围。
  • skp_idx_[Column].idxskp_idx_[Column].mrk:如果在建表时声明了二级索引就会额外生成相应的二级索引与标记文件,它们同样也使用二进制存储。显然,最终目的与一级索引相同,都是为了进一步减少所需扫描的数据范围。
反问一个问题,为什么Clickhouse的引擎叫MergeTree呢?

搞懂了它的数据分区相信你们得到答案。我将详细介绍一下Clickhouse是如何进行数据分区的。

2.2 MergeTree数据分区

如果进入数据表所在的磁盘目录后,会发现MergeTree分区目录的完整物理名称并不是只有id而已,在id之后还跟着一串奇怪的数字,例如202112_1_1_0。这些数字代码什么呢?

对于MergeTree而言,最核心的特点就是分区目录的合并过程,想要搞清楚过程,其实从分区目录的命名中就能解读出它的合并逻辑。如下一个完整分区目录的命名公式:

PartitionID_MinBlockNum_MaxBlockNum_Level

我这里画了一张图来便于大家理解,并在下面附加对它的解释。

2.2.1 命名规则

  • PartitionID: 分区ID**
  • MinBlockNumMaxBlockNum:顾名思义,最小数据块编号和最大数据块编号。这里的BlockNum是一个整型的自增长编号,从1开始,每新创建一个分区目录时,计数n就会累加1。对于一个新的分区目录而言,MinBlockNum与MaxBlockNum取值一样,等同于n,例如202112_1_1_0、202112_2_2_0以此类推。
  • Level:合并的层级,可以理解为某个分区被合并过的次数,或者这个分区的年龄,数值越高,年龄越大。

2.2.2 分区目录的合并过程

MergeTree的分区目录当然和传统意义上的其他数据库有所不同。其中最大的差别是在其他数据库的设计中,追加数据后目录自身不会变化,只是在相同分区目录中追加新的数据文件,比如hive存储目录就是如此。

而MergeTree不同,随着每一批数据的写入,MergeTree都会生成一批新的分区目录。即便不同批次写入的数据属于相同分区,也会生成不同的分区目录。也就是说,对于同一个分区而言,也会存在多个分区目录的情况。

在之后某个时刻,Clickhouse会通过后台任务再将数据相同分区的多个目录合并成一个新的目录。已经存在的旧分区不会立即删除,而是在之后的某个时刻被后台任务删除(默认8分钟)。新目录的合并方式遵循以下规则:

  • MinBlockNum:取同一分区内所有目录中最小的MinBlockNum值。
  • MaxBlockNum:取同一分区内所有目录中最大的MaxBlockNum值。
  • Level:取同一分区内最大的Level值并加一。

2.2.3 合并目录名称的变化过程

照例我这里先上一张图让大家直观的看一下合并目录名称的变化过程

按照上图所示,测试表按日期字段格式分区,即PARTITION BY toYTYYYMM(EvenT-Time)。假设在T0时刻,首先分3批插入如上3条数据,按照目录规则,上述代码会创建3个分区目录。分区目录的名称由PartitionIDMinBlockNumMaxBlockNumLevel组成,3个分区目录ID依次是202111,202111和202112。

而对于每个新建的分区目录而言,它们的MinBlock- NumMaxBlockNum取值相同,均来源于表内全局自增长的BlockNum。

  • BlockNum初始为1,每次新建目录后累计加1。所以,3个分区目录的MinBlockNum和MaxBlockNum依次为0_01_12_2。最后就是Level层级,每个新建的分区目录初始Level都是0。
  • 假设在T1时刻,MergeTree的合并动作开始了,属于同一分区的目录就会发生合并,这个时候202111_1_1_02021_2_2_0如上图所示完成合并动作之后形成一个新的分区202111_1_2_1
  • 其中MinBlockNum取同一分区内所有目录中最小MinBlockNum值所以是1。MaxBlockNum取同一分区内所有目录最大的MaxBlockNum值,也就是2。此时Level取同一分区内最大的Level值加1,即1。而后的合并就是重复上述的过程而已。

至此,大家已经知道了分区ID、目录命名和目录合并的相关规则。相信开始对Clickhouse有一些感觉了。

2.3 稀疏索引

如果仅仅只是分区的优化,详细大家还是会对Clickhouse持怀疑态度。其实相比于传统基于HDFS的OLAP引擎,clickhouse不仅有基于分区的过滤,还有基于列级别的稀疏索引,这样在进行条件查询的时候可以过滤到很多不需要扫描的块,这样对提升查询速度是很有帮助的。

2.3.1 稀疏索引与稠密索引

简单来说,在稠密索引中的每一行索引标记都会对应到一行具体的数据记录。而在稀疏索引中,每一行索引标记对应的是一段数据,而不是一行。

如果把MergeTree比作一本书,那么稀疏索引就好比这本书的一级章节目录。

稀疏索引的优势是显而易见的,他仅需要少量的索引标记就可以记录大量的区间位置信息,且数据量越大优势约为明显。以默认的索引粒度(8192)为例,只需要12208行索引就可以为1亿行的数据记录提供索引。由于稀疏索引占用空间小,索引primary.idx内的索引数据常驻内存,取用速度自然极快。

我们以一张图看下索引数据写入primary.idx又是如何进行的

上图不难理解,我们把AccountId作为我们的主键字段,Clickhouse每间隔8192行数据会取一次AccountId作为索引值,索引数据最终被写入primary.idx文件进行保存。

如上的编号0、编号1、编号2是按照字段顺序紧密排序在一起的,比如1112345689,因此我们可以看出MergeTree对于稀疏索引的存储还是非常紧凑的。

不仅此处,clickhouse中很多数据结构都被设计的非常紧凑。比如其使用位读取代替专门的标志位状态码,不浪费哪怕一个字段的空间。以小见大,这也是为什么Clickhouse为何性能如此出众的深层原因之一。

说到这里,索引的查询过程就是我们不可回避的问题了。

了解索引的查询过程,首先需要了解什么是MarkRange

MarkRange在Clickhouse是用于定义标记区间的对象。通过先前的介绍可知,MergeTree按照自定义的间隔粒度,将一段完整的数据划分成了多个小的间隔数据段,一个具体的数据段即是一个MarkRange。MarkRange与索引编号对应,使用startend两个属性表示其区间范围。

整个索引的查询过程可以大致分为3个步骤:

  • 生成查询条件区间:首先,将查询条件转换成条件区间。如:wehre AccountId = 'U003'会转换成['U003','U003']。
  • 递归交集判断:以递归的方式,依次对MarkRange的数值区间与条件区间做交集判断。
    • 如果不存在交集,直接剪枝
    • 如果存在交集,判断MarkRange步长是否大于8(end - start) ,如果大于则进一步拆分成8个子区间(merge_tree_coarse_index_granularity指定,默认值为8),并重复此规则,继续递归交集判断。
    • 如果存在交集,且MarkRange不可再分解(步长小于8),则记录MarkRange并返回。
    • 合并MarkRange区间:将最终匹配的MarkRange聚在一起,合并他们的范围。

Clickhouse同样支持二级索引,目的也是与一级索引一样,帮助查询时减少数据扫描的范围。

当然,Clickhouse在设计上还做了很多优化来提高我们的数据处理能力以及数据存储能力。篇幅有限,后面再对它们进行详细展开,现在可以大致了解一下。

2.4 其他特性

1)CPP

clickhouse是CPP编写的,代码中大量使用了CPP最新的特性来对查询进行加速。优秀的代码,对性能的极致追求。

2)列存储

最小化数据扫描范围。更好的进行数据压缩。

3)压缩数据块

有效减少数据大小,降低存储空间并加速数据传输效率。在具体读取某一列数据时,首先需要将压缩数据加载到内存并解压,这样才能进行后续的数据处理。通过压缩数据块,可以在不读取整个数据文件的情况下将读取的粒度降低到压缩数据级别,从而进一步缩小了数据读取的范围。

4)SIMD

Clickhouse利用CPU的SIMD指令来实现向量化执行。SIMD的全称是Single Instruction Multiple Data,即用单条指令操作多条数据。现代计算机系统概念中,它是通过数据并行以提高性能的一种实现方式 ( 其他的还有指令级并行和线程级并行 )。

原理: 在CPU寄存器层面实现数据的并行操作。

简单来说就是一个指令能够同时处理多个数据。所以利用CPU向量化执行的特性,对于程序的性能提升意义非凡。

数据分片与分布式查询Clickhouse作为一款在线分析处理查询的大数据引擎,自然少不了集群部署数据分片的能力。从而利用分布式查询横向扩展提高数据查询速度。

光说不练假把式,我们最后就来看看基于Clickhouse做去重究竟有多快~

3. ClickHouse去重优化

下面是我针对12亿数据做的测试,帮大家直观的感受下Clickhouse的速度。(涉及保密信息会被msk-手动狗头)

3.1 数据查询(非去重)

select count(1) num 
from a 
whereimp_date >= '2021-12-01' and imp_date <= '2021-12-10'

3.2 数据查询(去重)

下面看看Clickhouse普通去重的写法:

select count(distinct dt_accountid ) user_num 
from awhere imp_date >= '2021-12-01' and imp_date <= '2021-12-10'

从两张图中的红框部分我们能得到两个信息:

  • 基于12亿数据做count聚合查询总数可以秒级别内得到响应,这与我前面提高的数据分区目录存储和索引密不可分。
  • 基于12亿数据数据去重,响应时间在7s左右

看到这样的测试结果,相信很多之前没有接触过Clickhouse的人会大吃一惊。因为它相比于sparkhive简直是太快了。其实它在去重上的能力还远不止于此。

3.3 数据查询(Bitmap去重)

下面我们使用Clihouse的Bitmap机制进行去重:

select groupBitmap(toUInt64OrZero (dt_accountid)) user_num 
from a
where imp_date >= '2021-12-01' and imp_date <= '2021-12-10' 

从图中我们会惊奇的发现,换了一个写法,Clickhouse基于Bitmap去重竟然将去重速度又提高了一个量级。

这就是我们经常在处理大数据去重为了加快速度和节省内存时做的终极优化: 利用Bitmap。

为什么用count(distinct)跟groupBitmap两个函数差异如此之大呢我们有必要看下源码:

// count(distinct)
// HashSetTable
void merge(const Self & rhs){if (!this->hasZero() && rhs.hasZero()){this->setHasZero();++this->m_size;}for (size_t i = 0; i < rhs.grower.bufSize(); ++i)if (!rhs.buf[i].isZero(*this))this->insert(rhs.buf[i].getValue());}
// groupBitmap
// RoaringBitmapWithSmallSet
void merge(const RoaringBitmapWithSmallSet & r1){if (r1.isLarge()){if (isSmall())toLarge();*rb |= *r1.rb;}else{for (const auto & x : r1.small)add(x.getValue());}}
Bitmap原理
  • 从上面的代码块我们可以很清楚地看到: count(distinct)用到的uniqExact是使用一个hash表来合并聚合数据,利用hash表的唯一特性来精确去重,在10亿级别的for循环中十分耗时(gdb打印了rhs.grower.bufSize()的值为20亿,耗时2分钟左右)。
  • RoaringBitmapWithSmallSetmerge在高基维场景下只做了一次或运算,耗时基本可以忽略。大家如果还不了解HashSetTable和RoaringBitmap可以自行补上这些知识,这里暂且不展开来讲。

所以我们发现即便在Clickhouse这种场景下,不深入了解他的基本原理,我们对于同一个业务不同的实现性能也会差别极大。

相关文章:

Clickhouse数据去重

1. Hive去重 先以两个简单的sql启发我们的话题 select count(distinct id)from order_combine;select count(id) from (select id from order_combine group by id ) t;从执行日志当中我们可以看到二者的差异&#xff08;只摘取关键部分&#xff09; # distinctStage-Stage…...

精讲typescript从入门到入土

前言 TypeScript是一种由Microsoft开发的编程语言&#xff0c;它是JavaScript的超集&#xff0c;意味着它可以编写与JavaScript完全兼容的代码&#xff0c;并且可以扩展其功能。TypeScript的主要目标是提供类型安全性和更好的可维护性&#xff0c;使得开发大型复杂应用程序更加…...

typora-beta-0.11.18版本又提示过期的解决方案

很实用&#xff0c;所以照搬一下下面的作者的回答&#xff0c;省得以后再找~~~ 知乎的作者来源如下&#xff1a; 作者&#xff1a;吴小皓 链接&#xff1a;typora打开报错&#xff1a;This beta version of Typora is expired, please download and install a newer version …...

WebUI自动化测试框架搭建(二十)-优化:测试对象无法连接或出现异常时,请更新本文作为测试对象

(二十)-测试对象无法连接或出现异常时,请更新本文作为测试对象 1 测试对象说明2 源代码下载3 学生管理系统配置安装3.1 解压打开3.2 安装依赖3.3 安装mysql数据库3.4 修改项目数据库配置3.4 安装数据库连接工具Navicat3.5 导入数据库脚本4 运行学生管理系统5 系统查看1 测试…...

【FATE联邦学习】standalone版Fateboard修改配置

背景&做法 很多其他程序&#xff08;比如vscode的code server&#xff09;也会使用这个 127 0 0 1:8080 socket进行通信&#xff0c;这样就没办法远程用vscode去开发了&#xff0c;所以需要修改下Fateboard的socket配置。官方文档中也给出了如何修改配置 The default data…...

分享一个应急响应web日志:access.log文件分析小工具

有时做应急响应的时候&#xff0c;需要提取web日志如access.log日志文件来分析系统遭受攻击的具体原因&#xff0c;由于开源的工具并不是很好用&#xff0c;所以自己用Python3写了一个简单的日志分析工具。先介绍一下access.log日志access.log日志文件记录了所有目标对Web服务器…...

windows注册服务非常实用

方式一&#xff1a;使用Windows自带的sc命令 1、使用管理员权限打开cmd窗口 2、注册服务命令&#xff1a; sc create 服务名 binpath 程序所在路径 type own start auto displayname 服务显示名称 sc create redis binpath d:\tools\redis-x64-5.0.14\redis-server.exe type …...

蓝桥dfs专题

1、dfs 路径打印 小明冒充X星球的骑士&#xff0c;进入了一个奇怪的城堡。 城堡里边什么都没有&#xff0c;只有方形石头铺成的地面。 假设城堡地面是 n x n 个方格。【如图1.png】所示。 按习俗&#xff0c;骑士要从西北角走到东南角。 可以横向或纵向移动&#xff0c;但不能…...

[ 网络 ] 应用层协议——HTTPS协议原理

目录 1.HTTPS是什么 2.加密技术 2.1什么是加密 2.2为什么要加密 2.3加密处理防止被窃听 3.常见的加密方式 对称加密 非对称加密 4.数据摘要&&数据指纹 5.数字签名 6.HTTPS的工作过程探究 方案1——只是用对称加密 方案2——只进行非对称加密 方案3——双方…...

http协议如何操作

、HTTP协议&#xff08;超文本传输协议&#xff09; 1.1、http协议是一个基于“请求与响应”模式的、无状态的应用层协议。 http协议采用URL作为定位网络资源的标识。 1.2、URL格式 http://host[:port][path] host:合法的Internet主机域名或IP地址 port&#xff1a;端口号…...

ESP Insights 系列文章

ESP Insights 系列 #1 | 远程查看设备信息&#xff0c;快速解决固件问题 ESP Insights 是一个可远程查看设备固件运行状态和日志的平台&#xff0c;能够帮助开发人员快速定位并解决固件问题。 ESP Insights 系列 #2 | 新增功能 最新版本优化了用户界面、修复了系统稳定性&am…...

如何提高爬虫工作效率

单进程单线程爬取目标网站太过缓慢&#xff0c;这个只是针对新手来说非常友好&#xff0c;只适合爬取小规模项目&#xff0c;如果遇到大型项目就不得不考虑多线程、线程池、进程池以及协程等问题。那么我们该如何提升工作效率降低成本&#xff1f; 学习之前首先要对线程&#…...

React结合Drag API实现拖拽示例详解

Drag API React中的Drag API是用于实现拖放功能的API。该API由React DnD库提供&#xff0c;可用于实现拖放操作&#xff0c;例如将元素从一个位置拖动到另一个位置。 React DnD库提供了两种Drag API&#xff1a;基于HTML5的拖放API和自定义实现的拖放API。 基于HTML5的拖放AP…...

【华为OD机试java、python、c++、jsNode】新学校选址(100%通过+复盘思路)

代码请进行一定修改后使用,本代码保证100%通过率。本文章提供java、python、c++、jsNode四种代码。复盘思路在文章的最后 题目描述 为了解新学期学生暴涨的问题,小乐村要建立所新学校, 考虑到学生上学安全问题,需要所有学生家到学校的距离最短。 假设学校和所有学生家都走在…...

Nacos配置中心,分组配置参考,以及python、go、bash客户端连接获取

Nacos使用说明 nacos官方网站 https://nacos.io/zh-cn/docs/v2/what-is-nacos.html 1、基本配置说明 nacosIP地址&#xff1a;http://xxxxx:8848/nacos/ 服务管理端登录账号&#xff1a;nacos XXX Java最小配置&#xff0c;其他客户端可参考&#xff0c;配置可对应到第三章…...

node-red中有关用户登录,鉴权,权限控制的流程解析

前言 默认地,node-red编辑器可以被任何访问的用户操作,包括修改节点,流数据,重新部署流。 这种默认的部署方式只适用于运行在可靠的网络中。下面我就给大家介绍一下,在公网上部署node-red后,如何对其进行安全加固和权限验证。 主要分为三部分 开启https权限保护编辑器和…...

MQTT协议-使用CONNECT报文连接阿里云

使用网络调试助手发送CONNECT报文连接阿里云 参考&#xff1a;https://blog.csdn.net/daniaoxp/article/details/103039296 在前面文章介绍了如何组装CONNECT报文&#xff0c;以及如何计算剩余长度 CONNECT报文&#xff1a;https://blog.csdn.net/weixin_46251230/article/d…...

每日学术速递3.8

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Unleashing Text-to-Image Diffusion Models for Visual Perception 标题&#xff1a;释放用于视觉感知的文本到图像扩散模型 作者&#xff1a;Wenliang Zhao, Yongming Rao, Zuya…...

测牛学堂:软件测试之接口测试理论基础总结

接口概念 接口&#xff1a;系统之间数据交互的通道。 这个系统&#xff0c;可以是外部和内部&#xff0c;也可以是两个内部系统之间的通道。 比如我们前端的登录信息&#xff0c;主要是用户名和密码&#xff0c;它通过接口传递给后端&#xff0c;后端校验以后&#xff0c;把结…...

基于土壤数据与机器学习算法的农作物推荐算法代码实现

1.摘要 近年来&#xff0c;机器学习方法在农业领域的应用取得巨大成功&#xff0c;广泛应用于科 学施肥、产量预测和经济效益预估等领域。根据土壤信息进行数据挖掘&#xff0c;并在此基础上提出区域性作物的种植建议&#xff0c;不仅可以促进农作物生长从而带来经济效益&#…...

python中html必备基础知识

<!DOCTYPE html>此标签表示这是一个html文件<heml lang"en">向搜索引擎表示该页面是html语言&#xff0c;并且语言为英文网站&#xff0c;其"lang"的意思就是“language”&#xff0c;语言的意思&#xff0c;而“en”即表示English<head>…...

【专项训练】前言:刻意练习,不断的过遍数才是王道

如何精通一个领域? 拆分知识点刻意练习:每个区域的基础动作分解训练和反复刻意练习反馈(主动反馈、被动反馈、及时反馈)任何知识体系都是一颗树,一定要梳理成思维导图,明确知识与知识之间的关系! 通过7-8周密集训练,练好基本功,彻底攻克LeetCode! 严格执行五毒神掌!…...

【Leetcode】反转链表 合并链表 相交链表 链表的回文结构

目录 一.【Leetcode206】反转链表 1.链接 2.题目再现 3.解法A&#xff1a;三指针法 二.【Leetcode21】合并两个有序链表 1.链接 2.题目再现 3.三指针尾插法 三.【Leetcode160】相交链表 1.链接 2.题目再现 3.解法 四.链表的回文结构 1.链接 2.题目再现 3.解法 一.…...

M1、M2芯片Mac安装虚拟机

目录前言一、安装二、网络设置三、连接SSH客户端前言 一直想着给M1 Mac上安装虚拟机&#xff0c;奈何PD收费&#xff0c;找的破解也不稳定&#xff0c;安装上镜像就起不来。 注&#xff1a;挂长久的分享莫名其妙被封&#xff0c;需要安装包请私信我。 一、安装 虚拟机选择&a…...

算法刷题-只出现一次的数字、输出每天是应该学习还是休息还是锻炼、将有序数组转换为二叉搜索树

只出现一次的数字&#xff08;位运算、数组&#xff09; 给定一个非空整数数组&#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 说明&#xff1a; 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗&…...

详解专利对学生、老师和企业员工、创业者、积分落户、地方补助的好处

大家好,我是英子老师。作为一名知识产权专家,深耕于专利行业十余年,具有丰富的专利工作经验:曾在大型专利代理机构从事专利代理工作、专利质检工作(抽查代理机构的专利代理人的撰写质量并评分);之后在知名上市企业、行业龙头企业担任高级专利工程师的职位,主要工作内容…...

Python图像处理:频域滤波降噪和图像增强

图像处理已经成为我们日常生活中不可或缺的一部分&#xff0c;涉及到社交媒体和医学成像等各个领域。通过数码相机或卫星照片和医学扫描等其他来源获得的图像可能需要预处理以消除或增强噪声。频域滤波是一种可行的解决方案&#xff0c;它可以在增强图像锐化的同时消除噪声。 …...

智能手机高端“酣战”,转机在何方?

经过多年发展&#xff0c;如今全世界有七成手机由中国制造&#xff0c;但在利润最丰厚的高端市场&#xff0c;国产厂商在很长一段时间之内都是形单影只&#xff0c;曾经一度跻身高端的“华为”因为封禁成了“绝唱”。 华为“失声”高端之后&#xff0c;其他一众国产厂商或主动…...

K8s pod 动态弹性扩缩容 HPA

一、概述Horizontal Pod Autoscaler&#xff08;HPA&#xff0c;Pod水平自动伸缩&#xff09;&#xff0c;根据平均 CPU 利用率、平均内存利用率或你指定的任何其他自定义指标自动调整 Deployment 、ReplicaSet 或 StatefulSet 或其他类似资源&#xff0c;实现部署的自动扩展和…...

C++中的类简要介绍

文章目录前言一、什么是类什么是对象1.类的概述2.对象的概述二、如何创建使用类三、class和struct创建类时的区别1.访问级别2.继承方式总结前言 本篇文章讲给大家介绍一个C中重要的概念&#xff0c;了解了这个概念大家就明白了为什么C会叫做面向对象编程了。 一、什么是类什么…...

dw网站结构图怎么做/seo黑帽培训

模块式pyth1.on组织代码的基本方式一个python脚本可以单独运行&#xff0c;也可以导入另一个脚本中运行&#xff0c;当脚本被导入运行时&#xff0c;我们将其称为模块(module)所有的点p为文件都可以作为一个模块导入模块名与脚本的文件名相同&#xff0c;例如我们编写了一个名为…...

正规购物网站建设/成都网站建设公司排名

JVM中的堆一般分为三大部分&#xff1a;新生代、老年代、永久代&#xff0c;其大致的占比如下&#xff1a; 一、新生代 新生代主要用来存放新生的对象。一般占据堆空间的1/3。在新生代中&#xff0c;保存着大量的刚刚创建的对象&#xff0c;但是大部分的对象都是朝生夕死&#…...

如何建设网站效果好/打广告在哪里打最有效

实验环境为matlab2013b1、首先编写一个mseq.m文件,内容为:function[mseq]m_sequence(fbconnection)nlength(fbconnection);N2^n-1;register[zeros(1,n-1) 1]; %移位寄存器的初始状态mseq(1)register(n); %m序列的第一个输出码元for i2:Nnewregister(1)mod(sum(fbconnec…...

鹤壁市住房和城乡建设局网站/seo文章推广

2019独角兽企业重金招聘Python工程师标准>>> 想整个Python最新版学习学习&#xff0c;但官网死活打不开... 安装环境&#xff1a;Fedora15 sudo yum install gcc gcc-c autoconf automake 来安装编译环境 一般Linux 都自带有Python,Fedora 15 貌似是2.7.1版本的 解压…...

做外贸学网站/武汉seo学徒

敏捷领导力队长 我最近阅读了Petri Kainulainen关于团队成员之间共享领导力的文章 &#xff0c;在这方面&#xff0c;我持同样的观点&#xff0c;因为敏捷方法强调了“有动力的人&#xff0c;应该值得信任”的重要性。 虽然团队领导者可以被视为旧的僵化组织结构的回忆&#x…...

北京公司网站制作哪家专业/北京百度推广代理

Java基础编程题&#xff08;13&#xff09;【程序 13 根据条件求数字】 **题目&#xff1a;**一个整数&#xff0c;它加上 100 后是一个完全平方数&#xff0c;再加上168 又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; **程序分析&#xff1a;**在 10 万以内判断…...