首页 文章 精选 留言 我的

精选列表

搜索[字节级],共10000篇文章
优秀的个人博客,低调大师

消息称阿里通义大模型前核心员工加入字节跳动,被诉违反竞业协议

据《科创板日报》报道,有消息称阿里通义大模型前员工周畅违反竞业协议,阿里方面已起诉递交劳动争议仲裁申请书。据接近通义的业内人士对《科创板日报》记者表示:情况属实。 公开资料显示,周畅 2017 年博士毕业于北京大学计算机软件与理论专业,随后加入阿里巴巴,花名“钟煌”,是阿里通义千问大模型的技术负责人,曾和团队推出一系列语言模型、多模态模型。 在阿里巴巴工作期间,周畅带领团队设计并实现了超大规模的多模态预训练模型 M6,在参数数量和低碳训练模式上取得了突破。 M6 模型是 2021 年 3 月阿里巴巴与清华大学联合发布的业界最大中文多模态预训练 AI 模型,参数规模高达 1000 亿,是多模态预训练领域史上最大的模型。 今年 7 月曾有知情人士表示,周畅是通义实验室算法团队的核心技术骨干之一,属于正常离职。通义大模型的研发和开源工作还在进行中,目前通义实验室负责人为阿里云 CTO 周靖人。 询问AI

优秀的个人博客,低调大师

字节面试数据结构与算法:B+树的删除和插入,不够详细你打我

之前在讲解mysql底层算法架构的时候,我提到了一个点:树,这个大学的时候让我们只有恨没有爱的课程中的一员,但是,最近的一段时间里,算法和数据结构成为面试过程汇总弄个地考虑重点,之前的时候图解过红黑树,也以mysql为例,将B+树的相关原理进行了讲解,想要重新回顾一下的朋友,可以去公众号:Java架构师联盟查看 今天我们来看一下B+树的应用,图解哦,开始之前先来回归一下一些基础的东西 使用场景 文件系统和数据库系统中常用的B/B+ 树,他通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时间,提高存储的空间局部性从而减少IO操作。他广泛用于文件系统及数据库中,如: Windows:HPFS 文件系统 Mac:HFS,HFS+ 文件系统 Linux:ResiserFS,XFS,Ext3FS,JFS 文件系统 数据库:ORACLE,MYSQL,SQLSERVER 等中 B+树特点 (1)根结点只有1个,分支数量范围[2,m]。 (2)除根以外的非叶子结点,每个结点包含分支数范围[[m/2],m],其中[m/2]表示取大于m/2的最小整数。 (3)所有非叶子节点的关键字数目等于它的分支数量。 (4)所有叶子节点都在同一层,且关键字数目范围是[[m/2],m],其中[m/2]表示取大于m/2的最小整数。 (5)所有非叶子节点的关键字可以看成是索引部分,这些索引等于其子树(根结点)中的最大(或最小)关键字。例如一个非叶子节点包含以下信息: (n,AO,KO,A1,K1..."n,An),其中Ki为关键字,Ai为指向子树根结点的指针,n表示关键字个数。即Ai所指字数中的关键字均小于或等于Ki,而Ai+1所指的关键字均大于Ki (i=1,2,......, n) 。 (6)叶子节点包含全部关键字的信息(非叶子节点只包含索引),且叶子结点中的所有关键字依照大小顺序链接(所以一个B+树通常有两个头指针,一个是指向根节点的root,另一个是指向最小关键字的sqt)。 不知道这些东西能不能理解啊,理解不了的话可能你大学需要回炉重造了,哈哈哈哈 接下来,开始正事吧,我以一个B+树进行串联讲解,先创建一棵树 主要是B+树的应用 插入数据 第一种情况:向B+树中插入数据9 首先查找9应插入的叶节点(最左下角的那一个)插入发现没有破坏B+树的性质,完毕 第二种情况:向B+树中插入20 1、首先查找20应插入的叶节点(第二个叶子节点),插入,但是这个时候改变了结构 2、发现第二个叶子节点已经破坏了B+树的性质,则把之分解成[20 21],[3744]两个,并把21往父节点移 3、发现父节点也破坏了B+树的性质,则把之再分解成[15 21].[4459]两个,并把21往其父节点移 第三种情况:向B+树中插入100 1、首先查找100应插入的叶节点(最后一个节点),插入,同样是影响了结构 2、修改其所有父辈节点的键值为10O(只有插入比当前树的最大数大的数时要做此步) 3、重复上述的方法开始拆分节点,直到完成 代码演示 PtrBpNode BpAllocateNode(BoolType IsLeaf){ int i; PtrBpNode NewNode = (PtrBpNode)malloc(sizeof(BpNode)); NewNode->Num = 0; if(True == IsLeaf){ NewNode->IsLeaf = True; } else{ NewNode->IsLeaf = False; } NewNode->Key = (PtrElementType)malloc(sizeof(ElementType) * (MinDegree * 2 - 1)); NewNode->Child =(PtrBpNode*)malloc(sizeof(PtrBpNode) * MinDegree * 2); for(i = 0; i < MinDegree * 2; i++){ NewNode->Child[i] = NULL; } NewNode->Next = NULL; return NewNode; } void BpInsert(PtrBp T, ElementType Val){ PtrBpNode NewNode; if(MinDegree * 2 - 1 == T->Root->Num){ NewNode = BpAllocateNode(False); NewNode->Child[0] = T->Root; T->Root = NewNode; BpSpilitNode(NewNode, 0); } BpInsertNonFull(T->Root, Val); } void BpInsertNonFull(PtrBpNode CurrentNode, ElementType Val){ int Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val); if(True == CurrentNode->IsLeaf){ ShiftKey(CurrentNode->Key, True, Index, CurrentNode->Num - 1); CurrentNode->Key[Index] = Val; (CurrentNode->Num)++; } else{ if(MinDegree * 2 - 1 == CurrentNode->Child[Index]->Num){ BpSpilitNode(CurrentNode, Index); //Caution if(CurrentNode->Key[Index] < Val){ Index++; } } BpInsertNonFull(CurrentNode->Child[Index], Val); } } void BpSpilitNode(PtrBpNode SpilitNodeP, int ChildIndex){ int i; PtrBpNode NewNode, SubNode = SpilitNodeP->Child[ChildIndex]; if(True == SubNode->IsLeaf){ NewNode = BpAllocateNode(True); for(i = 0; i < MinDegree - 1; i++){ NewNode->Key[i] = SubNode->Key[i + MinDegree]; } NewNode->Num = MinDegree - 1; SubNode->Num = MinDegree; NewNode->Next = SubNode->Next; SubNode->Next = NewNode; } else{ NewNode = BpAllocateNode(False); for(i = 0; i < MinDegree - 1; i++){ NewNode->Key[i] = SubNode->Key[i + MinDegree]; } for(i = 0; i < MinDegree; i++){ NewNode->Child[i] = SubNode->Child[i + MinDegree]; } NewNode->Num = SubNode->Num = MinDegree - 1; } ShiftKey(SpilitNodeP->Key, True, ChildIndex, SpilitNodeP->Num - 1); ShiftChild(SpilitNodeP->Child, True, ChildIndex + 1, SpilitNodeP->Num); SpilitNodeP->Key[ChildIndex] = SubNode->Key[MinDegree - 1]; SpilitNodeP->Child[ChildIndex + 1] = NewNode; (SpilitNodeP->Num)++; } 删除 第一种情况:向B+树中删除91 首先找到91所在叶节点(最后一个节点),删除之,和插入一样,他并没有改变数据结构,所以可以直接进行插入和删除 第二种情况:向B+树中删除97 首先找到97所在叶节点(最后一个节点),删除之,但是相比删除91多了一步操作,需要修改该节点的父辈的键字为91(ps:只有删除树中最大数时要做此步) 第三种情况:向B+树中删除51 首先找到51所在节点(第三个节点),删除之 破坏了B+树的性质,从该节点的兄弟节点(左边或右边)借节点44,并修改相应键值,判断没有破坏B+树,完毕 第四种情况:向B+树中删除59 首先找到59所在叶节点(第三个节点),删除之 破坏B+树性质,尝试借节点,无效(因为左兄弟节点被借也会破坏B+树性质)合并第二第三叶节点并调整键值 第五种情况:向B+树中删除63 首先找到63所在叶节点(第四个节点),删除之 合并第四五叶节点并调整键值 但是在删除后可以发现一个问题,在第二层的第二个节点不满足B+树性质,所以需要从第二层的第一个节点借59,并调整键值 代码演示 void BpDelete(PtrBp T, PtrBpNode CurrentNode, ElementType Val){ int Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val); PtrBpNode Precursor, SubNode, Successor; if(Index < CurrentNode->Num && Val == CurrentNode->Key[Index]){ if(True == CurrentNode->IsLeaf){ ShiftKey(CurrentNode->Key, False, Index + 1, CurrentNode->Num - 1); (CurrentNode->Num)--; return; } else{ Precursor = CurrentNode->Child[Index]; Successor = CurrentNode->Child[Index + 1]; if(Precursor->Num >= MinDegree){ if(True == SubNode->IsLeaf){ CurrentNode->Key[Index] = Precursor->Key[SubNode->Num - 2]; } else{ CurrentNode->Key[Index] = Precursor->Key[SubNode->Num - 1]; } BpDelete(T, Precursor, Precursor->Key[SubNode->Num - 1]); } else if(Successor->Num >= MinDegree){ CurrentNode->Key[Index] = Successor->Key[0]; if(True == SubNode->IsLeaf){ SubNode->Key[SubNode->Num - 1] = CurrentNode->Key[Index]; } BpDelete(T, Successor, Successor->Key[0]); } else{ BpMerge(T, CurrentNode, Index, Index + 1); BpDelete(T, Precursor, Val); } } } else{ if(True == CurrentNode->IsLeaf){ return; } else{ if(Index > 0){ Precursor = CurrentNode->Child[Index - 1]; } SubNode = CurrentNode->Child[Index]; if(Index < CurrentNode->Num){ Successor = CurrentNode->Child[Index + 1]; } if(SubNode->Num >= MinDegree){ BpDelete(T, SubNode, Val); } else{ if(Index > 0 && Precursor->Num >= MinDegree){ ShiftKey(SubNode->Key, True, 0, SubNode->Num - 1); SubNode->Key[0] = CurrentNode->Key[Index - 1]; if(True == SubNode->IsLeaf){ CurrentNode->Key[Index - 1] = Precursor->Key[Precursor->Num - 2]; } else{ CurrentNode->Key[Index - 1] = Precursor->Key[Precursor->Num - 1]; ShiftChild(SubNode->Child, True, 0, SubNode->Num); SubNode->Child[0] = Precursor->Child[Precursor->Num]; } (SubNode->Num)++; (Precursor->Num)--; BpDelete(T, SubNode, Val); } else if(Index < CurrentNode->Num && Successor->Num >= MinDegree){ if(True == SubNode->IsLeaf){ SubNode->Key[SubNode->Num] = Successor->Key[0]; } else{ SubNode->Key[SubNode->Num] = CurrentNode->Key[Index]; } CurrentNode->Key[Index] = Successor->Key[0]; SubNode->Child[SubNode->Num + 1] = Successor->Child[0]; (SubNode->Num)++; ShiftKey(Successor->Key, False, 1, Successor->Num - 1); ShiftChild(Successor->Child, False, 1, Successor->Num); (Successor->Num)--; BpDelete(T, SubNode, Val); } else{ if(Index > 0){ BpMerge(T, CurrentNode, Index - 1, Index); BpDelete(T, Precursor, Val); } else{ BpMerge(T, CurrentNode, Index, Index + 1); BpDelete(T, SubNode, Val); } } } } } } void BpMerge(PtrBp T, PtrBpNode CurrentNode, int LeftIndex, int RightIndex){ int i; PtrBpNode LeftNode = CurrentNode->Child[LeftIndex]; PtrBpNode RightNode = CurrentNode->Child[RightIndex]; if(True == LeftNode->IsLeaf){ for(i = 0; i < MinDegree - 1; i++){ LeftNode->Key[i + MinDegree - 1] = RightNode->Key[i]; } LeftNode->Num = MinDegree * 2 - 2; LeftNode->Next = RightNode->Next; } else{ for(i = 0; i < MinDegree - 1; i++){ LeftNode->Key[i + MinDegree] = RightNode->Key[i]; } for(i = 0; i < MinDegree; i++){ LeftNode->Key[i + MinDegree] = RightNode->Key[i]; } LeftNode->Key[MinDegree - 1] = CurrentNode->Key[LeftIndex]; LeftNode->Num = MinDegree * 2 - 1; } ShiftKey(CurrentNode->Key, False, LeftIndex + 1, CurrentNode->Num - 1); ShiftChild(CurrentNode->Child, False, RightIndex + 1, CurrentNode->Num); (CurrentNode->Num)--; if(CurrentNode == T->Root && 0 == CurrentNode->Num){ T->Root = LeftNode; } }

优秀的个人博客,低调大师

字节跳动大佬用最通俗方法讲明白了红黑树算法

不知道大家有没有看今天的那个面试官被害视频,我那神奇的同事不知道那个脑回路突然被打通了,在办公室问了一句:是不是面试官问了一下红黑树,把面试的人给问毛了啊,都问,我不会这个还问,然后暴起下手呀!!然后办公室掀起了一阵讨论热潮 树,这个大学时代数据结构与算法的重点之一,当时真的也是头疼了好久,但是其实现在想想,害,没啥变化,依旧头疼,看下面这张图,树包含的内容 而树的内容又以二叉树作为重点,先来复习一下基础知识 BST树: 二叉搜索树(Binary Search Tree,简写BST),又称为二叉排序树,属于树的一种,通过二叉树将数据组织起来,树的每个节点都包含了健值key、数据值data、左子节点指针、右子节点指针。其中健值key是最核心的部分,它的值决定了树的组织形状;数据值data是该节点对应的数据,有些场景可以忽略,举个例子,key为身份证号而data为人名,通过身份证号找人名;左子节点指针指向左子节点;右子节点指针指向右子节点。 特点: 左右子树也分别是二叉搜索树。 左子树的所有节点key值都小于它的根节点的key值。右子树的所有节点key值都大于他的根节点的key值。二叉搜索树可以为一棵空树。 一般来说,树中的每个节点的 key值都不相等,但根据需要也可以将相同的key值插入树中 AVL树: AVL树,也称平衡二叉搜索树,AVL是其发明者姓名简写。AVL树属于树的一种,而且它也是一棵二叉搜索树,不同的是他通过一定机制能保证二叉搜索树的平衡,平衡的二叉搜索树的查询效率更高。 特点: AVL树是一棵二叉搜索树。 AVL树的左右子节点也是AVL树。 AVL树拥有二叉搜索树的所有基本特点。 每个节点的左右子节点的高度之差的绝对值最多为1,即平衡因子为范围为[-1,1]。 还有一个就是今天我们讨论的重点:红黑树,我们就来看一下 红黑(Red-black)树 是一种自平衡二叉查找树,1972年由Rudolf Bayer发明,它与AVL树类似,都在插入和删除操作时能通过旋转操作保持二叉查找树的平衡,以便能获得高效的查找性能。它可以在O(logn)时间内做查找,插入和删除等操作。红黑树是2-3-4树的一种等同,但有些红黑树设定只能左边是红树,这种情况就是2-3树的一种等同了。对于AVL树来说,红黑树牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树。 特点: 节点是红色或黑色。根节点是黑色。 每个叶节点(NIL节点)是黑色的。 每个红色节点的两个子节点都为黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 最长路径不超过最短路径的2倍 上图就是一颗简单的红黑树。其中Nil为叶子结点,并且它是黑色的。(H和M的黑色叶子节点没画出来) 介绍到此,为了后面讲解不至于混淆,我们还需要来约定下红黑树一些结点的叫法,如图2所示。 我们把正在处理(遍历)的结点叫做当前结点,如图2中的D,它的父亲叫做父结点,它的父亲的另外一个子结点叫做兄弟结点,父亲的父亲叫做祖父结点。 3.基本操作 前面讲到红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色。 左旋:逆时针旋转,父节点被自己的右孩子取代,而自己成为自己的左孩子(注:左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树挪了。) 右旋:顺时针旋转,父节点被左孩子取代,而自己成为自己的右孩子(注:右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树挪了。) 变色:结点的颜色由红变黑或由黑变红。 所以不难看出,无论什么旋转操作都是局部的改变了树的的节点,但要保持红黑树的性质,结点不能乱挪,还得靠变色了。怎么变?具体情景有不同变法,来看一下 至此,变色的任务完成,按照步骤,满足规则,一步步进行,错过一步可能就理解不了了 好了,理论的东西,到这里基本就讲解完了,红黑树难吗?说实话我个人觉得,这破玩意太为难人了 4.红黑树的部分实现 红-黑树的节点实现 红-黑树是对二叉搜索树的改进,所以其节点与二叉搜索树是差不多的,只不过在它基础上增加了一个boolean型变量来表示节点的颜色,具体看RBNode类: public class RBNode<T extends Comparable<T>>{ boolean color; //颜色 T key; //关键字(键值) RBNode<T> left; //左子节点 RBNode<T> right; //右子节点 RBNode<T> parent; //父节点 public RBNode(T key, boolean color, RBNode<T> parent, RBNode<T> left, RBNode<T> right) { this.key = key; this.color = color; this.parent = parent; this.left = left; this.right = right; } public T getKey() { return key; } public String toString() { return "" + key + (this.color == RED? "R" : "B"); } } 左旋的具体实现 上面对左旋的概念已经有了感性的认识了,这里就不再赘述了,我们从下面的代码中结合上面的示意图,探讨一下左旋的具体实现: /*************对红黑树节点x进行左旋操作 ******************//* * 左旋示意图:对节点x进行左旋 * 左旋做了三件事: * 1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时) * 2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右) * 3. 将y的左子节点设为x,将x的父节点设为y * */ private void leftRotate(RBNode<T> x) { //1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时) RBNode<T> y = x.right; x.right = y.left; if(y.left != null) y.left.parent = x; //2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右) y.parent = x.parent; if(x.parent == null) { this.root = y; //如果x的父节点为空,则将y设为父节点 } else { if(x == x.parent.left) //如果x是左子节点 x.parent.left = y; //则也将y设为左子节点 else x.parent.right = y; //否则将y设为右子节点 } //3. 将y的左子节点设为x,将x的父节点设为y y.left = x; x.parent = y; } 右旋具体实现 上面对右旋的概念已经有了感性的认识了,这里也不再赘述了,我们从下面的代码中结合上面的示意图,探讨一下右旋的具体实现: /*************对红黑树节点y进行右旋操作 ******************//* * 左旋示意图:对节点y进行右旋 * 右旋做了三件事: * 1. 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时) * 2. 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右) * 3. 将x的右子节点设为y,将y的父节点设为x * */ private void rightRotate(RBNode<T> y) { //1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时) RBNode<T> x = y.left; y.left = x.right; if(x.right != null) x.right.parent = y; //2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右) x.parent = y.parent; if(y.parent == null) { this.root = x; //如果x的父节点为空,则将y设为父节点 } else { if(y == y.parent.right) //如果x是左子节点 y.parent.right = x; //则也将y设为左子节点 else y.parent.left = x;//否则将y设为右子节点 } //3. 将y的左子节点设为x,将x的父节点设为y x.right = y; y.parent = x; } 5.功能 这里我就简单的介绍一下,篇幅原因(我不会承认是我饿了,想去吃宵夜了,嘿嘿嘿),后面我会进行详细的介绍 5.1查找 因为红黑树是一颗二叉平衡树,并且查找不会破坏树的平衡,所以查找跟二叉平衡树的查找无异,为了让大家更好理解,看下面这张 二叉树查找流程图 非常简单,但简单不代表它效率不好。正由于红黑树总保持黑色完美平衡,所以它的查找最坏时间复杂度为O(2lgN),也即整棵树刚好红黑相隔的时候。能有这么好的查找效率得益于红黑树自平衡的特性,而这背后的付出,红黑树的插入操作功不可没~ 5.2 插入 插入操作包括两部分工作: 一查找插入的位置 即找到要插入的父节点; 二插入后自平衡。 查找插入的父结点很简单,跟查找操作区别不大,还是以一张流程图总结一下 特别注意: 如果在面试的时候有人问你插入结点是应该是什么颜色呢?答案是红色。理由很简单,红色在父结点(如果存在)为黑色结点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡。 还有一个删除,实在是太饿了,不行了,不能随随便便应付大家,听我下回分解,哈哈哈哈,咱下次详细的讲解红黑树的应用 文章首发公众号:Java架构师联盟

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册