B树(B-Tree)

news/2024/5/17 10:55:39

        B树(B-Tree)是一种多路搜索树,用于在磁盘上存储和查找数据。它具有以下特点: 1. 每个节点可以包含多个子节点,通常用于处理大量数据和磁盘存储。 2. 每个节点中的键值按照升序排列,子节点的范围也由这些键值决定。 3. B树的节点中至少包含 t-1 个键值(t 为树的最小度数),最多包含 2t-1 个键值。 4. 所有叶子节点都在同一层,且不包含数据,用于存储实际数据的节点为内部节点。

import java.util.ArrayList;
import java.util.List;public class BTree {private Node root;private int t; // 最小度数private static class Node {List<Integer> keys;List<Node> children;boolean leaf;public Node() {keys = new ArrayList<>();children = new ArrayList<>();leaf = true;}}public BTree(int t) {this.t = t;root = new Node();}// 插入操作public void insert(int key) {Node r = root;if (r.keys.size() == 2*t - 1) {Node s = new Node();root = s;s.children.add(r);splitChild(s, 0);insertNonFull(s, key);} else {insertNonFull(r, key);}}private void insertNonFull(Node x, int key) {int i = x.keys.size() - 1;if (x.leaf) {x.keys.add(0);while (i >= 0 && key < x.keys.get(i)) {x.keys.set(i+1, x.keys.get(i));i--;}x.keys.set(i+1, key);} else {while (i >= 0 && key < x.keys.get(i)) {i--;}i++;if (x.children.get(i).keys.size() == 2*t - 1) {splitChild(x, i);if (key > x.keys.get(i)) {i++;}}insertNonFull(x.children.get(i), key);}}private void splitChild(Node x, int i) {Node z = new Node();Node y = x.children.get(i);z.leaf = y.leaf;for (int j = 0; j < t - 1; j++) {z.keys.add(y.keys.get(j + t));}if (!y.leaf) {for (int j = 0; j < t; j++) {z.children.add(y.children.get(j + t));}}for (int j = x.keys.size(); j > i; j--) {x.children.add(j + 1, x.children.get(j));}x.children.add(i + 1, z);for (int j = x.keys.size()-1; j >= i; j--) {x.keys.add(j + 1, x.keys.get(j));}x.keys.add(i, y.keys.get(t - 1));y.keys.subList(t - 1, y.keys.size()).clear();if (!y.leaf) {y.children.subList(t, y.children.size()).clear();}}// 查询操作public boolean search(int key) {return searchNode(root, key);}private boolean searchNode(Node x, int key) {int i = 0;while (i < x.keys.size() && key > x.keys.get(i)) {i++;}if (i < x.keys.size() && key == x.keys.get(i)) {return true;}if (x.leaf) {return false;} else {return searchNode(x.children.get(i), key);}}// 修改操作public void modify(int oldKey, int newKey) {delete(oldKey);insert(newKey);}// 删除操作public void delete(int key) {deleteKey(root, key);if (root.keys.isEmpty() && !root.leaf) {root = root.children.get(0);}}private void deleteKey(Node x, int key) {int idx = x.keys.indexOf(key);if (idx != -1) {if (x.leaf) {x.keys.remove(idx);} else {Node pred = x.children.get(idx);Node succ = x.children.get(idx + 1);if (pred.keys.size() >= t) {int newKey = pred.keys.get(pred.keys.size() - 1);deleteKey(pred, newKey);x.keys.set(idx, newKey);} else if (succ.keys.size() >= t) {int newKey = succ.keys.get(0);deleteKey(succ, newKey);x.keys.set(idx, newKey);} else {pred.keys.add(key);pred.keys.addAll(succ.keys);pred.children.addAll(succ.children);x.keys.remove(idx);x.children.remove(idx + 1);deleteKey(pred, key);}}} else {int i = 0;while (i < x.keys.size() && key > x.keys.get(i)) {i++;}Node child = x.children.get(i);if (child.keys.size() == t - 1) {Node leftSibling = (i > 0) ? x.children.get(i - 1) : null;Node rightSibling = (i < x.children.size() - 1) ? x.children.get(i + 1) : null;if (leftSibling != null && leftSibling.keys.size() >= t) {child.keys.add(0, x.keys.get(i - 1));x.keys.set(i - 1, leftSibling.keys.remove(leftSibling.keys.size() - 1));if (!leftSibling.leaf) {child.children.add(0, leftSibling.children.remove(leftSibling.children.size() - 1));}} else if (rightSibling != null && rightSibling.keys.size() >= t) {child.keys.add(x.keys.get(i));x.keys.set(i, rightSibling.keys.remove(0));if (!rightSibling.leaf) {child.children.add(rightSibling.children.remove(0));}} else {if (leftSibling != null) {leftSibling.keys.add(x.keys.remove(i - 1));leftSibling.keys.addAll(child.keys);leftSibling.children.addAll(child.children);x.children.remove(i);} else {child.keys.addAll(rightSibling.keys);child.children.addAll(rightSibling.children);x.keys.remove(i);x.children.remove(i + 1);}deleteKey(child, key);}} else {deleteKey(child, key);}}}// 中序遍历public void inorder() {inorderTraversal(root);}private void inorderTraversal(Node node) {if (node != null) {int i = 0;for (i = 0; i < node.keys.size(); i++) {if (!node.leaf) {inorderTraversal(node.children.get(i));}System.out.print(node.keys.get(i) + " ");}if (!node.leaf) {inorderTraversal(node.children.get(i));}}}
}

        实战代码:

import java.util.ArrayList;
import java.util.List;/*** BTree类* * @author lingfeng**/
public class BTree {/**BTree 基础参数信息配置最小度数    t=2时,称作2-3-4数,表示只能存在2、3、4子女数**/private int t = 2;  /**非根节点最小关键字数**/private int minKeys = t-1;/**内节点最大关键字数**/private int maxKeys = 2*t - 1;/**树根节点**/private BTreeNode root;/**构造函数**/public BTree(){//初始化root = new BTreeNode();root.setLeaf(true);}/**构造函数  t 最小度数**/public BTree(int t){this();this.t  = t;minKeys = t - 1;maxKeys = 2*t - 1;}/**插入元素* * 1.元素存在,插入元素* 2.元素不存在,插入元素* * **/public void insertNode(Integer key){if(root.size() == maxKeys){   //根节点已满//进行根节点分割BTreeNode tmpNode = new BTreeNode();tmpNode.setLeaf(false);tmpNode.getChildrens().add(root);btreeSplitChild(tmpNode, 0, root);root = tmpNode;}insertRootNotFull(root, key);}public void insertRootNotFull(BTreeNode node, int key){//如果该节点为叶子节点if(node.isLeaf()){//寻找当前节点所有关键字ResultSearch result = divideSearch(node.getKeys(), key);//查询结果:成功  返回已经存在关键字位置if(result.result == true){}else{    //查询结果:失败   返回插入位置node.insertKey(key, result.index);}}else{//寻找当前节点所有关键字ResultSearch result = divideSearch(node.getKeys(), key);//查询结果:成功  返回已经存在关键字位置if(result.result == true){}else{    //查询结果:失败   返回插入子女节点的位置//判断子女状态BTreeNode childNode = node.childAt(result.index);if(node.childAt(result.index).size() == (2*t - 1)){btreeSplitChild(node, result.index, childNode);if(key > node.keyAt(result.index)){   //判断key落在 前半部分 或 后半部分childNode = node.childAt(result.index + 1);}}insertRootNotFull(childNode, key);}}}/**查询元素 (BTree查询)* * 1.先查询节点的关键字列表* 2.失败则,查询子女节点;成功则,返回值* 3.递归搜索* * **/public Integer search(BTreeNode node,Integer key){List<Integer> keys_tmp = node.getKeys();ResultSearch result = divideSearch(keys_tmp, key);if(result.result){return result.index;}else{return search(node.childAt(result.index), key);}}/**二分查询* 参数 元素列表、所需要查询元素的值* * **/public ResultSearch divideSearch(List<Integer> elements, int key){int start = 0;    //扫描起始位置int end   = 0;    //扫描结束位置end = elements.size() - 1;int mid = 0; while(start<=end){mid   = (start + end)/2;if(elements.get(mid) == key){ //满足等值条件break;}else if(elements.get(mid) > key){ //在列表前半部分//改变扫描结束位置end = mid - 1;}else if(elements.get(mid) <= key){//改变扫描开始位置start = mid + 1;}}boolean result = false;Integer index = 0;if(start<=end){  //二分查找成功result = true;index = mid;}else{    //查找失败,不存在元素result = false;index = start;}return new ResultSearch(result,index);}/*** 节点分割函数* @param parentNode  被分割节点的父节点* @param index  被分割节点在父节点的第index个子女索引位置* @param Node   被分割节点*/public void btreeSplitChild(BTreeNode parentNode, int index, BTreeNode node){//新建一个节点,保存分割后[t+1 2t-1]数据BTreeNode subNode = new BTreeNode();//必须加上subNode.setLeaf(node.isLeaf());for(int i=1; i<=minKeys ; i++){subNode.getKeys().add(node.keyAt(t + i -1));}//保存分割点值[t]Integer key = node.keyAt(t - 1);//删除被分割节点的[t 2t-1]数据for(int i= maxKeys - 1; i>=minKeys; --i){node.getKeys().remove(i);}if(!node.isLeaf()){ //如果满节点不是叶节点,关键字分割后,需要将其子女进行操作//subNode应该拥有后半部分的子女for(int i=0 ; i<minKeys+1 ; ++i){subNode.getChildrens().add(node.childAt(i+t));}//并删除node后半部分的子女for(int i=maxKeys ; i>=minKeys+1; --i){node.getChildrens().remove(i);}}else{}//将[t]数据加入父节点中去parentNode.insertKey(key, index);//将新节点关联到父节点index+1子女中parentNode.insertChild(subNode, index+1);}public void delete(Integer key){delete(root, key);}public void delete(BTreeNode node, Integer key){//删除关键字时,必须保证关键字大于等于tassert node.size() >=t || node == root;//对当前节点进行二分查找ResultSearch resultSearch = divideSearch(node.getKeys(), key);//成功if(resultSearch.result){//如果当前节点属于叶子节点,可以直接进行删除if(node.isLeaf()){node.getKeys().remove(resultSearch.index.intValue());}else{//如果不是叶子节点 ,判断前于key子节点状态BTreeNode leftChildNode = node.childAt(resultSearch.index);if(leftChildNode.size() >= t){//从leftChildNode进行借值 代替当前需要删除的关键字//删除当前节点关键字node.getKeys().remove(resultSearch.index.intValue());node.insertKey(leftChildNode.keyAt(leftChildNode.size()-1), resultSearch.index);delete(leftChildNode, leftChildNode.keyAt(leftChildNode.size()-1));}else{BTreeNode rightChildNode = node.childAt(resultSearch.index + 1);if(rightChildNode.size() >= t){//从rightChildNode进行借值 代替当前需要删除的关键字node.getKeys().remove(resultSearch.index.intValue());node.insertKey(rightChildNode.keyAt(0), resultSearch.index);delete(rightChildNode, rightChildNode.keyAt(0));}else{//对于索引的左右子节点的数量都等于t-1//合适进行合并//1.将父节点删除  将节点右子节点删除node.getKeys().remove(resultSearch.index.intValue());node.getChildrens().remove(resultSearch.index.intValue() + 1);//2.将父节点添加到左子节点上leftChildNode.getKeys().add(key);//3.将删除的右子节点添加到左子节点上for(int i=0 ; i<rightChildNode.size() ; i++){leftChildNode.getKeys().add(rightChildNode.getKeys().get(i));}//如果右子节点非叶子节点,需要将其子女继承到左节点之下if(!rightChildNode.isLeaf()){for(int k=0 ; k<=rightChildNode.size() ; k++){leftChildNode.getChildrens().add(rightChildNode.childAt(k));}}//递归删除delete(leftChildNode, key);}}}}else{ //失败if(node.isLeaf()){//不存在删除的对象System.out.println("不存在删除的对象");return ;}//获取子节点BTreeNode childNode = node.childAt(resultSearch.index);if(root == node && node.size()==0){root = childNode;}if(childNode.size() >= t){   //如果满足递归条件delete(childNode, key);}else{//不满足size == t//采取借关键字手段BTreeNode subNode = null;int subIndex = 0;//先检测右兄弟节点if(resultSearch.index < node.size()){if(node.childAt(resultSearch.index+1).size() >=t){subNode  = node.childAt(resultSearch.index+1);subIndex = resultSearch.index + 1;}}//测试左兄弟节点if(subNode == null){if(resultSearch.index > 0){if(node.childAt(resultSearch.index-1).size() >= t){subNode  = node.childAt(resultSearch.index-1);subIndex = resultSearch.index - 1;}}}//测试完成后if(subNode != null){  //存在兄弟节点大于等于t情况//判断节点if(subIndex > resultSearch.index){ //右兄弟//将右关键字插入自身childNode.insertKey(node.keyAt(subIndex - 1), childNode.size());node.getKeys().remove(subIndex - 1);node.insertKey(subNode.keyAt(0), subIndex - 1);subNode.getKeys().remove(0);//右兄弟非子叶节点,则带有孩子节点if(!subNode.isLeaf()){childNode.getChildrens().add(subNode.getChildrens().get(0));subNode.getChildrens().remove(0);}}else{  //左兄弟//将左关键字插入自身最前位置childNode.insertKey(node.keyAt(subIndex), 0);node.getKeys().remove(subIndex);node.insertKey(subNode.keyAt(subNode.size()-1), subIndex);subNode.getKeys().remove(subNode.size()-1);//如果左兄弟非子叶节点if(!subNode.isLeaf()){childNode.insertChild(subNode.childAt(subNode.size()), 0);subNode.getChildrens().remove(subNode.size()-1);}    }delete(childNode, key);}else{//该节点的左右兄弟节点关键字都为t-1//选择合并方案if(resultSearch.index < node.size()){   //右兄弟存在subNode = node.childAt(resultSearch.index + 1);//childNode.getKeys().add(node.keyAt(resultSearch.index + 1));childNode.getKeys().add(node.keyAt(resultSearch.index));node.getKeys().remove(resultSearch.index.intValue());node.getChildrens().remove(resultSearch.index.intValue());for(int i=0 ; i<subNode.size() ; i++){childNode.getKeys().add(subNode.keyAt(i));}if(!subNode.isLeaf()){for(int k=0 ; k<=subNode.size(); k++){childNode.getChildrens().add(subNode.childAt(k));}}}else{      //左兄弟存在subNode = node.childAt(resultSearch.index - 1);childNode.insertKey(node.keyAt(resultSearch.index-1), 0);node.getKeys().remove(resultSearch.index - 1);node.getChildrens().remove(resultSearch.index-1);for(int i=subNode.size()-1 ; i>=0 ; --i){childNode.insertKey(subNode.keyAt(i), 0);}if(!subNode.isLeaf()){for(int k=subNode.size() ; k>=0 ; --k){childNode.insertChild(subNode.childAt(k),0);}}}if(root == node && node.size() == 0){root = childNode;}delete(childNode, key);}}}}}
class BTreeNode{/**当前节点keys列表**/private List<Integer> keys; /**当前节点的child列表**/private List<BTreeNode> childrens;/**是否是叶子节点**/private boolean leaf;public BTreeNode(){keys      = new ArrayList<Integer>();childrens = new ArrayList<BTreeNode>();leaf = true;}/*** set and get methods* * **/public List<Integer> getKeys() {return keys;}public void setKeys(List<Integer> keys) {this.keys = keys;}public List<BTreeNode> getChildrens() {return childrens;}public void setChildrens(List<BTreeNode> childrens) {this.childrens = childrens;}public boolean isLeaf() {return leaf;}public void setLeaf(boolean leaf) {this.leaf = leaf;}/*** 获取子女节点* @param index* @return*/public BTreeNode childAt(int index){return childrens.get(index);}/*** 获取关键字* @param index* @return*/public Integer keyAt(int index){return keys.get(index);}/*** 获取节点关键字数量* @return*/public int size(){return keys.size();}/*** 插入key到指定的index中去* @param key* @param index*/public void insertKey(Integer key, int index){//插入key到指定的索引位置//保存索引前的所有关键字List<Integer> newlist = new ArrayList<Integer>();for(int i=0; i<index ; i++){newlist.add(keys.get(i));}//插入关键字newlist.add(key);//保存索引后多关键字for(int i=index ; i<keys.size() ; i++){newlist.add(keys.get(i));}//将新的关键字集合设置为节点关键字集合对象keys = newlist;}/*** 插入node子女到指定的index位置* @param node* @param index*/public void insertChild(BTreeNode node, int index){//插入childList<BTreeNode> newlist = new ArrayList<BTreeNode>();for(int i=0 ; i< index ; i++){newlist.add(childrens.get(i));}newlist.add(node);for(int i=index ; i<childrens.size() ; i++){newlist.add(childrens.get(i));}childrens = newlist;}
}
/*** 查询结果类* result 标识成功的状态 true or false* index 成功后返回元素的索引*       失败后返回元素的插入位置 * @author lingfeng*/
class ResultSearch{public Integer index;public boolean result;public ResultSearch(boolean rs, Integer i){index  = i;result = rs;}
}


http://www.mrgr.cn/p/22146123

相关文章

站立会议和燃尽图09

站立会议和燃尽图09 一、小组情况 组长:李宏威 组员:董泽豪 队名:隐约雷名 二、Scrum例会 时间:2024年4月21日 出席人员:李宏威,董泽豪 要求1 工作照片要求2 时间跨度 2024年4月28日 7:00 至 2024年4月28日 7:20 共计 20 分钟 要求3 地点 石家庄铁道大学 要求4 立会内容包…

vulnhub靶场之FunBox-1

一.环境搭建 1.靶场描述 Boot2Root ! This is a reallife szenario, but easy going. You have to enumerate and understand the szenario to get the root-flag in round about 20min. This VM is created/tested with Virtualbox. Maybe it works with vmware. If you n…

重定义大语言模型的记忆能力:对抗性压缩如何挑战现有测量法

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; Rethinking LLM Memorization through the Lens of Adversarial Compression 引言&#xff1a;探索大型语言模型的记忆能力 在当今信息时代&#xff0c;大型…

Elasticsearch:理解近似最近邻 (ANN) 算法

作者&#xff1a;来自 Elastic Elastic Platform Team 如果你是在互联网出现之前长大的&#xff0c;你会记得找到新喜好并不总是那么容易。我们是在无意中听到收音机里的新乐队时发现他们的&#xff0c;是因为忘了换频道偶然看到一个新电视节目的&#xff0c;也是几乎完全依据游…

jsrpc获取瑞数请求后缀和cookie

jsrpc获取瑞数请求后缀和cookie 记得加入我们的学习群:961566389 点击链接加入群聊:https://h5.qun.qq.com/s/62P0xwrCNO 1.分析xhr 每次请求都能看到会携带一个请求后缀uB04BPdr:以及每次请求都会更换cookie下的mEsoE3ffu2LGP:这两个就是需要逆向的参数。 2.调试 因为使用j…

spring boot3单模块项目工程搭建-下(个人开发模板)

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 目录 写在前面 上文衔接 常用依赖介绍以及整合 web组件 测试组件 样板代码生成 数据库连接器 常用工具包 面向切面编程 ORM框架 数据连接池 接口测试、文档导出 缓存中间件 参数校…

[转帖]华为鲲鹏930归来,ARM成为服务器趋势

https://zhuanlan.zhihu.com/p/675438893 今年8月,Mate60搭载的麒麟9000S归来,12月3日,笔记本L420搭载了麒麟9006C也已经上市;当年数据中心CPU领域叱咤风云的鲲鹏920,什么时候推出下一代? 2023年12月29日,华为云鲲鹏通用计算增强型实例kC2正式开启公测。官方产品鲲鹏92…

Gradformer: 通过图结构归纳偏差提升自注意力机制的图Transformer

这是4月刚刚发布在arxiv上的论文,介绍了一种名为“Gradformer”的新型图Transformer,它在自注意力机制中引入了指数衰减掩码。以下是主要创新点:指数衰减掩码: Gradformer在其自注意力模块中集成了衰减掩码。该掩码随着图结构中节点之间的距离减小而呈指数递减。这种设计使…

Ubuntu 20.04下安装Samba(Cifs/Smb)

接上一篇,本篇记录一下Ubuntu下Samba的安装配置,windows下的共享目录的设置有很多文章,这里就不说了。Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,我们可以使用apt安装,也可以去官网下载软件或者源码:apt安装如果是使用apt,那么安装就方便很多了:  #安装s…

从零开始安装 stable diffusion webui v1.9.3 (windows10)

从零开始安装 stable diffusion webui v1.9.3 (windows10) CUDA 安装 CUDA 12.1 | https://developer.nvidia.com/cuda-toolkit-archive CUDNN 8.x | https://developer.nvidia.com/rdp/cudnn-archive 安装路径 F:/CUDA/v12.1 安装git git官网 | https://git-scm.com/ 安…

树莓派点亮LED灯

简介 使用GPIO Zero library 的 Python库实现点亮LED灯。接线 树莓派引脚参考图如下&#xff1a; LED正极 接GPIO17 LED负极 接GND 权限 将你的用户加到gpio组中&#xff0c; 否则无法控制GPIO sudo usermod -a -G gpio 代码 from gpiozero import LED from time impor…

《自动机理论、语言和计算导论》阅读笔记:p215-p351

《自动机理论、语言和计算导论》学习第 11 天,p215-p351总结,总计 37 页。 一、技术总结 1.constrained problem 2.Fermats lats theorem Fermats Last Theorem states that no three positive integers a, b and c satisfy the equation a^n + b^n = c^n for any integer v…

018、Python+fastapi,第一个Python项目走向第18步:ubuntu24.04 安装cuda和pytorch环境

一、说明 我们安装了pytorch环境之后&#xff0c;会用yolo v9 来测试一下&#xff0c;看8g 显存能不能跑下来&#xff0c;上次用无影云电脑&#xff0c;4cpu8g内存直接爆了&#xff0c;云电脑也死机了&#xff0c;提示一直占用内存不释放&#xff0c;我自己的云电脑不能占用内…

在UI界面中播放视频_unity基础开发教程

在UI界面中播放视频_unity基础开发教程 前言操作步骤结语 前言 之前我写过一篇在场景中播放视频的文章&#xff0c;但是在开发中有时候也会在UI的界面中播放视频&#xff0c;这期我们做一下在UI的界面中播放视频。 操作步骤 首先在场景中创建一个Raw Image&#xff0c;UI->…

Akima算法

测量数据的内插已有各种方法,如线性内插、多项式内插、样条函数插值等,但这里的Akima插值法具有独特的优点。线性内插只顾及其附近两点的影响。多项式内插时,低阶多项式由于参数较少,内插精度很低,而使用高阶多项式又会使解不稳定,出现“龙格”现象,即内插函数在插值点与实际数…

C++11 设计模式5. 原型模式

什么是原型模式&#xff1f; 原型模式⼀种创建型设计模式&#xff0c;该模式的核⼼思想是基于现有的对象创建新的对象&#xff0c;⽽不是从头开始创建。在原型模式中&#xff0c;通常有⼀个原型对象&#xff0c;它被⽤作创建新对象的模板。新对象通过复制原型对象的属性和状态来…

CF1054F Electric Scheme

传送门和 Bricks 很像。 初始把每个点看作一条线段,然后我们可以通过行相邻的或者列相邻的两个点合并。如果横向和竖向相交了且不是相交在给定的点,不能同时选。 最大独立集即可。

OpenCV的图像矩(64)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何为等值线创建边界旋转框和椭圆(63) 下一篇 &#xff1a;OpenCV4.9的点多边形测试(65) Image Moments&#xff08;图像矩&#xff09;是 OpenCV 库中的一个功能&#xff0c;它可…

水稻病害检测(YOLO数据集,多分类,稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫)

是自己利用LabelImg工具进行手工标注&#xff0c;数据集制作不易&#xff0c;请尊重版权&#xff08;稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫&#xff09; 如果需要yolv8检测模型和数据集放在一起的压缩包&#xff0c;可以关注&#xff1a;最新最…

从MySQL+MyCAT架构升级为分布式数据库,百丽应用OceanBase 4.2的感受分享

本文来自OceanBase的客户&#xff0c;百丽时尚的使用和测试分享 业务背景 百丽时尚集团&#xff0c;作为国内大型时尚鞋服集团&#xff0c;在中国超过300个城市设有直营门店&#xff0c;数量超过9,000家。集团构建了以消费者需求为核心的垂直一体化业务模式&#xff0c;涵盖了…