04树 + 堆 + 优先队列 + 图(D1_树(D6_B树(B)))
目录
一、学习前言
二、基本介绍
三、特性
1. 从概念上说起
2. 举个例子
四、代码实现
节点准备
大体框架
实现分裂
实现新增
实现删除
五、完整源码
一、学习前言
前面我们已经讲解过了二叉树、二叉搜索树(BST)、平衡二叉搜索树(AVL树)、红黑树,接下
爱就让我们了解下B树到底是什么?
二、基本介绍
在计算机科学中,B树是一种自平衡的树形数据结构,能够保持数据有序。
这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数量级的时间复杂度内
完成。
- B树,其实是一颗特殊的二叉查找树(binary search tree),可以拥有多于2个子节点。与自平衡二叉查找树不同,B树为系统大块数据的读写操作做了优化。
- B树减少定位记录时所经历的中间过程,从而加快存取速度,其实B树主要解决的就是数据IO的问题。
- 树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。
总而言之,B树主要用于管理磁盘上的数据管理(减少磁盘IO次数),而之前说的AVL树与红黑树
适合用于内存数据管理。例如:存储一个100w的数据使用AVL存储,树高大约为20层(),如果使用磁盘IO查询20次效率较低。
注意:常常面试官会问B树和B-树的区别,我们该如何回答?其实,B树和B-树是同一种数据结
构,如果不清楚的话,会被面试官忽悠!
三、特性
1. 从概念上说起
度degree:指树中节点孩子数
阶order:指所有节点孩子数中最大值
一棵 B-树具有以下性质
特性1:每个节点 x 具有
- 属性 n,表示节点 x 中 key 的个数
- 属性 leaf,表示节点是否是叶子节点
- 节点 key 可以有多个,以升序存储
特性2:每个节点最多具有m个孩子,其中m叫做B-树的阶
特性3:除根结点与叶子节点外,每个节点至少有ceil(m/2)个孩子,根节点不是叶子节点时,最少
有两个孩子。叶子节点没有孩子
特性4:每个非叶子节点中的孩子数是 n + 1。而n的取值为ceil(m/2)-1<=n<=m-1。
特性5:最小度数t(节点的孩子数称为度)和节点中键数量的关系如下:
最小度数t | 键数量范围 |
2 | 1 ~ 3 |
3 | 2 ~ 5 |
4 | 3 ~ 7 |
... | ... |
n | (n-1) ~ (2n-1) |
其中,当节点中键数量达到其最大值时,即 3、5、7 ... 2n-1,需要分裂
特性6:叶子节点的深度都相同
2. 举个例子
一个m阶的B树特点如下:
- 所有叶子节点都在同一层级;
- 除了根节点以外的其他节点包含的key值数量在[m/2]-1到m-1的数据范围;
- 除了根节点和叶子节点外,所有中间节点至少有m/2个孩子节点;
- 根节点如果不是叶子节点的话,它必须包含至少2个孩子节点;
- 拥有n-1个key值非叶子节点必须有n个孩子节点;
- 一个节点的所有key值必须是升序排序的;
以上六点就是B树的全部特性
四、代码实现
节点准备
B树的节点属性,与其他树不太相同,首先是key可以有多个,因此要设置为数组,孩子节点也未
知,因此也要设置为数组。
本应该还存在一个value属性,这里简化掉,不添加该属性。
static class Node {boolean leaf = true; //是否是叶子节点int keyNumber; //有效keyint t; //最小度int[] keys; // keys数组Node[] children; //孩子节点数组public Node(int t) {this.t = t;this.keys = new int[2 * t - 1];this.children = new Node[2 * t];//最大孩子节点个数为为2*t}@Overridepublic String toString() {return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumber));}/*** 根据key获取对应节点** @param key* @return*/Node get(int key) {int i = 0;while (i < keyNumber) {//如果在该节点找到,那么直接返回即可if (keys[i] == key) {return this;}//说明要找的元素可能在children[i]中if (keys[i] > key) {break;}i++;}//如果是叶子节点,直接返回nullif (leaf) {return null;}return children[i].get(key);}/*** 指定位置插入元素** @param key* @param index*/void insertKey(int key, int index) {System.arraycopy(keys, index, keys, index + 1, keyNumber - index);keys[index] = key;keyNumber++;}/*** 向节点中插入孩子节点* @param child* @param index*/void insertChild(Node child, int index) {System.arraycopy(children, index, children, index + 1, keyNumber - index);children[index] = child;}
}
这里我采用了静态数组,因此需要多添加一个keyNumber参数来获取有效key的数量,如果使用
ArrayList,可以通过size方法获取,因此不需要添加这个属性。
大体框架
public class BTree {private Node root;private int t;//最小度数final int MAX_KEY_NUMBER;//最大key数量final int MIN_KEY_NUMBER;//最小key数量。用于分裂使用public BTree(int t) {this.t = t;root = new Node(t);MAX_KEY_NUMBER = 2 * t - 1;MIN_KEY_NUMBER = 2 * t;}static class Node {boolean leaf = true; //是否是叶子节点int keyNumber; //有效keyint t; //最小度int[] keys; // keys数组Node[] children; //孩子节点数组public Node(int t) {this.t = t;this.keys = new int[2 * t - 1];this.children = new Node[2 * t];//最大孩子节点个数为为2*t}@Overridepublic String toString() {return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumber));}/*** 根据key获取对应节点** @param key* @return*/Node get(int key) {int i = 0;while (i < keyNumber) {//如果在该节点找到,那么直接返回即可if (keys[i] == key) {return this;}//说明要找的元素可能在children[i]中if (keys[i] > key) {break;}i++;}//如果是叶子节点,直接返回nullif (leaf) {return null;}return children[i].get(key);}/*** 指定位置插入元素** @param key* @param index*/void insertKey(int key, int index) {System.arraycopy(keys, index, keys, index + 1, keyNumber - index);keys[index] = key;keyNumber++;}/*** 向节点中插入孩子节点** @param child* @param index*/void insertChild(Node child, int index) {System.arraycopy(children, index, children, index + 1, keyNumber - index);children[index] = child;}}
}
实现分裂
先说分裂规律,当新增一个节点后使节点中的key满足2t-1,那么该节点就会被分裂。
- 创建一个新的节点,暂时称为right节点(分裂后会在被分裂节点的右边)
- 被分裂节点把 t 以后的 key 和 child 都拷贝到right节点
- t-1 处的 key 插入到 parent 的 index 处,index 指被分裂节点作为孩子时的索引
- right 节点作为 parent 的孩子插入到 index + 1 处
图示如下:
红色部分的意思是当前节点是父结点中作为孩子的下标。黑色部分是key,小数字代表key的下标。
起始存在一个t为3的B树。那么最大key就为 2*3-1 。
此时作为父结点孩子下标为1的节点以及存在 4 个key,再添加一个key就会触发分裂。
现在,再添加一个新的key值 8 ,此时到达最大key数,触发分裂
此时分裂结束,分裂后结果如下
具体实现代码如下
private void split(Node parent, int index, Node split) {if (parent == null) {//说明分割根节点,除了需要创建right节点之外,还需要创建parent节点parent = new Node(t);parent.leaf = false;parent.insertChild(split, 0);root = parent;}Node right = new Node(t);//将被分裂节点的一部分key放入right节点中。System.arraycopy(split.keys, index, right.keys, 0, t - 1);//新建的节点与被分裂节点在同一层,因此leaf属性应该和被分裂节点一样right.leaf = split.leaf;right.keyNumber = t - 1;//如果被分裂节点不是叶子节点,也需要将孩子节点一并拷贝到right节点中if (!split.leaf) {System.arraycopy(split.children, t, right.children, 0, t);}split.keyNumber = t - 1;//将t-1节点放入父结点中int mid = split.keys[t - 1];parent.insertKey(mid, index);parent.insertChild(right, index + 1);
}
实现新增
- 首先查找本节点中的插入位置 i,如果没有空位(key 被找到),应该走更新的逻辑。
- 接下来分两种情况
- 如果节点是叶子节点,可以直接插入了
- 如果节点是非叶子节点,需要继续在 children[i] 处继续递归插入
- 无论哪种情况,插入完成后都可能超过节点 keys 数目限制,此时应当执行节点分裂
- 参数中的 parent 和 index 都是给分裂方法用的,代表当前节点父节点,和分裂节点是第几个孩子
具体实现代码如下
public void put(int key) {doPut(null, 0, root, key);
}private void doPut(Node parent, int index, Node node, int key) {int i = 0;while (i < node.keyNumber && node.keys[i] < key) {i++;}// TODO i<node.keyNumber是否多余?if (i < node.keyNumber && node.keys[i] == key) {return;}if (node.leaf) {node.insertKey(key, i);} else {doPut(node, i, node.children[i], key);}if (isFull(node)) {split(parent, index, node);}
}private boolean isFull(Node node) {return node.keyNumber == MAX_KEY_NUMBER;
}
实现删除
删除节点会存在下面几种情况
case 1:当前节点是叶子节点,没找到。直接返回null
case 2:当前节点是叶子节点,找到了。直接移除节点即可
case 3:当前节点是非叶子节点,没找到。递归寻找孩子节点是否存在该key
case 4:当前节点是非叶子节点,找到了。找到后驱节点交换key,并将交换后的key删除
case 5:删除后 key 数目 < 下限(不平衡)。需要进行调整
在兄弟节点中keyNumber数量充足的情况下可以通过旋转调整平衡。
图示如下:
现在要删除节点 2
删除之后,左侧孩子的key数量少于最小限度,因此需要进行一次左旋。
父结点 3 移动到左侧孩子节点中,右侧孩子节点中的第一个key 5 移动到父结点中,左旋结束。
但如果兄弟节点的key数量是最小限度,那么此时应该进行合并,而不是旋转。
合并时,我们通常选择将右侧的节点合并到左侧节点中去。
图示如下:
此时要删除key 3 ,右侧兄弟节点无法再向被删除节点提供key。
于是将右侧节点移除,同时将父结点的值与被移除节点的值都放在最初的左孩子节点中。
case 6:根节点当经过合并之后,根结点可能会存在为null的情况,此时让根节点中的 0 号孩子替
代掉根节点就好。
具体实现代码如下
/*** 移除指定元素** @param key*/
public void remove(int key) {doRemove(null,root,0, key);
}private void doRemove(Node parent,Node node,int index, int key) {//首先要获取指定元素int i = 0;while (i < node.keyNumber && node.keys[i] < key) {i++;}if (node.leaf) {if (node.keys[i] == key) {//case 2:如果找到了并且是叶子节点node.removeKey(i);} else {//case 1:如果没找到并且是叶子节点return;}} else {if (node.keys[i] == key) {//case 4:如果找到了但不是叶子节点//找到后驱节点并交换位置Node child = node.children[i + 1];while (!child.leaf) {child = child.children[0];}int nextKey = child.keys[0];node.keys[i] = nextKey;//之所以不直接调用孩子节点的removeKey方法是为了避免删除后发生不平衡//child.removeKey(0);doRemove(node,child,i+1, nextKey);} else {//case 3:如果没找到但不是叶子节点doRemove(node,node.children[i],i, key);}}//如果删除后,节点中的key少于下限,那么需要进行调整if (node.keyNumber < MIN_KEY_NUMBER) {//平衡调整balance(parent,node,index);}
}/*** 调整B树** @param parent 父结点* @param node 被调整节点* @param index 被调整节点在父结点中的孩子数组下标*/
private void balance(Node parent, Node node, int index) {//case 6 根节点if (node == root) {if (node.keyNumber==0 && node.children[0]!=null){root = node.children[0];}return;}Node leftChild = parent.leftSibling(index);Node rightChild = parent.rightSibling(index);//如果左边孩子节点中的key值充足if (leftChild != null && leftChild.keyNumber > MIN_KEY_NUMBER) {//将父结点中的key赋值给nodenode.insertKey(parent.keys[index - 1], 0);if (!leftChild.leaf) {//如果左侧孩子不是一个叶子节点,在旋转过后,会导致keysNumber+1!=children。//因此将多出来的孩子赋值更多出来一个key的被调整节点node.insertChild(leftChild.removeRightmostChild(), 0);}//将左孩子中最右侧元素赋值给父结点parent.keys[index - 1] = leftChild.removeRightmostKey();return;}//如果右边充足if (rightChild != null && rightChild.keyNumber > MIN_KEY_NUMBER) {node.insertKey(parent.keys[index], node.keyNumber);if (!rightChild.leaf) {node.insertChild(rightChild.removeLeftmostChild(), node.keyNumber);}parent.keys[index] = rightChild.removeLeftmostKey();return;}//合并//如果删除节点存在左兄弟,向左合并if (leftChild != null) {//将被删除节点从父结点上移除parent.removeChild(index);//将父结点的被移除节点的前驱节点移动到左兄弟上leftChild.insertKey(parent.removeKey(index - 1), leftChild.keyNumber);node.moveToTarget(leftChild);} else {//如果没有左兄弟,那么移除右兄弟节点,并将右兄弟移动到被删除节点上。parent.removeChild(index+1);node.insertKey(parent.removeKey(index),node.keyNumber);rightChild.moveToTarget(node);}
}
五、完整源码
public class BTree {private Node root;private int t;//最小度数private final int MAX_KEY_NUMBER;private final int MIN_KEY_NUMBER;public BTree(int t) {this.t = t;root = new Node(t);MAX_KEY_NUMBER = 2 * t - 1;MIN_KEY_NUMBER = t-1;}static class Node {boolean leaf = true; //是否是叶子节点int keyNumber; //有效keyint t; //最小度int[] keys; // keys数组Node[] children; //孩子节点数组public Node(int t) {this.t = t;this.keys = new int[2 * t - 1];this.children = new Node[2 * t];//最大孩子节点个数为为2*t}@Overridepublic String toString() {return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumber));}/*** 根据key获取对应节点** @param key* @return*/Node get(int key) {int i = 0;while (i < keyNumber) {//如果在该节点找到,那么直接返回即可if (keys[i] == key) {return this;}//说明要找的元素可能在children[i]中if (keys[i] > key) {break;}i++;}//如果是叶子节点,直接返回nullif (leaf) {return null;}return children[i].get(key);}/*** 指定位置插入元素** @param key* @param index*/void insertKey(int key, int index) {System.arraycopy(keys, index, keys, index + 1, keyNumber - index);keys[index] = key;keyNumber++;}/*** 向节点中插入孩子节点** @param child* @param index*/void insertChild(Node child, int index) {System.arraycopy(children, index, children, index + 1, keyNumber - index);children[index] = child;}//移除指定元素int removeKey(int index) {int t = keys[index];System.arraycopy(keys, index + 1, keys, index, --keyNumber - index);return t;}//移除最左边的元素int removeLeftmostKey() {return removeKey(0);}//移除最右边的元素int removeRightmostKey() {return removeKey(keyNumber - 1);}//移除指定位置的孩子节点Node removeChild(int index) {//获取被移除的节点Node t = children[index];//将被移除节点的后面元素向前移动一位System.arraycopy(children, index + 1, children, index, keyNumber - index);//将之前最后一位的引用释放children[keyNumber] = null;//返回被引用元素return t;}//移除最左边的孩子节点Node removeLeftmostChild() {return removeChild(0);}//移除最右边的孩子节点Node removeRightmostChild() {return removeChild(keyNumber);}//移动指定节点到目标节点void moveToTarget(Node target) {int start = target.keyNumber;if (!leaf) {for (int i = 0; i <= keyNumber; i++) {target.children[start + i] = children[i];}}for (int i = 0; i < keyNumber; i++) {target.keys[target.keyNumber++] = keys[i];}}//获取指定孩子节点的左边节点Node leftSibling(int index) {return index > 0 ? children[index - 1] : null;}//获取指定孩子节点的右边节点Node rightSibling(int index) {return index == keyNumber ? null : children[index + 1];}}/*** 查询key值是否在树中** @param key* @return*/public boolean contains(int key) {return root.get(key) != null;}/*** 新增节点** @param key*/public void put(int key) {doPut(null, 0, root, key);}//index的作用是,给分割方法提供参数private void doPut(Node parent, int index, Node node, int key) {//寻找插入位置int i = 0;while (i < node.keyNumber && node.keys[i] < key) {i++;}//如果找到了,直接更新if (node.keys[i] == key) {return;}//如果没找到,查看是否是叶子节点,如果不是,向下寻找if (node.leaf) {node.insertKey(key, i);} else {doPut(node, i, node.children[i], key);}if (isFull(node)) {split(parent, index, node);}}private boolean isFull(Node node) {return node.keyNumber == MAX_KEY_NUMBER;}/*** 分裂节点** @param parent* @param index 分割节点在父结点的孩子下标* @param split*/private void split(Node parent, int index, Node split) {if (parent == null) {//说明分割根节点,除了需要创建right节点之外,还需要创建parent节点parent = new Node(t);parent.leaf = false;parent.insertChild(split, 0);root = parent;}Node right = new Node(t);//将被分裂节点的一部分key放入right节点中。System.arraycopy(split.keys, t, right.keys, 0, t - 1);//新建的节点与被分裂节点在同一层,因此leaf属性应该和被分裂节点一样right.leaf = split.leaf;right.keyNumber = t - 1;//如果被分裂节点不是叶子节点,也需要将孩子节点一并拷贝到right节点中if (!split.leaf) {System.arraycopy(split.children, t, right.children, 0, t);for (int i =t;i<=split.keyNumber;i++){split.children[i]=null;}}split.keyNumber = t - 1;//将t-1节点放入父结点中int mid = split.keys[t - 1];parent.insertKey(mid, index);parent.insertChild(right, index + 1);}/*** 移除指定元素** @param key*/public void remove(int key) {doRemove(null,root,0, key);}private void doRemove(Node parent,Node node,int index, int key) {//首先要获取指定元素int i = 0;while (i < node.keyNumber && node.keys[i] < key) {i++;}if (node.leaf) {if (node.keys[i] == key) {//case 2:如果找到了并且是叶子节点node.removeKey(i);} else {//case 1:如果没找到并且是叶子节点return;}} else {if (node.keys[i] == key) {//case 4:如果找到了但不是叶子节点//找到后驱节点并交换位置Node child = node.children[i + 1];while (!child.leaf) {child = child.children[0];}int nextKey = child.keys[0];node.keys[i] = nextKey;//之所以不直接调用孩子节点的removeKey方法是为了避免删除后发生不平衡//child.removeKey(0);doRemove(node,child,i+1, nextKey);} else {//case 3:如果没找到但不是叶子节点doRemove(node,node.children[i],i, key);}}//如果删除后,节点中的key少于下限,那么需要进行调整if (node.keyNumber < MIN_KEY_NUMBER) {//平衡调整balance(parent,node,index);}}/*** 调整B树** @param parent 父结点* @param node 被调整节点* @param index 被调整节点在父结点中的孩子数组下标*/private void balance(Node parent, Node node, int index) {//case 6 根节点if (node == root) {if (node.keyNumber==0 && node.children[0]!=null){root = node.children[0];}return;}Node leftChild = parent.leftSibling(index);Node rightChild = parent.rightSibling(index);//如果左边孩子节点中的key值充足if (leftChild != null && leftChild.keyNumber > MIN_KEY_NUMBER) {//将父结点中的key赋值给nodenode.insertKey(parent.keys[index - 1], 0);if (!leftChild.leaf) {//如果左侧孩子不是一个叶子节点,在旋转过后,会导致keysNumber+1!=children。//因此将多出来的孩子赋值更多出来一个key的被调整节点node.insertChild(leftChild.removeRightmostChild(), 0);}//将左孩子中最右侧元素赋值给父结点parent.keys[index - 1] = leftChild.removeRightmostKey();return;}//如果右边充足if (rightChild != null && rightChild.keyNumber > MIN_KEY_NUMBER) {node.insertKey(parent.keys[index], node.keyNumber);if (!rightChild.leaf) {node.insertChild(rightChild.removeLeftmostChild(), node.keyNumber);}parent.keys[index] = rightChild.removeLeftmostKey();return;}//合并//如果删除节点存在左兄弟,向左合并if (leftChild != null) {//将被删除节点从父结点上移除parent.removeChild(index);//将父结点的被移除节点的前驱节点移动到左兄弟上leftChild.insertKey(parent.removeKey(index - 1), leftChild.keyNumber);node.moveToTarget(leftChild);} else {//如果没有左兄弟,那么移除右兄弟节点,并将右兄弟移动到被删除节点上。parent.removeChild(index+1);node.insertKey(parent.removeKey(index),node.keyNumber);rightChild.moveToTarget(node);}}}
相关文章:
04树 + 堆 + 优先队列 + 图(D1_树(D6_B树(B)))
目录 一、学习前言 二、基本介绍 三、特性 1. 从概念上说起 2. 举个例子 四、代码实现 节点准备 大体框架 实现分裂 实现新增 实现删除 五、完整源码 一、学习前言 前面我们已经讲解过了二叉树、二叉搜索树(BST)、平衡二叉搜索树(…...
350.两个数组的交集 ②
目录 题目过程解法 题目 给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑…...
C#,入门教程(09)——运算符的基础知识
上一篇: C#,入门教程(08)——基本数据类型及使用的基础知识https://blog.csdn.net/beijinghorn/article/details/123906998 一、算术运算符号 算术运算符号包括:四则运算 加 , 减-, 乘*, 除/与取模%。 // 加法,运算 int va 1 …...
Python-基于PyQt5,wordcloud,pillow,numpy,os,sys等的智能词云生成器
前言:日常生活中,我们有时后就会遇见这样的情形:我们需要将给定的数据进行可视化处理,同时保证呈现比较良好的量化效果。这时候我们可能就会用到词云图。词云图(Word cloud)又称文字云,是一种文…...
海外问卷调查之渠道查,企业经营的指南针
海外问卷调查,是企业调研最常用到的方法,有目的、有计划、有系统地收集研究对象的现实状况或历史状况的一种有效手段,是指导企业经营的有效手段。 海外问卷调查充分运用历史法、观察法等方法,同时使用谈话、问卷、个案研究、测试…...
C++:虚函数与多态性习题
题目内容: 构建一个车(vehicle)基类,包含Run、Stop两个纯虚函数。由此基类,派生出(Car)轿车类,(truck)卡车类,在这两个类中别分定义Run和Stop两个…...
单片机基础模块学习——超声波传感器
一、超声波原理 左边发射超声波信号,右边接收超声波信号 左边的芯片用来处理超声波发射信号,中间的芯片用来处理接收的超声波信号 二、超声波原理图 T——transmit 发送R——Recieve 接收 U18芯片对输入的N_A1信号进行放大,然后输入给超声…...
通过protoc工具生成proto的pb.go文件以及使用protoc-go-inject-tag工具注入自定义标签
1.ProtoBuf认识,安装以及用法 参考:[golang 微服务] 3. ProtoBuf认识,安装以及golang 中ProtoBuf使用 2. 使用protoc-go-inject-tag工具注入自定义标签 这里有一个案例: syntaxproto3; package test;option go_package ".;test";message MyMessage {int6…...
42【语言的编码架构】
不同语言采用的编码架构不一样 火山采用:UTF-16 易语言采用:GBK php采用:UTF-8 这个编码架构指的就是文本所代表的字节集,比如易语言中“你好”表示的就是{196,227,186,195} 窗口程序集名保 留 保 留备 注窗口程序集_启动窗口 …...
TOF技术原理和静噪对策
本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时,也能帮助其他需要参考的朋友。如有谬误,欢迎大家进行指正。 一、什么是TOF TOF 是Time of Flight的缩写,它是一种通过利用照射波和反射波之间的时间差来测量到物体的距离的测…...
ssh调试:fatal: Could not read from remote repository.
我遇到的原因和网上说的什么在生产密钥时没加邮箱,以及多个密钥的配置问题都不一样; 例如https://blog.csdn.net/baoyin0822/article/details/122584931 或https://blog.csdn.net/qq_55558061/article/details/124117445 我遇到的问题的原因跟他们都i不…...
win10部署本地deepseek-r1,chatbox,deepseek联网(谷歌网页插件)
win10部署本地deepseek-r1,chatbox,deepseek联网(谷歌网页插件) 前言一、本地部署DeepSeek-r1step1 安装ollamastep2 下载deepseek-r1step2.1 找到模型deepseek-r1step2.2 cmd里粘贴 后按回车,进行下载 step3 测试指令…...
SpringCloud系列教程:微服务的未来(十九)请求限流、线程隔离、Fallback、服务熔断
前言 前言 在现代微服务架构中,系统的高可用性和稳定性至关重要。为了解决系统在高并发请求或服务不可用时出现的性能瓶颈或故障,常常需要使用一些技术手段来保证服务的平稳运行。请求限流、线程隔离、Fallback 和服务熔断是微服务中常用的四种策略&…...
Hot100之子串
560和为K的子数组 题目 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列 思路解析 ps:我们的presum【0】就是0,如果没有这个0的话我们的第一个元素就无法减去上…...
SpringBoot笔记
1.创建 使用idea提供的脚手架创建springboot项目,选上需要的模块,会自动进行导包 打成jar包,之前直接用原生的maven打包的是一个瘦jar,不能直接跑,把服务器上部署的jar排除在外了,但是现在加上打包查件&am…...
一、TensorFlow的建模流程
1. 数据准备与预处理: 加载数据:使用内置数据集或自定义数据。 预处理:归一化、调整维度、数据增强。 划分数据集:训练集、验证集、测试集。 转换为Dataset对象:利用tf.data优化数据流水线。 import tensorflow a…...
4 Hadoop 面试真题
4 Hadoop 面试真题 1. Apache Hadoop 3.0.02. HDFS 3.x 数据存储新特性-纠删码Hadoop面试真题 1. Apache Hadoop 3.0.0 Apache Hadoop 3.0.0在以前的主要发行版本(hadoop-2.x)上进行了许多重大改进。 最低要求的Java版本从Java 7增加到Java 8 现在&…...
信息学奥赛一本通 ybt 1608:【 例 3】任务安排 3 | 洛谷 P5785 [SDOI2012] 任务安排
【题目链接】 ybt 1608:【 例 3】任务安排 3 洛谷 P5785 [SDOI2012] 任务安排 【题目考点】 1. 动态规划:斜率优化动规 2. 单调队列 3. 二分答案 【解题思路】 与本题题面相同但问题规模不同的题目: 信息学奥赛一本通 1607:…...
实验六 项目二 简易信号发生器的设计与实现 (HEU)
声明:代码部分使用了AI工具 实验六 综合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 实验项目 要求利用硬件描述语言Verilog(或VHDL)、图形描述方式、IP核,结合数字系统设计方法,在Quartus开发环境下ÿ…...
基于最近邻数据进行分类
人工智能例子汇总:AI常见的算法和例子-CSDN博客 完整代码: import torch import numpy as np from sklearn.neighbors import KNeighborsClassifier from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt# 生成一个简单的数据…...
SpringSecurity:There is no PasswordEncoder mapped for the id “null“
文章目录 一、情景说明二、分析三、解决 一、情景说明 在整合SpringSecurity功能的时候 我先是去实现认证功能 也就是,去数据库比对用户名和密码 相关的类: UserDetailsServiceImpl implements UserDetailsService 用于SpringSecurity查询数据库 Logi…...
redex快速体验
第一步: 2.回调函数在每次state发生变化时候自动执行...
Flask框架基础入门教程_ezflaskapp
pip install flaskFlask 快速入门小应用 学东西,得先知道我们用这个东西,能做出来一个什么东西。 一个最小的基于flask 的应用可能看上去像下面这个样子: from flask import Flask app Flask(__name__)app.route(/) def hello_world():ret…...
Anaconda 全面解析:从入门到精通的操作教程
大家好,我是滔滔,欢迎来到我的空间。先简单介绍下anconda 一、环境管理 轻松创建独立的 Python 环境:可以为不同的项目创建不同的环境,每个环境可以有不同的 Python 版本和安装不同的包,避免了包冲突问题。例如&…...
3D图形学与可视化大屏:什么是材质属性,有什么作用?
一、颜色属性 漫反射颜色 漫反射颜色决定了物体表面对入射光进行漫反射后的颜色。当光线照射到物体表面时,一部分光被均匀地向各个方向散射,形成漫反射。漫反射颜色的选择会直接影响物体在光照下的外观。例如,一个红色的漫反射颜色会使物体在…...
HTML-新浪新闻-实现标题-排版
标题排版 图片标签:<img> src:指定图片的url(绝对路径/相对路径) width:图片的宽度(像素/相对于父元素的百分比) heigth:图片的高度(像素/相对于父元素的百分比&a…...
.Net / C# 分析文件编码 并将 各种编码格式 转为 另一个编码格式 ( 比如: GB2312→UTF-8, UTF-8→GB2312)
相关库 .Net 8 编码识别: github.com/CharsetDetector/UTF-unknown <PackageReference Include"UTF.Unknown" Version"2.5.1" />代码 using UtfUnknown;var dir_path "D:\\Desktop\\新建文件夹2\\新建文件夹"; var dir_new_path &quo…...
本地部署 DeepSeek-R1 大模型
本地部署 DeepSeek-R1 大模型指南 1. 引言 1.1 DeepSeek-R1 模型简介 在人工智能的世界里,大型语言模型(LLM)正如一座巨大的宝库,里面储存着丰富的信息和无限的潜力。而DeepSeek-R1,就像那扇打开智慧之门的钥匙。它…...
canvas的基本用法
canvas canvas元素简介 1.是个container元素<canvas width100 height100></canvas>,有开闭标签 2.有且只有width和height两个attribute,不需要写单位 canvas的基本使用 const canvasEl document.getElementById(canvas01) const ctx …...
力扣【416. 分割等和子集】详细Java题解(背包问题)
首先我们可以求出数组和,当我们找到一个子集中元素的和为数组和的一半时,该就说明可以分割等和子集。 对于该问题我们可以转换成背包问题,求 数组里的元素 装入 数组和的一半大小的背包 能取得的最大值。 然后注意可以剪枝的地方。 代码&…...
UE学习日志#17 C++笔记#3 基础复习3
19.2 [[maybe_unused]] 禁止编译器在未使用某些内容时发出警告 19.3 [[noreturn]] 永远不会把控制权返回给调用点 19.4 [[deprecated]] 标记为已弃用,仍然可以使用但是不鼓励使用 可以加参数表示弃用的原因[[deprecated("")]] 19.5 [[likely]]和[[un…...
c++:vector
1.使用 1.1构造函数 常见的三种构造方式:空构造,拷贝构造,指定元素构造 1.2iterator begin和end也分为正向和反向。 注意:反向迭代器可以反向遍历是因为在定义rbegin和rend函数的时候把尾地址给到了rbegin,而不是说改…...
37. RGBLCD实验
一、RGBLCD显示原理简介 1、像素点 于一个“小灯“,不管是液晶屏,还是手机,平板,RGBLCD屏幕他都是有由一个个的彩色小灯构成的。彩色点阵屏每个像素点有三个小灯,红色、绿色和蓝色,也叫做RGB。RGB就是光的…...
反馈驱动、上下文学习、多语言检索增强等 | Big Model Weekly 第55期
点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 01 A Bayesian Approach to Harnessing the Power of LLMs in Authorship Attribution 传统方法严重依赖手动特征,无法捕捉长距离相关性,限制了其有效性。最近的研究利用预训练语言模型的…...
【深度分析】微软全球裁员计划不影响印度地区,将继续增加当地就业机会
当微软的裁员刀锋掠过全球办公室时,班加罗尔的键盘声却愈发密集——这场资本迁徙背后,藏着数字殖民时代最锋利的生存法则。 表面是跨国公司的区域战略调整,实则是全球人才市场的地壳运动。微软一边在硅谷裁撤年薪20万美金的高级工程师&#x…...
H. Mad City
题目链接:Problem - H - Codeforces 题目大意:给定一个带环的图, 以及a, b两点 判断再图上不断的移动, b想不与a相遇, a想捉到b, 并且二者只能移动一步。 若b跑不掉 NO 否则YES. 具体题目看链接 输入: …...
【C++】类与对象(下)
🦄 个人主页: 小米里的大麦-CSDN博客 🎏 所属专栏: 小米里的大麦——C专栏_CSDN博客 🎁 代码托管: 小米里的大麦的Gitee仓库 ⚙️ 操作环境: Visual Studio 2022 文章目录 1. 再谈构造函数1.1 构造函数体赋值1.2 初始化列表1.3 explicit 关键…...
AJAX案例——图片上传个人信息操作
黑马程序员视频地址: AJAX-Day02-11.图片上传https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p26 图片上传 <!-- 文件选择元素 --><input type"file"…...
Redis-布隆过滤器
文章目录 布隆过滤器的特点:实践布隆过滤器应用 布隆过滤器的特点: 就可以把布隆过滤器理解为一个set集合,我们可以通过add往里面添加元素,通过contains来判断是否包含某个元素。 布隆过滤器是一个很长的二进制向量和一系列随机映射函数。 可以用来检索…...
OpenCV 图像旋转
在学习 OpenCV 和 Matplotlib 处理图像时,遇到了一些关于 cv2.imread()、cv2.getRotationMatrix2D()、plt.imshow() 的问题: import cv2 import numpy as np import matplotlib.pyplot as pltimg cv2.imread(img2.png, 1) # 读取彩色图像(…...
联想Y7000+RTX4060+i7+Ubuntu22.04运行DeepSeek开源多模态大模型Janus-Pro-1B+本地部署
直接上手搓了: conda create -n myenv python3.10 -ygit clone https://github.com/deepseek-ai/Janus.gitcd Januspip install -e .pip install webencodings beautifulsoup4 tinycss2pip install -e .[gradio]pip install pexpect>4.3python demo/app_januspr…...
在线知识库创建与维护提升企业效率与知识共享能力
内容概要 在当今数字化快速发展的背景下,在线知识库逐渐成为企业管理信息的重要工具。其核心在于将知识进行系统化、结构化的整理和存储,便于员工获取和分享。这不仅提高了信息的访问效率,还促进了团队之间的协作。在线知识库的建立可以有效…...
C语言指针专题二 -- 字符指针与字符串
目录 1. 字符指针与字符串的核心原理 字符串的本质 字符串的存储 字符指针的特性 字符指针的操作 2. 编程实例 3. 常见陷阱与注意事项 4. 总结 1. 字符指针与字符串的核心原理 字符串的本质 C语言中没有独立的字符串类型,字符串本质是 以\0(空…...
玄武计划--干中学,知行合一
作为开发者转型安全领域有一定优势,但需要系统学习网络安全知识。以下是针对你的情况(Java背景 + 快速入门)的实战导向学习路径,分为基础、工具、漏洞利用和进阶四个阶段: 一、基础准备(1-2周) 网络协议与渗透基础 重点协议:深入理解 TCP/IP、HTTP/HTTPS、DNS、SMTP,用…...
处理 .gitignore 未忽略文件夹问题
本地删除缓存 例如 .idea 文件夹被其他同事误提交,那么他本地执行以下代码 git rm -r --cached .idea对应本地再提交即可...
实验七 JSP内置对象II
实验七 JSP内置对象II 目的: 1、掌握JSP内置对象的使用。 2、理解JSP的作用域 3、掌握session,application对象的使用 实验要求: 1、完成实验题目 2、要求提交实验报告,将代码和实验结果页面截图放入报告中 实验过程:…...
【Leetcode 每日一题 - 补卡】219. 存在重复元素 II
问题背景 给你一个整数数组 n u m s nums nums 和一个整数 k k k,判断数组中是否存在两个 不同的索引 i i i 和 j j j,满足 n u m s [ i ] n u m s [ j ] nums[i] nums[j] nums[i]nums[j] 且 ∣ i − j ∣ < k |i - j| < k ∣i−j∣<…...
Flask数据的增删改查(CRUD)_flask删除数据自动更新
查询年龄小于17的学生信息 Student.query.filter(Student.s_age < 17) students Student.query.filter(Student.s_age.__lt__(17))模糊查询,使用like,查询姓名中第二位为花的学生信息 like ‘_花%’,_代表必须有一个数据,%任何数据 st…...
web自动化——前端知识
<iframe> 是 HTML 中的一个元素,用于在当前网页中嵌入另一个网页或文档。它就像一个“窗口”,可以在页面中显示其他内容。 主要特点: 嵌入外部内容:可以在网页中嵌入其他网页、视频、地图等。独立上下文:嵌入的…...
计算机网络一点事(22)
地址解析协议ARP ARP:查询Mac地址 ARP表(ARP缓存):记录映射关系,一个数据结构,定期更新ARP表 过程:请求分组,响应分组 动态主机配置协议DHCP 分配IP地址,配置默认网关…...