数据结构和算法(8):搜索树(二叉搜索树和AVL树)

查找

所谓的查找或搜索,指从一组数据对象中找出符合特定条件者,这是构建算法的一种基本而重要的操作。其中的数据对象,统一地表示和实现为 词条(entry) 的形式;不同词条之间,依照各自的 关键码(key) 彼此区分。

循关键码访问: 查找的过程与结果,仅仅取决于目标对象的关键码。

词条

template <typename K, typename V> struct Entry { //词条模板类K key; V value; //关键码、数值Entry ( K k = K(), V v = V() ) : key ( k ), value ( v ) {}; //默认构造函数Entry ( Entry<K, V> const& e ) : key ( e.key ), value ( e.value ) {}; //基于克隆的构造函数bool operator< ( Entry<K, V> const& e ) { return key < e.key; } //比较器:小于bool operator> ( Entry<K, V> const& e ) { return key > e.key; } //比较器:大于bool operator== ( Entry<K, V> const& e ) { return key == e.key; } //判等器:等于bool operator!= ( Entry<K, V> const& e ) { return key != e.key; } //判等器:不等于
}; //得益于比较器和判等器,从此往后,不必严格区分词条及其对应的关键码

词条对象拥有成员变量keyvalue。前者作为特征,是词条之间比对和比较的依据;后者为实际的数据。

任意词条之间可相互比较大小,是有序向量得以定义,以及二分查找算法赖以成立的基本前提

二叉搜索树

二叉搜索树中,处处都满足顺序性:任一 节点r的 左(右)子树中 ,所有节点( 若存在)均不大于(不小于)r
假定所有节点互不相等 ,回避边界情况:任一 节点r 的 左(右)子树中 ,所有 节点( 若 存在)均小于(大于)r
在实际应用中,对相等元素的禁止既不自然也不必要。

包含不同的 n n n 个节点可以构成的不同二叉搜索树有 C a t a l a n ( n ) = ( 2 n ) ! / [ ( n + 1 ) ! n ! ] Catalan(n) = (2n)!/[(n+1)!n!] Catalan(n)=(2n)!/[(n+1)!n!] 棵。

任何 一棵二叉树是二叉搜索树,当且仅当其中序遍历序列单调非降。
在这里插入图片描述

BST 模板类

#include "../BinTree/BinTree.h"	//引入BinTreetemplate <typename T> class BST : public BinTree<T>{ //由BinTree派生BST模板类
protected:BinNodePosi(T)_hot; //“命中”节点的父亲BinNodePosi(T) connect34 (//按照“3+4”结构,联接3个节点及四棵子树BinNodePosi(T)BinNodePosi(T)BinNodePosi(T),BinNodePosi(T)BinNodePosi(T)BinNodePosi(T)BinNodePosi(T));BinNodePosi(T) rotateAt ( BinNodePosi(T) x );//对x及其父亲、祖父做统一旋转调整
public: //基本接口︰以virtual修饰,强制要求所有派生类(BST变种)根据各自的规则对其重写virtual BinNodePosi(T) &search ( const T&e );//查找virtual BinNodePosi(T) insert ( const T&e );//插入virtual bool remove ( const T&e );//删除
};

BST:查找

采用了减而治之的思路与策略,其执行过程可描述为:从树根出发 ,逐步地缩小查找范围,直到发现目标(成功)或缩小至空树(失败)

查找过程中,一旦发现当前节点为NULL,即说明查找范围已经缩小至空,查找失败;否则,视关键码比较结果,向左(更小)或向右(更大)深入,或者报告成功(相等)。

template <typename T> //在以v为根的(AVL、SPLAY、rbTree等)BST子树中查找关键码e
static BinNodePosi(T) & searchIn ( BinNodePosi(T) & v, const T& e, BinNodePosi(T) & hot ) {if ( !v || ( e == v->data ) ) return v; //逑弻基:在节点v(或假想的通配节点)处命中hot = v; //一般情况:先记下当前节点,然后再return searchIn ( ( ( e < v->data ) ? v->lc : v->rc ), e, hot ); //深入一层,递归查找
} //返回时,返回值指向命中节点(或假想的通配哨兵),hot指向其父亲(退化时为初始值NULL)
template <typename T> BinNodePosi(T) & BST<T>::search ( const T& e ) //在BST中查找关键码e
{ return searchIn ( _root, e, _hot = NULL ); } //返回目标节点位置的引用,以便后续插入、删除操作

若查找成功,则searchIn()以及search()的返回值都将如图(a)所示,指向一个关键码为e且真实存在的节点;若查找失败,则返回值的数值虽然为NULL,但是它作为引用将如图(b)所示,指向最后一次试图转向的空节点。
在这里插入图片描述
在调用searchIn()算法之前,search()接口首先将内部变量_hot初始化为NULL,然后作为引用型参数hot传递给searchIn()。在整个查找的过程中,hot变量始终指向当前节点的父亲。因此在算法返回时,按照如上定义,_hot亦将统一指向“命中节点”的父亲。

_hot节点是否拥有另一个孩子,与查找成功与否无关。查找成功时,节点e可能是叶子,也可能是内部节点;查找失败时,假想的哨兵e等效于叶节点,但可能有兄弟。

复杂度
总体所需时间应线性正比于查找路径的长度,或最终返回节点的深度:最好为O(1),最坏为O(n).
若要控制单次查找在最坏情况下的运行时间,须从控制二叉搜索树的高度入手。

BST:插入

为了在二叉搜索树中插入一个节点,首先需要利用查找算法search()确定插入的位置及方向,然后才能将新节点作为叶子插入。

template <typename T> BinNodePosi(T) BST<T>::insert ( const T& e ) { //将关键码插入BST树中BinNodePosi(T) & x = search ( e ); if ( x ) return x; //确认目标不存在(留意对_hot癿设置)x = new BinNode<T> ( e, _hot ); //创建新节点x:以e为关键码,以_hot为父_size++; //更新全树规模updateHeightAbove ( x ); //更新x及其历代祖先的高度return x; //新插入的节点,必为叶子
} //无论e是否存在于原树中,返回时总有x->data == e

首先调用search()查找e。若返回位置x非空,则说明已有雷同节点,插入操作失败。否则,x必是_hot节点的某一空孩子,于是创建这个孩子并存入e。此后,更新全树的规模记录,并调用updateHeightAbove()更新x及其历代祖先的高度。
按照以上实现方式,无论插入操作成功与否,都会返回一个非空位置,且该处的节点与拟插入的节点相等。如此可以确保一致性,以简化后续的操作。

复杂度
节点插入操作所需的时间,主要消耗于对算法search()updateHeightAbove()的调用。后者与前者一样,在每一层次至多涉及一个节点,仅消耗O(1)时间,故其时间复杂度也同样取决于新节点的深度,在最坏情况下不超过全树的高度。

BST:删除

为从二叉搜索树中删除节点,首先也需要调用算法BST::search(),判断目标节点是否的确存在于树中。若存在,则需返回其位置,然后方能相应地具体实施删除操作。

单分支

在这里插入图片描述
若欲删除节点69,需首先通过search(69)定位待删除节点(69)。因该节点的右子树为空,故只需如图(b)所示,将其替换为左孩子(64),则拓扑意义上的节点删除即告完成。
当然,为保持二叉搜索树作为数据结构的完整性和一致性,还需更新全树的规模记录,释放被摘除的节点(69),并自下而上地逐个更新替代节点(64)历代祖先的高度。注意,首个需要更新高度的祖先(58),恰好由_hot指示。

双分支

设拟再删除二度节点36。如图(b)所示,首先调用BinNode::succ()算法,找到该节点的直接后继(40)。然后,只需如图©所示交换二者的数据项,则可将后继节点等效地视作待删除的目标节点。不难验证,该后继节点必无左孩子,从而相当于转化为此前相对简单的情况。于是最后可如图(d)所示,将新的目标节点(36)替换为其右孩子(46)。
请注意,在中途互换数据项之后,这一局部如图( c )所示曾经一度并不满足顺序性。但这并不要紧–不难验证,在按照上述方法完成整个删除操作之后,全树的顺序性必然又将恢复。
同样地,除了更新全树规模记录和释放被摘除节点,此时也要更新一系列祖先节点的高度。不难验证,此时首个需要更新高度的祖先(53),依然恰好由_hot指示。

remove() 删除关键码

1 template <typename T> bool BST<T>::remove ( const T& e ) { //从BST树中删除关键码e
2 BinNodePosi(T) & x = search ( e ); if ( !x ) return false; //确荏目标存在(留意_hot癿设置)
3 removeAt ( x, _hot ); _size--; //实施删除
4 updateHeightAbove ( _hot ); //更新_hot及其历代祖先的高度
5 return true;
6 } //删除成功与否,由返回值指示

首先调用search()查找e。若返回位置x为空,则说明树中不含目标节点,故删除操作随即可以失败返回。否则,调用removeAt()删除目标节点x。同样,此后还需更新全树的规模,并调用函数updateHeightAbove(_hot),更新被删除节点历代祖先的高度。

/*******************************************容*********容************************容***********
* BST节点删除算法︰删除位置x所指的节点(全局静态模板函数,适用于AVL、Splay、RedBlack等各种BST )
*目标x在此前经查找定位,并确认非NULL,故必删除成功;与searchIn不同,调用之前不必将hot置空
*返回值指向实际被删除节点的接替者,hot指向实际被删除节点的父亲——二者均有可能是NULL
*************************************牢*容**牢****水*容容容***水水水容容容*容容容容容容容容容容容容容容容容容字米林容容容容容**/
template <typename T>
static BinNodePosi(T) removeAt ( BinNodePosi(T) & x,BinNodePosi(T) & hot ) {BinNodePosi(T) w = x;	//实际被摘除的节点,初值同xBinNodePosi(T) succ = NULL;//实际被删除节点的接替者if (!HasLChild ( *x ) )	//若*x的左子树为空,则可succ = x = x->rc; //直接将*x替换为其右子树else if ( ! HasRChild (*x ) )	//若右子树为空,则可succ = x = x->lc; //对称地处理——注意∶此时succ != NULLelse {	//若左右子树均存在,则选择x的直接后继作为实际被摘除节点,为此需要w = w->succ(); //(在右子树中)找到*x的直接后继*wswap ( x->data,w->data );//交换*x和*w的数据元素BinNodePosi(T) u = w->parent;( ( u == x ) ? u->rc : u->lc ) = succ = w->rc;//隔离节点*w}hot = w->parent;//记录实际被删除节点的父亲if ( succ ) succ->parent = hot; //并将被删除节点的接替者与hot相联release ( w->data ); release ( w ); return succ;//释放被摘除节点,返回接替者
}

复杂度
删除操作所需的时间,主要消耗于对search()succ()updateHeightAbove()的调用。在树中的任一高度,它们至多消耗O(1)时间。故总体的渐进时间复杂度,亦不超过全树的高度。

平衡

search()insert()remove()等主要接口的运行时间,均线性正比于二叉搜索树的高度。
在最坏情况下,二叉搜索树可能彻底地退化为列表,此时的查找效率甚至会降至O(n),线性正比于数据集的规模。
因此,需要控制树高。

任意的n个互异关键码,都可以构成n!种全排列。若各排列作为输入序列的概率均等,则只要将它们各自所生成二叉搜索树的平均查找长度进行平均,即可在一定程度上反映二叉搜索树的平均查找性能。可以证明,在这一随机意义下,二叉搜索树的平均高度为O(logn)
假定n个互异节点同时给定,然后在遵守顺序性的前提下,随机确定它们之间的拓扑联接。如此,称二叉搜索树由这组节点“随机组成”。n个互异节点组成的二叉搜索树,总共可能有(2n)!/n!/(n + 1)!。若这些树出现的概率均等,则通过对其高度做平均可知 ,平均查找长度为 O ( n ) O(\sqrt n) O(n )

理想平衡与适度平衡

既然二叉搜索树的性能主要取决于高度,故在节点数目固定的前提下,应该尽可能降低高度。相应地,应尽可能使兄弟子树的高度彼此接近,即全树尽可能地平衡。
包含n个节点的二叉树,高度不可能小于 [ log ⁡ 2 n ] [\log_2n] [log2n]。若高度恰好为 [ log ⁡ 2 n ] [\log_2n] [log2n],则称作理想平衡树

在渐进意义下适当放松标准之后的平衡性,称作适度平衡

等价交换

若两颗二叉搜索树的中序遍历序列相同,则称它们彼此等价;反之亦然。
虽然等价二叉搜索树中各节点的垂直高度可能有所不同,但水平次序完全一致。

平衡二叉搜索树的适度平衡性,都是通过对树中每一局部增加某种限制条件来保证的。具有以下局部性:
1)经过单次动态修改操作后,至多只有O(1) 处局部不再满足限制条件
2)总可在O(log n)时间内,使这O(1)处局部(以至全树)重新满足限制条件。
意味着刚刚失去平衡的二叉搜索树,必然可以迅速转换为一棵等价的平衡二叉搜索树。等价二叉搜索树之间的上述转换过程,也称作等价变换

旋转调整

最基本的修复手段,就是通过围绕特定节点的旋转,实现等价前提下的局部拓扑调整。

在这里插入图片描述
zig 旋转(顺时针旋转):将X和v作为c的左子树、右孩子,Y和Z分别作为v的左、右子树。

在这里插入图片描述
zag旋转(逆时针旋转):将v和Z作为c的左孩子、右子树,X和Y分别作为v的左、右子树。

zig和zag旋转均属局部操作,均属于等价操作,仅涉及常数个节点及其之间的联接关系,故均可在常数时间内完成。就与树相关的指标而言,经一次 zig 或 zag 旋转之后,节点v的深度加一,节点c的深度减一;这一局部子树(乃至全树)的高度可能发生变化,但上、下幅度均不超过一层。

AVL树

通过合理设定适度平衡的标准,并借助等价变换,AVL树(AVL tree)可以实现近乎理想的平衡
在渐进意义下,AVL树可始终将其高度控制在O(logn)以内,从而保证每次查找、插入或删除操作,均可在O(logn)的时间内完成。

AVL树:重平衡

任一节点v平衡因子定义为“其左、右子树的高度差”,即:balFac(v) = height(lc(v)) - height(rc(v))
所谓AVL树,即平衡因子受限的二叉搜索树—其中各节点平衡因子的绝对值均不超过1

AVL 模板类

#include "../BST/BST.h”	//基于BST实现AVL树
template <typename T> class AVL : public BST<T>{ //由BST派生AVL树模板类
public:BinNodePosi(T) insert ( const T& e );	//插入(重写)bool remove ( const T& e );	//删除(重写)
//BST::search()等其余接口可直接沿用
};#define Balanced(x) ( stature( (x).lc ) == stature( (x).rc ) )//理想平衡条件
#define BalFac(x) ( stature( (x).lc ) - stature( (x).rc ) )//平衡因子
#define AvlBalanced(x)( ( -2<BalFac(x) ) && ( BalFac(x)<2 ) )//AVL平衡条件

在完全二叉树中各节点的平衡因子非01,故完全二叉树必是AVL树。

结论:高度为h的AVL树至少包含fib(h + 3) - 1个节点(数学归纳所得)
包含n个节点的AVL树的高度应为O(logn)

失衡与重平衡

AVL 树经过插入、删除等动态修改操作之后,节点的高度可能发生变化,以致于不再满足AVL树的条件。
因节点x的插入或删除而暂时失衡的节点,构成失衡节点集,记作UT(x)。请注意,若x为被摘除的节点,则UT(x)仅含单个节点;但若x为被引入的节点,则UT(x)可能包含多个节点。

AVL树:插入

新引入节点x后,UT(x)中的节点都是x的祖先,且高度不低于x的祖父。以下,将其中的最深者记作g(x)。在xg(x)之间的通路上,设pg(x)的孩子,vp的孩子。注意,既然g(x)不低于x的祖父,则p必是x的真祖先。
g(x)是因x的引入而失衡,则pv的高度均不会低于其各自的兄弟。

借助如下代码所示的宏tallerChild(),即可反过来由g(x)找到pv

/**************************************************************************************
*在左、右孩子中取更高者
*在AVL平衡调整前,借此确定重构方案
*************************************************************************************/
#define tallerChild(x)( \stature( (x)->lc ) > stature( (x)->rc ) ?(x)->lc : (/*左高*/	\stature( (x)->lc ) < stature( (x)->rc ) ?(x)->rc : (/*右高*/	\IsLChild(* (x)) ? (x)->lc : (x)->rc	/*等高︰与父亲x同侧者(zIg-zIg或zAg-zAg )优先*/	\
) \
) \

单旋

在这里插入图片描述
做逆时针旋转zag(g(x)),得到如图(b)所示的另一棵等价二叉搜索树。

双旋

在这里插入图片描述
节点插入后通过连续的两次旋转操作使AVL树重新平衡
此时,可先做顺时针旋转zig(p),得到如图(b)所示的一棵等价二叉搜索树。再做逆时针旋转zag(g(x)),得到如图( c )所示的另一棵等价二叉搜索树。

实现

template <typename T> BinNodePosi(T) AVL<T>::insert ( const T& e ) {	//将关键码e插入AVL树中BinNodePosi(T) & x = search ( e ); if ( x ) return x;//确认目标节点不存在BinNodePosi(T) xx = x = new BinNode<T> ( e,_hot ); _sizet+;//创建新节点x
//此时,x的父亲_hot若增高,则其祖父有可能失衡for ( BinNodePosi(T) g = _hot; g; g = g->parent ) { //从x之父出发向上,逐层检查各代祖先gif ( !AvlBalanced ( *g ) ) { //一旦发现g失衡,则(采用“3+4”算法)使之复衡,并将子树FromParentTo ( *g ) = rotateAt ( tallerChild ( tallerchild ( g ) ) );//重新接入原树break; //g复衡后,局部子树高度必然复原;其祖先亦必如此,故调整随即结束}else //否则( g依然平衡),只需简单地updateHeight ( g );//更新其高度(注意∶即便g未失衡,高度亦可能增加)}//至多只需一次调整﹔;若果真做过调整,则全树高度必然复原return xx;//返回新节点位置
}//无论e是否存在于原树中,总有AVL::insert(e)->data == e

无论单旋或双旋,经局部调整之后,不仅g(x)能够重获平衡,而且局部子树的高度也必将复原。这就意味着,g(x)以上所有祖先的平衡因子亦将统一地复原。换而言之,在AVL树中插入新节点后,仅需不超过两次旋转,即可使整树恢复平衡

算法首先按照二叉搜索树的常规算法,在O(logn)时间内插入新节点x。既然原树是平衡的,故至多检查O(logn)个节点即可确定g(x);如有必要,至多旋转两次,即可使局部乃至全树恢复平衡。
AVL树的节点插入操作可以在O(logn)时间内完成

AVL树:删除

与插入操作十分不同,在摘除节点x后,以及随后的调整过程中,失衡节点集UT(x)始终至多只含一个节点。而且若该节点g(x)存在,其高度必与失衡前相同。另外还有一点重要的差异是,g(x)有可能就是x的父亲。

与插入操作同理,从_hot节点出发沿parent指针上行,经过O(logn)时间即可确定g(x)位置。作为失衡节点的g(x),在不包含x的一侧,必有一个非空孩子p,且p的高度至少为1。于是,可按以下规则从p的两个孩子(其一可能为空)中选出节点v:若两个孩子不等高,则v取作其中的更高者;否则,优先取vp同向者(亦即,vp同为左孩子,或者同为右孩子)。

单旋

在这里插入图片描述
(a)所示,由于在T3中删除了节点而致使g(x)不再平衡,但p的平衡因子非负时,通过以g(x)为轴顺时针旋转一次即可恢复局部的平衡。平衡后的局部子树如图(b)所示。

双旋

在这里插入图片描述
节点删除后通过两次旋转恢复局部平衡。
g(x)失衡时若p的平衡因子为-1,则经过以p为轴的一次逆时针旋转之后(图(b)),即可转化为图(a)。再以g(x)为轴顺时针旋转,即可恢复局部平衡(图( c ))。

在删除节点之后,尽管也可通过单旋或双旋调整使局部子树恢复平衡,但复平衡之后,局部子树的高就全局而言,依然可能再次失衡。这种由于低层失衡节点的重平衡而致使其更高层祖先失衡的现象,称作“失衡传播
失衡传播的方向必然自底而上,而不致于影响到后代节点

实现

template <typename T> bool AVL<T>::remove ( const T& e ) { //从AVL树中删除关键码eBinNodePosi(T) & x = search ( e ); if ( !x ) return false;//确认目标存在(留意_hot的设置)removeAt ( x,_hot ); _size--;//先按BST规则删除之(此后,原节点之父_hot及其祖先均可能失衡)for ( BinNodePosi(T) g = _hot; g; g = g->parent ) { //从_hot出发向上,逐层检查各代祖先gif ( !AvlBalanced ( *g ) )//一旦发现g失衡,则(采用“3+4”算法)使之复衡,并将该子树联至g = FromParentTo ( *g ) = rotateAt ( tallerchild ( tallerChild ( g ) ) );//原父亲updateHeight ( g );//并更新其高度(注意:即便g未失衡,高度亦可能降低)}//可能需做O(logn)次调整——无论是否做过调整,全树高度均可能降低return true;//删除成功
}//若目标节点存在且被删除,返回true;否则返回false

较之插入操作,删除操作可能需在重平衡方面多花费一些时间。不过,既然需做重平衡的节点都是x的祖先,故重平衡过程累计只需不过O(logn)时间。
综合各方面的消耗,AVL树的节点删除操作总体的时间复杂度依然是O(logn)

AVL树:(3+4)-重构

同样需要从刚发生修改的位置x出发逆行而上,直至遇到最低的失衡节点g(x)。于是在g(x)更高一侧的子树内,其孩子节点p和孙子节点v必然存在,而且这一局部必然可以g(x)pv为界,分解为四棵子树,将它们按中序遍历次序重命名为 T0 至 T3。
若同样按照中序遍历次序,重新排列g(x)pv,并将其命名为abc,则这一局部的中序遍历序列应为:{T0 , a, T1 , b, T2 , c, T3}
将这三个节点与四棵子树重新“组装”起来,恰好即是一棵AVL树
在这里插入图片描述
这一理解涵盖了此前两节所有的单旋和双旋情况。相应的重构过程,仅涉及局部的三个节点及其四棵子树,故称作 “3 + 4”重构

/********************************************************************
*按照“3+4”结构联接3个节点及其四棵子树,返回重组之后的局部子树根节点位置(即b)
*子树根节点与上层节点之间的双向联接,均须由上层调用者完成
*可用于AVL和RedBlack的局部平衡调整
***********************************************************************/
template <typename T> BinNodePosi(T) BST<T>::connect34 (BinNodePosi(T) a,BinNodePosi(T) b, BinNodePosi(T) c,BinNodePosi(T)T0,BinNodePosi(T)T1,BinNodePosi(T) T2,BinNodePosi(T) T3
) {a->lc = T0; if ( T0 ) T0->parent = a;a->rc = T1; if ( T1 ) T1->parent = a; updateHeight ( a );c->lc = T2; if ( T2 ) T2->parent = c;c->rc = T3; if ( T3 ) T3->parent = c; updateHeight ( c );b->lc = a; a->parent = b;b->rc = c; c->parent = b; updateHeight ( b );return b;//该子树新的根节点
}

利用以上connect34()算法,即可视不同情况,按如下具体方法完成重平衡:

/**********************************************************************
* BST节点旋转变换统一算法( 3节点+4子树),返回调整之后局部子树根节点的位置
*注意:尽管子树根会正确指向上层节点(如果存在),但反向的联接须由上层函数完成
*************************************************************************/
template <typename T> BinNodePosi(T) BST<T>::rotateAt ( BinNodePosi(T)v ) { //v为非空孙辈节点BinNodePosi(T) p = v->parent;BinNodePosi(T) g = p->parent;//视v、 p和g相对位置分四种情况if ( IsLChild ( *p ) )/* zig */if ( IsLChild (*v ) ) {/* zig-zig*/p->parent = g->parent;//向上联接return connect34 ( v, p, g, v->lc, v->rc, p->rc, g->rc );}else { /* zig-zag */v->parent = g->parent;//向上联接return connect34 ( p, v,g,p->lc, v->lc, v->rc, g->rc );else/* zag */if ( IsRChild (*v ) ) {/* zag-zag */p->parent = g->parent;//向上联接return connect34 ( g, p, v, g->lc,p->lc,v->lc, v->rc );}else { /* zag-zig */v->parent = g->parent;//向上联接return connect34 ( g, v, p,g->lc, v->lc, v->rc,p->rc );}
}

复杂度也依然是O(1)

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

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

相关文章

Python:使用PySimpleGUI中sg.Input控件获取数据plot导致yticks错乱

sg.Input获取y轴数据代码 sg.Text(First Read:, font("Times New Roman", 9)),sg.Input(key-first_read-, size(25, 1), default_text0,0,0, justificationcenter, font("Times New Roman", 9), expand_xTrue), sg.Text(Second Read:, font("Times Ne…

Vue Grid Layout -️ 适用Vue.js的栅格布局系统,在vue3+上使用

文章目录 1、官网简介2、在vue3中使用1)、需要导入vue3支持的版本插件2)、在mian.js里引入&#xff1a;3)、在组件中使用 3、layout布局的计算逻辑4、 gridLayout 的属性 该栅格系统目前对 vue2 的支持是最好的&#xff0c;vue3 是需要用插件支持的&#xff0c;会在小节详细讲解…

求二维子数组的和(剖析)

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;本篇剖析一下二维子数组求和规则&#xff1a; &#x1f412;个人主页 &#x1f3c5;JavaSE系列专栏 &#x1f4d6;前言&#xff1a;本篇剖析一下二维子数组求和 规则&#xff1a; 这…

PyTorch C++ 前端:张量

本篇文章将尝试了解 PyTorch 组件的高级概述以及它们如何配合。 PyTorch 组件的高级概述 后端 PyTorch 后端是用 C++ 编写的,它提供 API 来访问高度优化的库,例如:用于高效矩阵运算的张量库、用于执行 GPU 运算的 CUDA 库以及用于梯度计算的自动微分等。 前端 可以使用…

蓝牙核心规范(V5.4)10.2-BLE 入门笔记之CIS篇

LE CIS 同步通信 同步通信提供了一种使用蓝牙LE在设备之间传输有时间限制的数据的方式。它提供了一个机制,允许多个接收器设备在不同的时间从相同的源接收数据,以同步它们对该数据的处理。LE AUDIO使用同步通信。 当使用同步通信时,数据具有有限的时间有效期,在到期时被认…

【强化学习】01—— 强化学习简介

文章目录 两种机器学习类型强化学习定义强化学习交互过程强化学习系统要素历史(History)状态(State)策略(Policy)奖励(Reward)价值函数(Value Function)模型(Model)迷宫例子 强化学习智能体分类参考 两种机器学习类型 监督学习/无监督学习/强化学习/机器学习之间的关系 预测 根…

坚鹏:浙江农商联合银行同业核心产品解读与差异化分析培训第7期

浙江农商联合银行同业核心产品解读与差异化分析培训第7期 1952年&#xff0c;浙江第一家农村信用社成立。2004年4月18日&#xff0c;浙江省农信联社成立&#xff0c;承担对全省农信社的管理、指导、协调和服务职能。2021年10月&#xff0c;经国务院批准同意、银保监会批复&…

虚拟DOM与diff算法

虚拟DOM与diff算法 snabbdom虚拟DOMdiff算法 snabbdom 是什么&#xff1a;snabbdom是著名的虚拟DOM库&#xff0c;是diff算法的鼻祖&#xff0c;Vue源码借鉴了snabbdom 虚拟DOM 是什么&#xff1a;本质上是存在内存里的 JavaScript 对象 作用&#xff1a;用来描述真实DOM的层…

Android毕业设计,基于Android 语音朗读书籍管理系统

视频演示&#xff1a; 基于Android 语音朗读书籍管理系统 基于 Android 的语音朗读书籍管理系统可以提供用户管理书籍、朗读书籍的功能。以下是一个简单的步骤和功能列表&#xff1a; 用户注册和登录功能&#xff1a; 用户可以注册新账号或使用现有账号登录系统。用户信息可以包…

php宝塔搭建实战Cloud网盘网站php源码

注意&#xff1a;因为微信最近又改了推送机制&#xff0c;经常有朋友说错过了之前的搭建教程文章&#xff0c;每次都要主动搜索才能搜到公众号。所以建议大家加个星标&#xff0c;就能第一时间收到推送。 大家好啊&#xff0c;欢迎来到web测评。本期给大家带来一套php开发的Clo…

Flutter flutter.minSdkVersion的实际文件位置

Flutter 项目的Android相关版本号配置&#xff1a; flutter.minSdkVersion 的版本号配置文件实际路径&#xff1a; …/flutter_sdk/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy Flutter版本号如下&#xff1a; bzbMacBook-Pro ccsmec % flutter --version …

不要动 WindowsApps 文件夹的权限以及更新 win10 版本

前言&#xff1a; 先简单说几句&#xff0c;本来打算开始写论文的&#xff0c;装个 mathtype 到 word 中&#xff0c;word 的安装路径在 WindowsApps 文件夹中&#xff0c;修改权限后导致 wsl 不能使用、微软自带的软件报错&#xff08;参数错误&#xff09;以及微软商店不能使…

C语言中的虚拟地址

虚拟地址 虚拟地址空间 对于操作系统而言&#xff0c;每个进程所得到的虚拟地址都在一个独立的固定的范围内&#xff0c;不会超过这个范围&#xff0c;我们把这个范围称为虚拟地址空间。所谓的虚拟地址空间本质就是一个地址范围&#xff0c;表示程序的寻址能力。对于32位系统…

直播视频处理过程

视频其实就是快速播放一连串连续的图片。 每一张图片&#xff0c;我们称为一帧。只要每秒钟帧的数据足够多&#xff0c;也即播放得足够快。比如每秒 30 帧&#xff0c;以人的眼睛的敏感程度&#xff0c;是看不出这是一张张独立的图片的&#xff0c;这就是我们常说的帧率&#…

如何用一行CSS实现10种现代布局

现代 CSS 布局使开发人员只需按几下键就可以编写十分有意义且强大的样式规则。上面的讨论和接下来的帖文研究了 10 种强大的 CSS 布局&#xff0c;它们实现了一些非凡的工作。 超级居中&#xff1a;place-items: center 对于第一个“单行”布局&#xff0c;让我们解决所有 CSS…

分析数组,结构体在反汇编中存储

本文会在IDA中分析数组&#xff0c;结构体在内存中的存储 目录 IDA分析数组存储 IDA分析结构体存储 传递参数的方式 IDA分析数组存储 测试代码如下&#xff1a; /************************************************************************/ /*Author : 玄都大…

已解决 Kotlin Error: Null can not be a value of a non-null type String

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

Intel汇编在VS下开发的环境配置

1. 创建一个C/C的空项目 2. 创建汇编源码文件, 就是C文件改后缀为asm 3. 在生成依赖项一栏中选择自定义 4. 选择masm 5. 在源文件上右击选择属性 6. 这么设置一下 7. 为了让代码看的更舒服一些, 添加一些高亮插件 8. 安装AsmHighligher和AsmDude插件(非必须), 其中前者主要是高…

signal(SIGPIPE, SIG_IGN)

linux查看signal常见信号。 [rootplatform:]# kill -l1) HUP2) INT3) QUIT4) ILL5) TRAP6) ABRT7) BUS8) FPE9) KILL 10) USR1 11) SEGV 12) USR2 13) PIPE 14) ALRM 15) TERM 16) STKFLT 17) CHLD 18) CONT 19) STOP 20) TSTP 21) TTIN 22) TTOU 23) URG 24) XCPU 25) XFSZ 2…

【动态规划刷题 16】最长等差数列 (有难度) 等差数列划分 II - 子序列

1027. 最长等差数列 https://leetcode.cn/problems/longest-arithmetic-subsequence/ 给你一个整数数组 nums&#xff0c;返回 nums 中最长等差子序列的长度。 回想一下&#xff0c;nums 的子序列是一个列表 nums[i1], nums[i2], …, nums[ik] &#xff0c;且 0 < i1 <…