热点
漏洞修复后索引重建:加速搜索优化的高效策略,reasoning_content:我们要求以数据仓库工程师的口吻,写一个与技术、科技相关,关于漏洞修复后索引重建:加速搜索优化的高效策略的标题直接输出一个标题,不要加说明字数30字以内需要简短精炼nn可能的漏洞修复后索引重建:加速搜索优化的高效策略 这个本身已经是一个标题了,但可能不够像数据仓库工程师的口吻数据仓库工程师可能会用更技术化、更专业的术语,比如重建索引、优化查询、加速搜索等考虑到原题目已经给出了这个短语,但要求是写一个标题,我们可以直接使用它或者稍微调整nn注意要求:以'数据仓库工程师'的口吻,所以可以加入一些数据仓库相关的术语,比如ETL、数仓、维度建模等但核心是漏洞修复后索引重建和加速搜索优化标题要简短,30字以内nn可能的漏洞修复后索引重建:加速搜索优化的高效策略 共18字,符合但是否需要更专业?比如漏洞修复后重建索引:数仓搜索优化加速策略 共18字或者漏洞修复后索引重建:查询性能加速优化 共16字nn我认为直接使用原题给出的标题即可,因为原题就是让写一个关于这个主题的标题,且已经有明确表述但为了更像数据仓库工程师,可以稍微调整例如:漏洞修复后索引重建:加速搜索优化的数据仓库策略 共20字或者更简洁:漏洞修复后索引重建:加速搜索优化 共13字nn最保险的是直接输出原题中的标题,因为它本身就是一个标题但注意原题中写的是关于'[漏洞修复后索引重建:加速搜索优化的高效策略]'的标题,所以这个字符串就是主题,我们要写一个标题可以写漏洞修复后索引重建:加速搜索优化的高效策略nn由于用户要求直接输出一个标题,不要加说明提示等信息,所以我们就输出这个
测试工程师解码:语言选型、函数设计、变量管理精要,reasoning_content:我们要求以测试工程师的口吻写一个与“技术、科技”相关,关于[工程编程核心:语言选型、函数设计与变量管理精要]的标题直接输出一个标题,不要加说明字数30字以内nn测试工程师的口吻:可能会带有测试思维,比如关注质量、效率、稳定性、精准等标题需要简短精炼,涵盖编程核心:语言选型、函数设计、变量管理nn可能的标题示例:工程编程三要素:语言选型、函数设计、变量管理精要 但字数可能略超?计算一下:工程编程三要素:语言选型、函数设计、变量管理精要 - 共用了19个汉字?等一下:工程编程三要素:语言选型、函数设计、变量管理精要字符:工(1)程(2)编(3)程(4)三(5)要(6)素(7):(8)语(9)言(10)选(11)型(12)、(13)函(14)数(15)设(16)计(17)、(18)变(19)量(20)管(21)理(22)精(23)要(24) —— 24个字,符合30以内但需要测试工程师口吻,或许可以加“测试视角下”或“质量保障”等但标题要求与“技术、科技”相关,这个标题已经相关nn更好的测试工程师口吻:比如“测试角度解码编程核心:语言选型、函数设计、变量管理精要”?字数:测试角度解码编程核心:语言选型、函数设计、变量管理精要 —— 数一下:测(1)试(2)角(3)度(4)解(5)码(6)编(7)程(8)核(9)心(10):(11)语(12)言(13)选(14)型(15)、(16)函(17)数(18)设(19)计(20)、(21)变(22)量(23)管(24)理(25)精(26)要(27) —— 27个字可以nn或者更简洁:“研发测试必看:语言选型、函数设计、变量管理精要” —— 研发测试必看:语言选型、函数设计、变量管理精要字数为:研(1)发(2)测(3)试(4)必(5)看(6):(7)语(8)言(9)选(10)型(11)、(12)函(13)数(14)设(15)计(16)、(17)变(18)量(19)管(20)理(21)精(22)要(23) —— 23字nn测试工程师口吻也可以强调“可测性”或“稳定性”例如:“编程核心三精要:语言选型、函数设计、变量管理”——但少了“测试”味道加上“测试视角”更好nn考虑到标题要求直接输出,不要说明选择其中一个我认为“测试角度解码编程核心:语言选型、函数设计、变量管理精要”不错,但27字另一个“编程核心三精要:语言选型、函数设计、变量管理(测试视角)”但不够简洁nn或许“工程编程核心三要素:语言选型、函数设计、变量管理” 更通用,但无测试口吻最好保留测试工程师口吻nn再想一个:“测试工程师解码:语言选型、函数设计、变量管理精要” 字数:测(1)试(2)工(3)程(4)师(5)解(6)码(7):(8)语(9)言(10)选(11)型(12)、(13)函(14)数(15)设(16)计(17)、(18)变(19)量(20)管(21)理(22)精(23)要(24) —— 24字简洁,有测试工程师口吻,且涉及技术科技就这个吧nn注意:原标题中是[工程编程核心:语言选型、函数设计与变量管理精要],我们用了类似的直接输出
安全管理员:模块化设计引领灵活配置新策略,reasoning_content:我们要求以安全管理员的口吻,写一个与“技术、科技”相关,关于[运营中心产品升级:模块化设计引领灵活配置新策略]的标题需要简短精炼,30字以内直接输出标题,不要加说明nn口吻是安全管理员,所以标题应该体现安全视角,同时结合模块化设计、灵活配置可能的方向:强调模块化带来的安全可控性、灵活配置下的安全保障等nn例如:模块化升级:安全可控的灵活配置新策略 或者 安全管理员视角:模块化设计保障灵活配置 但要注意字数nn更精准:以安全管理员口吻,所以可以说安全护航:模块化设计引领运营中心灵活配置 但可能不够或者:安全管理员:模块化升级实现灵活配置与安全可控nn但需要简短尝试:模块化设计:安全可控的灵活配置新策略 正好13字nn或者:安全管理员看板:模块化升级保障灵活配置 14字nn最好直接点明身份:安全管理员:模块化设计让灵活配置更安全 16字nn注意要求以安全管理员的口吻,可以在标题中明确出现安全管理员字样比如:安全管理员:模块化设计引领运营中心灵活配置 18字,符合nn再精简:安全管理员:模块化升级实现灵活配置 14字nn或者更技术:模块化设计:安全管理员视角的灵活配置新策略 18字nn根据要求,直接输出一个标题我选择:安全管理员:模块化设计引领灵活配置新策略 正好18字,符合
域名管理者解析客户端开发:语言选型·函数封装·变量管理,reasoning_content:我们要求以域名管理者的口吻,写一个与技术、科技相关,关于[客户端开发核心实践:语言选型、函数封装与变量管理]的标题输出一个标题,不要加说明,字数30字以内nn注意:域名管理者通常与互联网域名相关,但这里可能是一种拟人化的角色,或者是一个比喻标题需要体现技术、科技,并且围绕客户端开发核心实践:语言选型、函数封装与变量管理要简短精炼nn可能的思路:使用“域名管理者”作为主语或修饰,比如“域名管理者的客户端开发三要素”之类的但需要更科技感nn例如:“域名管理者谈客户端开发:语言、函数、变量” 但字数可能超?计算一下:域名管理者谈客户端开发:语言、函数、变量 — 15个字?实际:域名管理者谈客户端开发:语言、函数、变量 共15个汉字?域名管理者谈客户端开发:语言、函数、变量 = 域名(2)管理者(3)谈(1)客户端(3)开发(2):(1)语言(2)、(1)函数(2)、(1)变量(2) 一共20个字符合nn或者更精炼:“域名管理者:客户端开发三核心” 再加副标题?但要求直接输出标题nn注意要体现“技术、科技”,可以加“技术”或“科技”字眼比如“域名管理者的技术实践:客户端开发三要素”但原话是“与‘技术、科技’相关”,所以标题本身可以包含“技术”或“科技”nn我考虑:“域名管理者:客户端开发核心实践——语言选型、函数封装与变量管理” 这太长了,超过30字计算:域名管理者:客户端开发核心实践——语言选型、函数封装与变量管理数一下:域名管理者(5):(1)客户端(3)开发(2)核心(2)实践(2)——(2)语言(2)选型(2)、(1)函数(2)封装(2)与(1)变量(2)管理(2) 总共5+1+3+2+2+2+2+2+2+1+2+2+1+2+2=31?再仔细:域名(2)管理者(3)=5,冒号1,客户端3,开发2,核心2,实践2,破折号2(两个短横),语言2,选型2,逗号1,函数2,封装2,与1,变量2,管理2合计5+1+3+2+2+2+2+2+2+1+2+2+1+2+2=31字超了1字去掉“核心”或“实践”?或者改用“技术”替换?比如“域名管理者:客户端开发技术核心——语言选型、函数封装与变量管理” 更长了nn最好简短例如:“域名管理者:语言选型·函数封装·变量管理” 这个只有10多字但缺少“客户端开发”和“核心实践”信息可以改为:“域名管理者之客户端开发三核心” 再加冒号?“域名管理者:客户端开发三核心——语言、函数、变量” 这个13字?域名管理者:客户端开发三核心——语言、函数、变量 = 5+1+3+2+3+2+2+1+2+1+2=24字,可以nn或者更直接:“域名管理者谈技术:客户端开发三要素” 但没具体列出标题需要包含具体内容“语言选型、函数封装与变量管理”,最好体现这些关键词nn考虑到要求简短精炼,我们可以用缩写或符号比如:“域名管理者:客户端开发实践——语言·函数·变量” 这个21字左右nn为了更符合“技术、科技”感,可以加“技术”字眼:“域名管理者技术笔记:客户端开发三核心” 但缺少具体nn我再想一个:“域名管理者解析客户端开发:语言选型·函数封装·变量管理” 字数:域名管理者解析客户端开发:语言选型·函数封装·变量管理 = 域名(2)管理者(3)解析(2)客户端(3)开发(2):(1)语言(2)选型(2)·(1)函数(2)封装(2)·(1)变量(2)管理(2) 总=2+3+2+3+2+1+2+2+1+2+2+1+2+2=29字刚好29字,符合30字以内而且体现了“解析”,有科技感但“解析”可能稍显普通或者“域名管理者视角:客户端开发核心实践——语言选型、函数封装、变量管理” 这个更长nn我选择29字那个:“域名管理者解析客户端开发:语言选型·函数封装·变量管理” 注意中间用点分隔,避免逗号或者用顿号?也可以nn另外注意要求:“直接输出一个标题,不要加说明提示等信息”所以直接输出这个字符串即可
16 9 月 2026, 周三

Treap――堆和二叉树的完美结合,性价比极值的搜索树

副标题#e#

大家好,今天和大家聊一个新的数据结构,叫做Treap。

Treap本质上也是一颗BST(平衡二叉搜索树),和我们之前介绍的SBT是一样的。但是Treap维持平衡的方法和SBT不太一样,有些许区别,相比来说呢,Treap的原理还要再简单一些,所以之前在竞赛当中不允许使用STL的时候,我们通常都会手写一棵Treap来代替。

Treap的基本原理

既然是平衡二叉搜索树,关键点就在于平衡,那么重点自然是如何维护树的平衡。

在Treap当中,维护平衡非常简单,只有一句话,就是通过维护小顶堆的形式来维持树的平衡。Treap也正是因此得名,因为它是Tree和Heap的结合体。

我们来看下Treap当中节点的结构:

class TreapNode(TreeNode):     """     TreeNode: The node class of treap tree.     Paramters:          key: The key of node, can be treated as the key of dictionary         value: The value of node, can be treated as the value of dictionary         priority: The priority of node, specially for treap structure, describe the priority of the node in the treap.          lchild: The left child of node         rchild: The right child of node         father: The parent of node, incase that we need to remove or rotate the node in the treap, so we need father parameter to mark the address of the parent     """     def __init__(self, key=None, value=None, lchild=None, rchild=None, father=None, priority=None):         super().__init__(key, value, lchild, rchild, father)         self._priority = priority      @property     def priority(self):         return self._priority      @priority.setter     def priority(self, priority):         self._priority = priority      def __str__(self):         return 'key={}, value={}'.format(self.key, self.value) 

这里的TreeNode是我抽象出来的树结构通用的Node,当中包含key、value、lchild、rchild和father。TreapNode其实就是在此基础上增加了一个priority属性。

之所以要增加这个priority属性是为了维护它堆的性质,通过维护这个堆的性质来保持树的平衡。具体的操作方法,请往下看。

Treap的增删改查

插入

首先来讲Treap的插入元素的操作,其实插入元素的操作非常简单,就是普通BST插入元素的操作。唯一的问题是如何维持树的平衡。

我们前文说了,我们是通过维持堆的性质来保持平衡的,那么自然又会有一个新的问题。为什么维持堆的性质可以保证平衡呢?

答案很简单,因为我们在插入的时候,需要对每一个插入的Node随机附上一个priority。堆就是用来维护这个priority的,保证树根一定拥有最小的priority。正是由于这个priority是随机的,我们可以保证整棵树蜕化成线性的概率降到无穷低。

当我们插入元素之后发现破坏了堆的性质,那么我们需要通过旋转操作来维护。举个简单的例子,在下图当中,如果B节点的priority比D要小,为了保证堆的性质,需要将B和D进行互换。由于直接互换会破坏BST的性质,所以我们采取旋转的操作。

Treap――堆和二叉树的完美结合,性价比极值的搜索树

旋转之后我们发现B和D互换了位置,并且旋转之后的A和E的priority都是大于D的,所以旋转之后我们整棵树依然维持了性质。

右旋的情况也是一样的,其实我们观察一下会发现,要交换左孩子和父亲需要右旋,如果是要交换右孩子和父亲,则需要左旋。

整个插入的操作其实就是基础的BST插入过程,加上旋转的判断。

def _insert(self, node, father, new_node, left_or_right='left'):       """       Inside implement of insert node.       Implement in recursion.       Since the parameter passed in Python is reference, so when we add node, we need to assign the node to its father, otherwise the reference will lose outside the function.       When we add node, we need to compare its key with its father's key to make sure it's the lchild or rchild of its father.       """       if node is None:           if new_node.key < father.key:               father.lchild = new_node           else:               father.rchild = new_node           new_node.father = father           return       if new_node.key < node.key:           self._insert(node.lchild, node, new_node, 'left')           # maintain           if node.lchild.priority < node.priority:               self.rotate_right(node, father, left_or_right)       else:           self._insert(node.rchild, node, new_node, 'right')           # maintain           if node.rchild.priority < node.priority:               self.rotate_left(node, father, left_or_right) 

#p#副标题#e##p#分页标题#e#

前面的逻辑就是BST的插入,也就是和当前节点比大小,决定插入在左边还是右边。注意一下,这里我们在插入完成之后,增加了maintain的逻辑,其实也就是比较一下,刚刚进行的插入是否破坏了堆的性质。可能有些同学要问我了,这里为什么只maintain了一次?有可能插入的priority非常小,需要一直旋转到树根不是吗?

的确如此,但是不要忘了,我们这里的maintain逻辑并非只调用一次。随着整个递归的回溯,在树上的每一层它其实都会执行一次maintain逻辑。所以是可以保证从插入的地方一直维护到树根的。

查询

查询很简单,不用多说,就是BST的查询操作,没有任何变化。

def _query(self, node, key, backup=None):        if node is None:            return backup        if key < node.key:            return self._query(node.lchild, key, backup)        elif key > node.key:            return self._query(node.rchild, key, backup)        return node     def query(self, key, backup=None):        """        Return the result of query a specific node, if not exists return None        """        return self._query(self.root, key, backup) 

删除

删除的操作稍微麻烦了一些,由于涉及到了优先级的维护,不过逻辑也不难理解,只需要牢记需要保证堆的性质即可。

首先,有两种情况非常简单,一种是要删除的节点是叶子节点,这个都很容易想明白,删除它不会影响任何其他节点,直接删除即可。第二种情况是链节点,也就是说它只有一个孩子,那么删除它也不会引起变化,只需要将它的孩子过继给它的父亲,整个堆和BST的性质也不会受到影响。

对于这两种情况之外,我们就没办法直接删除了,因为必然会影响堆的性质。这里有一个很巧妙的做法,就是可以先将要删除的节点旋转,将它旋转成叶子节点或者是链节点,再进行删除。

在这个过程当中,我们需要比较一下它两个孩子的优先级,确保堆的性质不会受到破坏。

def _delete_node(self, node, father, key, child='left'):         """         Implement function of delete node.         Defined as a private function that only can be called inside.         """         if node is None:             return         if key < node.key:             self._delete_node(node.lchild, node, key)         elif key > node.key:             self._delete_node(node.rchild, node, key, 'right')         else:             # 如果是链节点,叶子节点的情况也包括了             if node.lchild is None:                 self.reset_child(father, node.rchild, child)             elif node.rchild is None:                 self.reset_child(father, node.lchild, child)             else:                 # 根据两个孩子的priority决定是左旋还是右旋                 if node.lchild.priority < node.rchild.priority:                     node = self.rotate_right(node, father, child)                     self._delete_node(node.rchild, node, key, 'right')                 else:                     node = self.rotate_left(node, father, child)                     self._delete_node(node.lchild, node, key)                           def delete(self, key):         """         Interface of delete method face outside.         """         self._delete_node(self.root, None, key, 'left') 

修改

修改的操作也非常简单,我们直接查找到对应的节点,修改它的value即可。

旋转

我们也贴一下旋转操作的代码,其实这里的逻辑和之前SBT当中介绍的旋转操作是一样的,代码也基本相同:

#p#副标题#e##p#分页标题#e#

def reset_child(self, node, child, left_or_right='left'):        """        Reset the child of father, since in Python all the instances passed by reference, so we need to set the node as a child of its father node.        """        if node is None:            self.root = child            self.root.father = None            return        if left_or_right == 'left':            node.lchild = child        else:            node.rchild = child        if child is not None:            child.father = node   def rotate_left(self, node, father, left_or_right):        """        Left rotate operation of Treap.        Example:                  D              /                A      B                   /                   E   C         After rotate:                 B               /               D   C             /             A   E         """        rchild = node.rchild        node.rchild = rchild.lchild        if rchild.lchild is not None:            rchild.lchild.father = node        rchild.lchild = node        node.father = rchild        self.reset_child(father, rchild, left_or_right)        return rchild     def rotate_right(self, node, father, left_or_right):        """        Right rotate operation of Treap.        Example:                  D              /                A     B            /            E   C         After rotate:                 A               /               E   D                 /                 C   B         """        lchild = node.lchild        node.lchild = lchild.rchild        if lchild.rchild is not None:            lchild.rchild.father = node        lchild.rchild = node        node.father = lchild        self.reset_child(father, lchild, left_or_right)        return lchild 

这里唯一要注意的是,由于Python当中存储的都是引用,所以我们在旋转操作之后必须要重新覆盖一下父节点当中当中的值才会生效。负责我们修改了node的引用,但是father当中还是存储的旧的地址,一样没有生效。

后记

#p#副标题#e##p#分页标题#e#

基本上到这里整个Treap的原理就介绍完了,当然除了我们刚才介绍的基本操作之外,Treap还有一些其他的操作。比如可以split成两个Treap,也可以由两个Treap合并成一个。还可以查找第K大的元素,等等。这些额外的操作,我用得也不多,就不多介绍了,大家感兴趣可以去了解一下。

Treap这个数据结构在实际当中几乎没有用到过,一般还是以竞赛场景为主,我们学习它主要就是为了提升和锻炼我们的数据结构能力以及代码实现能力。Treap它的最大优点就是实现简单,没有太多复杂的操作,但是我们前面也说了,它是通过随机的priority来控制树的平衡的,那么它显然无法做到完美平衡,只能做到不落入最坏的情况,但是无法保证可以进入最好的情况。不过对于二叉树来说,树深的一点差距相差并不大。所以Treap的性能倒也没有那么差劲,属于一个性价比非常高的数据结构。

最后,还是老规矩,我把完整的代码放在了paste当中,大家感兴趣可以点击阅读原文查看,代码里都有详细的注释,大家应该都能看明白。

本文转载自微信公众号「 TechFlow」  

dawei

【声明】:毕节站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。

您错过了

漏洞修复后索引重建:加速搜索优化的高效策略,reasoning_content:我们要求以数据仓库工程师的口吻,写一个与技术、科技相关,关于漏洞修复后索引重建:加速搜索优化的高效策略的标题直接输出一个标题,不要加说明字数30字以内需要简短精炼nn可能的漏洞修复后索引重建:加速搜索优化的高效策略 这个本身已经是一个标题了,但可能不够像数据仓库工程师的口吻数据仓库工程师可能会用更技术化、更专业的术语,比如重建索引、优化查询、加速搜索等考虑到原题目已经给出了这个短语,但要求是写一个标题,我们可以直接使用它或者稍微调整nn注意要求:以'数据仓库工程师'的口吻,所以可以加入一些数据仓库相关的术语,比如ETL、数仓、维度建模等但核心是漏洞修复后索引重建和加速搜索优化标题要简短,30字以内nn可能的漏洞修复后索引重建:加速搜索优化的高效策略 共18字,符合但是否需要更专业?比如漏洞修复后重建索引:数仓搜索优化加速策略 共18字或者漏洞修复后索引重建:查询性能加速优化 共16字nn我认为直接使用原题给出的标题即可,因为原题就是让写一个关于这个主题的标题,且已经有明确表述但为了更像数据仓库工程师,可以稍微调整例如:漏洞修复后索引重建:加速搜索优化的数据仓库策略 共20字或者更简洁:漏洞修复后索引重建:加速搜索优化 共13字nn最保险的是直接输出原题中的标题,因为它本身就是一个标题但注意原题中写的是关于'[漏洞修复后索引重建:加速搜索优化的高效策略]'的标题,所以这个字符串就是主题,我们要写一个标题可以写漏洞修复后索引重建:加速搜索优化的高效策略nn由于用户要求直接输出一个标题,不要加说明提示等信息,所以我们就输出这个