AVL、B树和B+树

AVL树定义

AVL树(Adelson-Velsky 和 Landis 树)是一种自平衡的二叉搜索树(Binary Search Tree, BST),由苏联数学家Georgy Adelson-Velsky和Evgenii Landis在1962年提出。AVL树通过在每个节点上维护一个平衡因子(Balance Factor),确保树的高度始终保持在对数级别,从而保证查找、插入和删除操作的时间复杂度为O(log N)。

主要特性
  1. 二叉搜索树性质

    • 每个节点的左子树中所有节点的值均小于该节点的值。
    • 每个节点的右子树中所有节点的值均大于该节点的值。
  2. 高度平衡

    • 对于每个节点,其左子树和右子树的高度差(平衡因子)最多为1,即:
      ∣ 左子树高度 − 右子树高度 ∣ ≤ 1 | \text{左子树高度} - \text{右子树高度} | \leq 1 左子树高度右子树高度1
    • 通过旋转操作在插入或删除节点后保持树的平衡。
  3. 平衡因子

    • 每个节点维护一个平衡因子,定义为其左子树的高度减去右子树的高度:
      平衡因子 = 左子树高度 − 右子树高度 \text{平衡因子} = \text{左子树高度} - \text{右子树高度} 平衡因子=左子树高度右子树高度
    • 平衡因子的取值范围为-1、0、+1。
      在这里插入图片描述
旋转操作

当插入或删除节点导致某个节点的平衡因子超出范围时,需要通过旋转操作来恢复AVL树的平衡。主要的旋转操作包括:

  1. 单右旋(Right Rotation)

    • 用于解决左-左(LL)失衡的情况。
  2. 单左旋(Left Rotation)

    • 用于解决右-右(RR)失衡的情况。
  3. 双旋转(Left-Right Rotation 和 Right-Left Rotation)

    • 用于解决左-右(LR)和右-左(RL)失衡的情况。
优点
  • 高效的查找性能:由于严格的平衡条件,AVL树的查找、插入和删除操作的时间复杂度均为O(log N)。
  • 动态平衡:插入和删除操作后自动调整,保持树的平衡,避免退化为链表结构。
缺点
  • 维护复杂性:插入和删除操作后可能需要进行多次旋转,增加了实现的复杂性。
  • 额外的内存消耗:每个节点需要存储平衡因子或高度信息,增加了内存开销。
应用场景

AVL树适用于需要频繁查找、插入和删除操作的动态数据集,如:

  • 数据库索引:保证高效的数据检索和更新。
  • 内存中的符号表:如编译器中的符号表管理。
  • 实时系统:需要快速响应的应用场景。
示例

以下是一个简单的AVL树节点结构的示例(以C语言为例):

typedef struct AVLNode {int key;struct AVLNode *left;struct AVLNode *right;int height;
} AVLNode;

在这个结构中,每个节点包含一个键值、指向左子节点和右子节点的指针,以及存储该节点高度的字段。

总结

AVL树通过严格的高度平衡条件,确保了二叉搜索树的操作效率,特别适合需要频繁动态更新和高效查找的应用场景。尽管在实现上比普通的二叉搜索树更为复杂,但其在性能上的优势使其在许多系统中得到了广泛应用。

B树

早上好!我很乐意帮你修改文本,让我们添加适当的LaTeX标记。

B树的结构与性质分析

  1. 节点的孩子数
  • 每个节点最多有 m m m 个子节点( m ≥ 2 m \geq 2 m2)。
  • 除根节点和叶子节点外,其他每个非叶子节点必须至少有 ⌈ m / 2 ⌉ \lceil m / 2 \rceil m/2 个孩子。
  • ⌈ m / 2 ⌉ \lceil m / 2 \rceil m/2:表示向上取整的函数,它确保了树的平衡性。例如,对于 m = 5 m = 5 m=5,最少需要 ⌈ 5 / 2 ⌉ = 3 \lceil 5 / 2 \rceil = 3 5/2=3 个子节点。
  1. 根节点的特殊情况
  • 根节点必须至少有2个孩子,除非根节点是叶子节点。在这种情况下,整棵树只有根节点,它既是叶子节点也是根节点。
  1. 叶子节点的特性
  • 所有叶子节点都出现在同一层级:这确保了树的平衡性,所有查找操作的路径长度一致,从而提高查找效率。
  • 叶子节点不包含关键字信息:叶子节点通常不包含实际的关键字数据,可能包含数据的指针或者是表示查询失败的空指针。实际上,这些节点依然存在,只是它们没有指向其他子节点的指针。
  1. 非叶子节点的结构
    每个非叶子节点包含若干个关键字( n n n个)和指向子树的指针( P 0 , P 1 , . . . , P n P_0, P_1, ..., P_n P0,P1,...,Pn)。具体结构如下:
  • 关键字:每个非叶子节点包含有 n n n 个关键字 K 1 , K 2 , . . . , K n K_1, K_2, ..., K_n K1,K2,...,Kn,且这些关键字按升序排列,即 K i − 1 < K i K_{i-1} < K_i Ki1<Ki i = 1 , 2 , . . . , n i = 1, 2, ..., n i=1,2,...,n)。
  • 子树指针:每个非叶子节点有 n + 1 n+1 n+1 个子树指针 P 0 , P 1 , . . . , P n P_0, P_1, ..., P_n P0,P1,...,Pn,每个子树指针 P i P_i Pi 指向一个子树,且子树中的所有关键字都小于 K i K_i Ki 并大于 K i − 1 K_{i-1} Ki1(对于 i = 1 i = 1 i=1,指针 P 0 P_0 P0 指向所有小于 K 1 K_1 K1 的关键字)。
  1. 关键字的个数
  • 对于每个非根节点(即非叶子节点),包含的关键字数 n n n 必须满足以下条件: ⌈ m / 2 ⌉ − 1 ≤ n ≤ m − 1 \lceil m / 2 \rceil - 1 \leq n \leq m - 1 m/21nm1

这意味着每个节点的关键字数至少是 ⌈ m / 2 ⌉ − 1 \lceil m / 2 \rceil - 1 m/21,而最多是 m − 1 m - 1 m1,从而控制了节点的密度,防止树的某部分过于稀疏或过于密集,保证了树的平衡。


图示结构说明

假设我们考虑一个3阶( m = 3 m = 3 m=3)B树的例子。按照您给出的规则,3阶B树的结构会是这样的:

  • 节点最多含有2个子节点
  • 每个非根节点至少含有1个子节点
  • 每个节点最多含有2个关键字
  • 所有叶子节点都在同一层。

例如,一棵3阶B树的具体结构如下:

[30]
/ \
[10,20] [40,50]

其中:

  • 根节点包含一个关键字 30 30 30 和两个子树指针。
  • 每个子树的根节点(如 [ 10 , 20 ] [10,20] [10,20] [ 40 , 50 ] [40,50] [40,50])分别包含两个关键字,并且每个子树都至少有 1 个子节点。
  • 所有叶子节点( [ 10 , 20 ] [10,20] [10,20] [ 40 , 50 ] [40,50] [40,50])都在同一层,且没有子节点,通常这些叶子节点会存储实际的数据(在实际应用中可能是指向数据记录的指针)。

B树(B-Tree)实现示例

下面将以 Python 语言实现一个 B树,遵循您提供的详细定义和属性。此实现包括节点结构、插入和搜索操作。由于删除操作相对复杂,为了保持示例简洁,本文主要集中在插入和搜索功能上。

1. B树的基本结构

首先,定义B树节点的结构。每个节点包含以下部分:

  • keys:存储节点中的关键字,按照升序排列。
  • children:指向子节点的指针列表。
  • leaf:布尔值,指示节点是否为叶子节点。

2. B树类的实现

以下是B树的完整实现,包括插入和搜索功能:

import mathclass BTreeNode:def __init__(self, t, leaf=False):"""初始化B树节点:param t: 最小度数(ceil(m/2)):param leaf: 是否为叶子节点"""self.t = t  # 最小度数self.keys = []  # 存储关键字self.children = []  # 存储子节点self.leaf = leaf  # 是否为叶子节点def __str__(self):return f'Keys: {self.keys}, Leaf: {self.leaf}'class BTree:def __init__(self, m):"""初始化B树:param m: 阶数"""if m < 3:raise ValueError("阶数m必须大于等于3")self.m = m  # 阶数self.t = math.ceil(m / 2)  # 最小度数self.root = BTreeNode(self.t, leaf=True)def search(self, k, x=None):"""在B树中搜索关键字k:param k: 关键字:param x: 当前节点:return: 节点和关键字的位置"""if x is None:x = self.rooti = 0while i < len(x.keys) and k > x.keys[i]:i += 1if i < len(x.keys) and k == x.keys[i]:return (x, i)elif x.leaf:return Noneelse:return self.search(k, x.children[i])def insert(self, k):"""向B树中插入关键字k:param k: 关键字"""root = self.rootif len(root.keys) == self.m - 1:# 根节点已满,需要分裂s = BTreeNode(self.t, leaf=False)s.children.insert(0, self.root)self.split_child(s, 0)self.root = sself.insert_non_full(s, k)else:self.insert_non_full(root, k)def insert_non_full(self, x, k):"""在非满节点x中插入关键字k:param x: 当前节点:param k: 关键字"""i = len(x.keys) - 1if x.leaf:# 插入到叶子节点x.keys.append(None)  # 占位while i >= 0 and k < x.keys[i]:x.keys[i + 1] = x.keys[i]i -= 1x.keys[i + 1] = kelse:# 找到子节点while i >= 0 and k < x.keys[i]:i -= 1i += 1if len(x.children[i].keys) == self.m - 1:# 子节点已满,分裂self.split_child(x, i)if k > x.keys[i]:i += 1self.insert_non_full(x.children[i], k)def split_child(self, x, i):"""分裂子节点x.children[i]:param x: 父节点:param i: 子节点的索引"""t = self.ty = x.children[i]z = BTreeNode(t, leaf=y.leaf)# 分裂y的keys和children到zz.keys = y.keys[t:]y.keys = y.keys[:t - 1]if not y.leaf:z.children = y.children[t:]y.children = y.children[:t]# 插入z到x的childrenx.children.insert(i + 1, z)# 将中间键上移到xx.keys.insert(i, y.keys.pop(-1))def traverse(self, x=None, depth=0):"""遍历B树并打印:param x: 当前节点:param depth: 当前深度"""if x is None:x = self.rootprint("  " * depth + str(x))if not x.leaf:for child in x.children:self.traverse(child, depth + 1)def delete(self, k):"""删除B树中的关键字k:param k: 关键字"""self.delete_internal(self.root, k)# 如果根节点的关键字为空,调整根if len(self.root.keys) == 0 and not self.root.leaf:self.root = self.root.children[0]def delete_internal(self, x, k):"""在节点x中删除关键字k:param x: 当前节点:param k: 关键字"""t = self.ti = 0while i < len(x.keys) and k > x.keys[i]:i += 1if i < len(x.keys) and k == x.keys[i]:if x.leaf:# 情况1: k在叶子节点x中x.keys.pop(i)else:# 情况2: k在非叶子节点x中y = x.children[i]z = x.children[i + 1]if len(y.keys) >= t:pred = self.get_predecessor(y)x.keys[i] = predself.delete_internal(y, pred)elif len(z.keys) >= t:succ = self.get_successor(z)x.keys[i] = succself.delete_internal(z, succ)else:self.merge(x, i)self.delete_internal(y, k)elif not x.leaf:# 情况3: k不在节点x中,且x不是叶子节点flag = (i == len(x.keys))if len(x.children[i].keys) < t:self.fill(x, i)if flag and i > len(x.keys):self.delete_internal(x.children[i - 1], k)else:self.delete_internal(x.children[i], k)def get_predecessor(self, x):"""获取节点x的前驱关键字:param x: 当前节点:return: 前驱关键字"""while not x.leaf:x = x.children[-1]return x.keys[-1]def get_successor(self, x):"""获取节点x的后继关键字:param x: 当前节点:return: 后继关键字"""while not x.leaf:x = x.children[0]return x.keys[0]def fill(self, x, i):"""确保x.children[i]有至少t个关键字:param x: 当前节点:param i: 子节点索引"""t = self.tif i != 0 and len(x.children[i - 1].keys) >= t:self.borrow_from_prev(x, i)elif i != len(x.children) - 1 and len(x.children[i + 1].keys) >= t:self.borrow_from_next(x, i)else:if i != len(x.children) - 1:self.merge(x, i)else:self.merge(x, i - 1)def borrow_from_prev(self, x, i):"""从x.children[i-1]借一个关键字到x.children[i]:param x: 当前节点:param i: 子节点索引"""child = x.children[i]sibling = x.children[i - 1]# 移动x的keychild.keys.insert(0, x.keys[i - 1])if not child.leaf:child.children.insert(0, sibling.children.pop(-1))x.keys[i - 1] = sibling.keys.pop(-1)def borrow_from_next(self, x, i):"""从x.children[i+1]借一个关键字到x.children[i]:param x: 当前节点:param i: 子节点索引"""child = x.children[i]sibling = x.children[i + 1]# 移动x的keychild.keys.append(x.keys[i])if not child.leaf:child.children.append(sibling.children.pop(0))x.keys[i] = sibling.keys.pop(0)def merge(self, x, i):"""合并x.children[i]和x.children[i+1]:param x: 当前节点:param i: 子节点索引"""child = x.children[i]sibling = x.children[i + 1]t = self.t# 移动x的key到childchild.keys.append(x.keys.pop(i))# 合并sibling的keys到childchild.keys.extend(sibling.keys)# 合并sibling的子节点到childif not child.leaf:child.children.extend(sibling.children)# 移除siblingx.children.pop(i + 1)# 示例使用
if __name__ == "__main__":# 创建一个阶数为4的B树(每个节点最多有3个关键字和4个子节点)btree = BTree(m=4)# 插入关键字keys_to_insert = [10, 20, 5, 6, 12, 30, 7, 17]for key in keys_to_insert:btree.insert(key)print(f"插入关键字 {key} 后的B树结构:")btree.traverse()print("-" * 50)# 搜索关键字search_keys = [6, 15]for key in search_keys:result = btree.search(key)if result:node, idx = resultprint(f"找到关键字 {key} 在节点: {node}, 索引位置: {idx}")else:print(f"关键字 {key} 不存在于B树中。")# 删除关键字keys_to_delete = [6, 13, 7, 4, 2, 16]for key in keys_to_delete:print(f"\n删除关键字 {key} 后的B树结构:")btree.delete(key)btree.traverse()print("-" * 50)

3. 代码详解

3.1 BTreeNode 类
class BTreeNode:def __init__(self, t, leaf=False):self.t = t  # 最小度数self.keys = []  # 存储关键字self.children = []  # 存储子节点self.leaf = leaf  # 是否为叶子节点def __str__(self):return f'Keys: {self.keys}, Leaf: {self.leaf}'
  • t(最小度数):根据阶数 ( m ),最小度数 ( t = \lceil m/2 \rceil )。
  • keys:关键字列表,始终保持有序。
  • children:子节点列表。
  • leaf:布尔值,指示节点是否为叶子节点。
3.2 BTree 类
class BTree:def __init__(self, m):if m < 3:raise ValueError("阶数m必须大于等于3")self.m = m  # 阶数self.t = math.ceil(m / 2)  # 最小度数self.root = BTreeNode(self.t, leaf=True)
  • m(阶数):每个节点最多有 ( m-1 ) 个关键字和 ( m ) 个子节点。
  • t(最小度数):每个节点至少有 ( t-1 ) 个关键字(根节点除外)。
  • root(根节点):初始时为叶子节点。
3.3 插入操作

插入操作分为两部分:

  1. insert 方法:处理根节点是否需要分裂的情况。
  2. insert_non_full 方法:在非满节点中插入关键字。
  3. split_child 方法:分裂满节点。
def insert(self, k):root = self.rootif len(root.keys) == self.m - 1:# 根节点已满,需要分裂s = BTreeNode(self.t, leaf=False)s.children.insert(0, self.root)self.split_child(s, 0)self.root = sself.insert_non_full(s, k)else:self.insert_non_full(root, k)
  • 如果根节点已满,则创建一个新的根节点,并分裂原根节点。
def insert_non_full(self, x, k):i = len(x.keys) - 1if x.leaf:# 插入到叶子节点x.keys.append(None)  # 占位while i >= 0 and k < x.keys[i]:x.keys[i + 1] = x.keys[i]i -= 1x.keys[i + 1] = kelse:# 找到子节点while i >= 0 and k < x.keys[i]:i -= 1i += 1if len(x.children[i].keys) == self.m - 1:# 子节点已满,分裂self.split_child(x, i)if k > x.keys[i]:i += 1self.insert_non_full(x.children[i], k)
  • 叶子节点:直接插入关键字,保持有序。
  • 非叶子节点:找到合适的子节点,若子节点已满,则先分裂,再递归插入。
def split_child(self, x, i):t = self.ty = x.children[i]z = BTreeNode(t, leaf=y.leaf)# 分裂y的keys和children到zz.keys = y.keys[t:]y.keys = y.keys[:t - 1]if not y.leaf:z.children = y.children[t:]y.children = y.children[:t]# 插入z到x的childrenx.children.insert(i + 1, z)# 将中间键上移到xx.keys.insert(i, y.keys.pop(-1))
  • 将满的子节点 y 分裂为两个节点 yz,并将中间关键字上移到父节点 x
3.4 搜索操作
def search(self, k, x=None):if x is None:x = self.rooti = 0while i < len(x.keys) and k > x.keys[i]:i += 1if i < len(x.keys) and k == x.keys[i]:return (x, i)elif x.leaf:return Noneelse:return self.search(k, x.children[i])
  • 从根节点开始,递归搜索关键字 k
  • 若找到,返回包含关键字的节点和索引;否则,返回 None
3.5 遍历操作
def traverse(self, x=None, depth=0):if x is None:x = self.rootprint("  " * depth + str(x))if not x.leaf:for child in x.children:self.traverse(child, depth + 1)
  • 以层级形式遍历并打印B树结构,便于可视化。
3.6 删除操作

删除操作较为复杂,涉及多种情况的处理。以下是删除关键字的主要方法:

def delete(self, k):self.delete_internal(self.root, k)# 如果根节点的关键字为空,调整根if len(self.root.keys) == 0 and not self.root.leaf:self.root = self.root.children[0]
  • 调用内部方法 delete_internal 删除关键字。
  • 若删除后根节点无关键字且不是叶子节点,则更新根节点。
def delete_internal(self, x, k):t = self.ti = 0while i < len(x.keys) and k > x.keys[i]:i += 1if i < len(x.keys) and k == x.keys[i]:if x.leaf:# 情况1: k在叶子节点x中x.keys.pop(i)else:# 情况2: k在非叶子节点x中y = x.children[i]z = x.children[i + 1]if len(y.keys) >= t:pred = self.get_predecessor(y)x.keys[i] = predself.delete_internal(y, pred)elif len(z.keys) >= t:succ = self.get_successor(z)x.keys[i] = succself.delete_internal(z, succ)else:self.merge(x, i)self.delete_internal(y, k)elif not x.leaf:# 情况3: k不在节点x中,且x不是叶子节点flag = (i == len(x.keys))if len(x.children[i].keys) < t:self.fill(x, i)if flag and i > len(x.keys):self.delete_internal(x.children[i - 1], k)else:self.delete_internal(x.children[i], k)
  • 情况1:关键字在叶子节点中,直接删除。
  • 情况2:关键字在非叶子节点中,找到前驱或后继关键字替代后递归删除。
  • 情况3:关键字不在当前节点中,递归到合适的子节点删除,确保节点至少有 t 个关键字。

其他辅助方法包括获取前驱、后继、借用关键字以及合并节点等。

4. 示例运行

if __name__ == "__main__":# 创建一个阶数为4的B树(每个节点最多有3个关键字和4个子节点)btree = BTree(m=4)# 插入关键字keys_to_insert = [10, 20, 5, 6, 12, 30, 7, 17]for key in keys_to_insert:btree.insert(key)print(f"插入关键字 {key} 后的B树结构:")btree.traverse()print("-" * 50)# 搜索关键字search_keys = [6, 15]for key in search_keys:result = btree.search(key)if result:node, idx = resultprint(f"找到关键字 {key} 在节点: {node}, 索引位置: {idx}")else:print(f"关键字 {key} 不存在于B树中。")# 删除关键字keys_to_delete = [6, 13, 7, 4, 2, 16]for key in keys_to_delete:print(f"\n删除关键字 {key} 后的B树结构:")btree.delete(key)btree.traverse()print("-" * 50)
4.1 插入操作输出示例
插入关键字 10 后的B树结构:
Keys: [10], Leaf: True
--------------------------------------------------
插入关键字 20 后的B树结构:
Keys: [10, 20], Leaf: True
--------------------------------------------------
插入关键字 5 后的B树结构:
Keys: [5, 10, 20], Leaf: True
--------------------------------------------------
插入关键字 6 后的B树结构:
Keys: [5, 6, 10, 20], Leaf: True
--------------------------------------------------
插入关键字 12 后的B树结构:
Keys: [5, 6, 10, 12, 20], Leaf: True
--------------------------------------------------
插入关键字 30 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5, 6], Leaf: TrueKeys: [10, 12, 20, 30], Leaf: True
--------------------------------------------------
插入关键字 7 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5, 6, 7], Leaf: TrueKeys: [10, 12, 20, 30], Leaf: True
--------------------------------------------------
插入关键字 17 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5, 6, 7], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------
4.2 搜索操作输出示例
找到关键字 6 在节点: Keys: [5, 6, 7], Leaf: True, 索引位置: 1
关键字 15 不存在于B树中。
4.3 删除操作输出示例
删除关键字 6 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5, 7], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------删除关键字 13 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5, 7], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------删除关键字 7 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------删除关键字 4 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------删除关键字 2 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------删除关键字 16 后的B树结构:
Keys: [10, 20], Leaf: FalseKeys: [5], Leaf: TrueKeys: [10, 12, 17, 20, 30], Leaf: True
--------------------------------------------------

5. 代码功能总结

  • 节点分裂:当插入导致节点关键字数量超过最大限制时,节点被分裂,并将中间关键字上移到父节点。
  • 关键字插入:关键字被插入到合适的位置,保持节点内关键字有序。
  • 关键字搜索:通过递归搜索找到目标关键字所在的节点。
  • 关键字删除:处理多种删除情况,确保B树的平衡性,包括关键字的借用和节点的合并。

6. 扩展功能

为了全面实现B树,您可以考虑添加以下功能:

  • 删除操作的完整实现:当前实现已包含基本的删除逻辑,但在某些复杂情况下可能需要更多测试和调整。
  • 可视化工具:使用图形库如 graphviz 进行B树的可视化展示。
  • 批量插入与删除:优化插入和删除操作以支持批量处理,提高效率。

7. 参考资料

  • 《算法导论》(Introduction to Algorithms) - Cormen, Leiserson, Rivest, Stein
  • Wikipedia - B-tree

希望这个B树的Python实现能够帮助您更好地理解和应用B树数据结构。如有任何疑问或需要进一步的功能扩展,请随时提问!

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

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

相关文章

Unity ShaderLab 实现3D物体描边

实现思路&#xff1a; 给物体添加第二个材质球&#xff0c;在shader的顶点着色器中使顶点的位置变大&#xff0c;然后在片元着色器中输出描边颜色。 shader Graph实现如下&#xff1a; ShaderLab实现如下&#xff1a; Shader "Custom/Outline" {Properties{[HDR]_…

【C++第三方库】Muduo库结合ProtoBuf库搭建服务端和客户端的过程和源码

每日激励&#xff1a;“不设限和自我肯定的心态&#xff1a;I can do all things。 — Stephen Curry” 绪论​&#xff1a; 本章我将结合之前的这俩个第三方库快速上手protobuf序列化和反序列化框架和muduo网络&#xff0c;来去实现muduo库在protocol协议搭建服务端和客户端。…

Jenkins的使用

文章目录 一、Jenkins是什么\有什么用\与GitLab的对比二、Jenkins的安装与配置Jenkins的安装方式在Linux上安装Jenkins&#xff1a;在Windows上安装Jenkins&#xff1a;配置Jenkins&#xff1a; &#xff08;可选&#xff09;配置启动用户为root&#xff08;一定要是root吗??…

Qml-TabBar类使用

Qml-TabBar类使用 TabBar的概述 TabBar继承于Container 由TabButton进行填充&#xff0c;可以与提供currentIndex属性的任何容器或布局控件一起使用&#xff0c;如StackLayout 或 SwipeView&#xff1b;contentHeight : real:TabBar的内容高度&#xff0c;用于计算标签栏的隐…

Cyberchef 辅助网络安全运营-数据格式转换

在网络安全的世界中&#xff0c;经常会遇到各种格式的数据&#xff0c;比如二进制&#xff0c;比如说16进制&#xff0c;URL编码&#xff0c;HTML编码&#xff0c;Unicode编码&#xff0c;Base格式的编码。网络安全运营一个明确的目标就是把这些不同的数据格式换成为可读的字符…

C语言——指针初阶(一)

目录 一.什么是指针&#xff1f;&#xff1f;&#xff1f; 指针是什么&#xff1f; 指针变量&#xff1a; 总结&#xff1a; 总结&#xff1a; 二.指针和指针类型 指针-整数&#xff1a; 总结&#xff1a; 指针的解引用 总结&#xff1a; 三.野指针 如何规避野指针 往期…

Tcon技术和Tconless技术介绍

文章目录 TCON技术&#xff08;传统时序控制器&#xff09;定义&#xff1a;主要功能&#xff1a;优点&#xff1a;缺点&#xff1a; TCONless技术&#xff08;无独立时序控制器&#xff09;定义&#xff1a;工作原理&#xff1a;优点&#xff1a;缺点&#xff1a; TCON与TCONl…

World of Warcraft /script SetRaidTarget(“target“, n, ““) n=8,7,6,5,4,3,2,1,0

魔兽世界执行当前目标标记方法 /script SetRaidTarget("target", n, "") n8,7,6,5,4,3,2,1,0 解析这个lua脚本 D:\Battle.net\World of Warcraft\_classic_\Interface\AddOns\wMarker wMarker.lua /script SetRaidTarget("target", 8, &quo…

学习笔记035——MySQL索引

数据库索引 索引是为了提高数据的查询速度&#xff0c;相当于给数据进行编号&#xff0c;在查找数据的时候就可以通过编号快速找到对应的数据。 索引内部数据结构&#xff1a;B Tree 主键自带索引。 如&#xff1a; insert into user (id, name) values (1,f); insert int…

在Unity中实现物体动画的完整流程

在Unity中&#xff0c;动画是游戏开发中不可或缺的一部分。无论是2D还是3D游戏&#xff0c;动画都能为游戏增添生动的视觉效果。本文将详细介绍如何在Unity中为物体添加动画&#xff0c;包括资源的准备、播放组件的添加、动画控制器的创建以及动画片段的制作与调度。 1. 准备动…

Python数据分析实例五、US 大选捐款数据分析

美国联邦选举委员会 (FEC) 公布了对政治竞选活动的贡献数据。这包括投稿人姓名、职业和雇主、地址和投款金额。2012 年美国总统大选的贡献数据以单个 150 MB 的 CSV 文件P00000001-ALL.csv形式提供,该文件可以通过以下pandas.read_csv加载: import pandas as pdfec = pd.r…

vue3项目搭建-3-Pinia的使用

Pinia 是集中状态管理工具 基本用法 Pinia 是 Vue 的专属的最新状态管理库&#xff0c;是 Vuex 状态管理工具的替代品 官方文档&#xff1a;pinia官方文档 找到开始目录&#xff0c;根据文档安装和入门 pinia&#xff0c;启用一个新的终端&#xff0c;输入指令 npm install…

SAP开发语言ABAP开发入门

1. 了解ABAP开发环境和基础知识 - ABAP简介 - ABAP&#xff08;Advanced Business Application Programming&#xff09;是SAP系统中的编程语言&#xff0c;主要用于开发企业级的业务应用程序&#xff0c;如财务、物流、人力资源等模块的定制开发。 - 开发环境搭建 - 首先需…

修改bag的frame_id的工具srv_tools

在使用数据集导航或者建图时&#xff0c;bag中的点云或者其他话题的frame_id没有和需要的对应 1.创建工作空间 2.cd xxxx/src 3.git clone https://github.com/srv/srv_tools.git cd .. catkin_make source ./devel/setup.bash rosrun bag_tools change_frame_id.py -t /要改…

IDEA2023版本配置项目全局编码

IDEA默认的项目编码是UTF-8&#xff0c;有时候拿到别人的代码使用的编码是GBK&#xff0c;虽然可以在idea右下角进行修改&#xff0c;但是一个一个的修改太慢了。所以需要去进行该项目的编码全局配置。接下来直接讲步骤&#xff0c;以IDEA2023版本为例。 第一步 File>Sett…

大数据学习18之Spark-SQL

1.概述 1.1.简介 Spark SQL 是 Apache Spark 用于处理结构化数据的模块。 1.2.历史 1.2.1.Shark Hadoop诞生初期&#xff0c;Hive是唯一在Hadoop上运行的SQL-on-Hadoop工具&#xff0c;MR的中间计算过程产生了大量的磁盘落地操作&#xff0c;消耗了大量的I/O&#xff0c;降低…

【Android】Service使用方法:本地服务 / 可通信服务 / 前台服务 / 远程服务(AIDL)

1 本地Service 这是最普通、最常用的后台服务Service。 1.1 使用步骤 步骤1&#xff1a;新建子类继承Service类&#xff1a;需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法步骤2&#xff1a;构建用于启动Service的Intent对象步骤3&#xff1a;调用st…

QML学习 —— 34、视频媒体播放器(附源码)

效果 说明 您可以单独使用MediaPlayer播放音频内容(如音频),也可以将其与VideoOutput结合使用以渲染视频。VideoOutput项支持未转换、拉伸和均匀缩放的视频演示。有关拉伸均匀缩放演示文稿的描述,请参见fillMode属性描述。 播放可能出错问题 出现的问题:      DirectS…

Spring MVC练习(前后端分离开发实例)

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f439;今日诗词:二十五弦弹夜月&#xff0c;不胜清怨却飞来&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4…

如何把大模型调教成派大星?

目录 主要内容模型图实验结果如何把大模型变成派大星&#xff1f;chatglm3-6B 数据集准备代码运行微调结果 文章声明&#xff1a;非广告&#xff0c;仅个人体验&#xff1a;参考文献&#xff1a;https://www.aspiringcode.com/content?id17197387451937&uid291a2ae1546b48…