C++ AVL树手撕代码实现

使用C++实现AVL树模板 今天练习编写了一下AVL树。参考了Weiss的数据结构与算法C++描述版。上一个实现的一个一般的二叉搜索树,在使用的过程中可能会慢慢变得不平衡,这样很可能会降低查找、插入等等的效率,因此我们需要使用算法来实现树的平衡。AVL树是一种比较经典的平衡二叉搜索树,它规定每个节点的左子树和右子树的高度差最多为1,默认空树的高度为-1,这样能保证树的深度为O(logN),而且除了遍历,赋值,基本上所有树的操作都可以以时间O(logN)执行(当然插入和删除后因为要重新平衡树,可能时间会长于O(logN))。(Ps,上面 阅读详情

一、AVL树的旋转规则

1.AVL树概述(示意图)

AVL树是为了解决BST(二叉搜索树)树在一些情况下树的形状退化成线性链表而诞生的一种树。它有着BST树的特点(中序遍历是顺序的),但是有新的特点:对于每个节点来说,左右子树的高度差小于等于1.因此,AVL树是高度平衡的二叉搜索树。为了保持平衡,在四种情况下,树的结构要进行变形。这四种情况分别为:

  1. 左孩子的左子树太高
  2. 右孩子的右子树太高
  3. 左孩子的右子树太高
  4. 右孩子的左子树太高

在发生上面四种情况时,则需要变形操作。变形的操作分为左旋和右旋。

  1. 通过左旋,能让情况2变成平衡。
  2. 通过右旋,能让情况1变成平衡。
  3. 通过左平衡(左旋加右旋),能让情况3平衡。
  4. 通过右平衡(右旋加左旋),能让情况4平衡。

下面是四种情况的示意图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 四种操作的代码:

2.1 左旋

// 左旋
	Node* leftRotate(Node* node) {
		Node* child = node->right;
		node->right = child->left;
		child->left = node;
		
		// 更新高度
		node->_height = max(height(node->left), height(node->right)) + 1;
		child->_height = max(height(child->left), height(child->right)) + 1;

		return child;
	}

2.2 右旋

//右旋
	Node* rightRoate(Node* node) {
		Node* child = node->left;
		node->left = child->right;
		child->right = node;

		// 更新高度
		node->_height = max(height(node->left), height(node->right)) + 1;
		child->_height = max(height(child->left), height(child->right)) + 1;

		return child;
	}

2.3 左平衡(左旋+右旋)

//左平衡: 左孩子的右子树太高
	Node* leftBalance(Node* node) {
		node->left = leftRotate(node->left);
		return rightRoate(node);
	}

2.4 右平衡

//右平衡:右孩子的左子树太高
	Node* rightBalance(Node* node) {
		node->right = leftRotate(node->right);
		return rightRoate(node);
	}

二、AVL树的实现

AVL树的重点在于插入和删除操作。

1.插入操作

  1. AVL树的插入和BST树的插入一样,都是通过深度遍历来实现的。不同的地方是AVL树在每层递归结束之后,都会检查当前的节点是否满足高度平衡这个条件。如果不满足,就使用左右旋转或左右平衡来让当前节点平衡。

2.插入操作代码

// 内部的插入操作
	Node* insert(Node* node, const int& val) {
		if (node == nullptr) {
			return new Node(val);
		}

		if (node->_val > val) {
			node->left = insert(node->left, val);
			// 调整高度
			if (height(node->left) - height(node->right) > 1) {
				if (height(node->left->left) >= height(node->left->right)) {
					node = rightRoate(node);
				} else {
					node = leftBalance(node);
				}
			}
		} else if (node->_val < val) {
			node->right = insert(node->right, val);
			// 调整高度
			if (height(node->right) - height(node->left) > 1) {
				if (height(node->right->right) >= height(node->right->left)) {
					node = leftRotate(node);
				} else {
					node = rightBalance(node);
				}
			}
		} else {
			; //已经有该节点了,不做任何操作
		}

		node->_height = max(height(node->left), height(node->right)) + 1;

		return node;	
	}

3.删除操作

AVL树的删除是通过二分搜索实现的。在找到需要删除的节点后,可以分为三种情况:

  1. 待删除的节点有左右孩子
  2. 待删除的节点只有左孩子
  3. 待删除的节点只有右孩子

如果删除的节点同时有左右孩子,那么为了减少旋转操作:我们可以找出待删除节点究竟是左子树比较高还是右子树比较高。如果是左子树比较高,我们就把待删除节点的前驱节点(最后一个大于待删除节点的节点)数据覆盖到待删除节点上,再删除前驱节点。反之,我们就把待删除节点的后继节点(第一个大于待删除节点的节点)覆盖到待删除节点上,再删除后继节点。

而如果只有左孩子或者右孩子的话,就比较简单:待删除节点释放后,把左(右)孩子返回即可。

完成了删除操作之后,我们在每层回溯时来检查每个节点的左右子树高度是否平衡。如果不平衡,就使用左右旋转或左右平衡来让当前节点平衡。

4.删除操作代码

// 内部的删除操作
	Node* remove(Node* node, const int& val) {
		if (node == nullptr) return nullptr;

		if (node->_val > val) {
			node->left = remove(node->left, val); 
			// 已经删除完毕,在回溯中检查左子树有没有因为删除而失衡
			if (height(node->right) - height(node->left) > 1) {
				if (height(node->right->right) >= height(node->right->left)) {
					node = leftRotate(node);
				} else {
					node = rightBalance(node);
				}
			}
		} else if (node->_val < val) {
			node->right = remove(node->right, val);
			// 已经删除完毕,在回溯中检查右子树有没有因为删除而失衡
			if (height(node->left) - height(node->right) > 1) {
				if (height(node->left->left) >= height(node->left->right)) {
					node = rightRoate(node);
				} else {
					node = leftBalance(node);
				}
			}
		} else {
			// 1.存在左右孩子
			if (node->left && node->right) {
				// 如果左子树比右子树高就用前驱补到node的位置
				if (height(node->left) >= height(node->right)) {
					Node* pre = node->left;
					while (pre->right != nullptr) {
						pre = pre->right;
					}
					// 将前驱的数据覆盖到当前节点中
					node->_val = pre->_val;
					// 删除前驱
					node->left = remove(node->left, pre->_val);
				} else {
					// 如果左子树比右子树矮就用后继补到node的位置
					Node* post = node->right;
					while (post->left != nullptr) {
						post = post->left;
					}
					// 将后继的数据覆盖到node的位置
					node->_val = post->_val;
					// 删除后继
					node->right = remove(node->right, post->_val);
				}
			} else {
				// 2.只存在左孩子
				if (node->left != nullptr) {
					Node* tmp = node->left;
					delete node;
					return tmp;
				}
				// 3.只存在右孩子
				if (node->right != nullptr) {
					Node* tmp = node->right;
					delete node;
					return tmp;
				}
				// 4.没有左孩子和没有右孩子
				if (node->left == nullptr && node->right == nullptr) {
					delete node;
					return nullptr;
				}
			}
		}

		node->_height = max(height(node->left), height(node->right)) + 1;
		
		return node;
	}

3、完整实现

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;

struct Node {
	Node* left;
	Node* right;
	int _val;
	int _height;
	Node(int val) : _val(val), _height(1), left(nullptr), right(nullptr) {}
};

class AVLTree {
private:
	
	Node* _root;

	// 得到树的高度
	int height(Node* node) {
		return node ? node->_height : 0;
	}

	// 左旋
	Node* leftRotate(Node* node) {
		Node* child = node->right;
		node->right = child->left;
		child->left = node;
		
		// 更新高度
		node->_height = max(height(node->left), height(node->right)) + 1;
		child->_height = max(height(child->left), height(child->right)) + 1;

		return child;
	}

	//右旋
	Node* rightRoate(Node* node) {
		Node* child = node->left;
		node->left = child->right;
		child->right = node;

		// 更新高度
		node->_height = max(height(node->left), height(node->right)) + 1;
		child->_height = max(height(child->left), height(child->right)) + 1;

		return child;
	}

	//左平衡: 左孩子的右子树太高
	Node* leftBalance(Node* node) {
		node->left = leftRotate(node->left);
		return rightRoate(node);
	}

	//右平衡:右孩子的左子树太高
	Node* rightBalance(Node* node) {
		node->right = leftRotate(node->right);
		return rightRoate(node);
	}

	// 内部的插入操作
	Node* insert(Node* node, const int& val) {
		if (node == nullptr) {
			return new Node(val);
		}

		if (node->_val > val) {
			node->left = insert(node->left, val);
			// 调整高度
			if (height(node->left) - height(node->right) > 1) {
				if (height(node->left->left) >= height(node->left->right)) {
					node = rightRoate(node);
				} else {
					node = leftBalance(node);
				}
			}
		} else if (node->_val < val) {
			node->right = insert(node->right, val);
			// 调整高度
			if (height(node->right) - height(node->left) > 1) {
				if (height(node->right->right) >= height(node->right->left)) {
					node = leftRotate(node);
				} else {
					node = rightBalance(node);
				}
			}
		} else {
			; //已经有该节点了,不做任何操作
		}

		node->_height = max(height(node->left), height(node->right)) + 1;

		return node;	
	}

	// 内部的删除操作
	Node* remove(Node* node, const int& val) {
		if (node == nullptr) return nullptr;

		if (node->_val > val) {
			node->left = remove(node->left, val); 
			// 已经删除完毕,在回溯中检查左子树有没有因为删除而失衡
			if (height(node->right) - height(node->left) > 1) {
				if (height(node->right->right) >= height(node->right->left)) {
					node = leftRotate(node);
				} else {
					node = rightBalance(node);
				}
			}
		} else if (node->_val < val) {
			node->right = remove(node->right, val);
			// 已经删除完毕,在回溯中检查右子树有没有因为删除而失衡
			if (height(node->left) - height(node->right) > 1) {
				if (height(node->left->left) >= height(node->left->right)) {
					node = rightRoate(node);
				} else {
					node = leftBalance(node);
				}
			}
		} else {
			// 1.存在左右孩子
			if (node->left && node->right) {
				// 如果左子树比右子树高就用前驱补到node的位置
				if (height(node->left) >= height(node->right)) {
					Node* pre = node->left;
					while (pre->right != nullptr) {
						pre = pre->right;
					}
					// 将前驱的数据覆盖到当前节点中
					node->_val = pre->_val;
					// 删除前驱
					node->left = remove(node->left, pre->_val);
				} else {
					// 如果左子树比右子树矮就用后继补到node的位置
					Node* post = node->right;
					while (post->left != nullptr) {
						post = post->left;
					}
					// 将后继的数据覆盖到node的位置
					node->_val = post->_val;
					// 删除后继
					node->right = remove(node->right, post->_val);
				}
			} else {
				// 2.只存在左孩子
				if (node->left != nullptr) {
					Node* tmp = node->left;
					delete node;
					return tmp;
				}
				// 3.只存在右孩子
				if (node->right != nullptr) {
					Node* tmp = node->right;
					delete node;
					return tmp;
				}
				// 4.没有左孩子和没有右孩子
				if (node->left == nullptr && node->right == nullptr) {
					delete node;
					return nullptr;
				}
			}
		}

		node->_height = max(height(node->left), height(node->right)) + 1;
		
		return node;
	}

public:
	AVLTree() : _root(nullptr) {}
	
	// 得到头部节点
	Node* getHead() {
		return _root;
	}

	// 用户调用的插入操作
	void insert(const int& val) {
		_root = insert(_root, val);
	}

	// 用户调用的删除操作
	void remove(const int& val) {
		_root = remove(_root, val);
	}

	

};


void printTree(Node* root) {
	queue<Node*> que;
	if (root) que.push(root);

	while (!que.empty()) {
		int size = que.size();

		for (int i = 0; i < size; ++i) {
			Node* cur = que.front(); que.pop();
			cout << cur->_val << " ";

			if (cur->left) que.push(cur->left);
			if (cur->right) que.push(cur->right);
		}
		cout << endl;
	}
}


int main() {
	
	{	
		// 测试左旋
		AVLTree tree;
		for (int i = 0; i < 10; ++i) {
			tree.insert(i);
		}
		
		// 打印二叉树
		Node* head = tree.getHead();
		printTree(head);
		
		// 测试删除操作
		tree.remove(7);
		tree.remove(4);
		tree.remove(5);
		tree.remove(6);
		head = tree.getHead();
		printTree(head);

	}
	// 测试右旋
	/*{
		AVLTree tree;
		for (int i = 10; i >= 0; --i) {
			tree.insert(i);
		}

		Node* head = tree.getHead();
		printTree(head);
	}*/

	return 0;
}
利用Solidworks2020将挖掘机装配体转换为URDF模型 利用Solidworks2020将挖掘机装配体转换为URDF模型一、下载sw_urdf_exporter插件 URDF(Universal Robot Description Format)——通用机器人描述格式,它是ROS里边使用的一种机器人的描述文件,包含的内容有:连杆、关节,运动学和动力学参数、可视化模型、碰撞检测模型等。 一、下载sw_urdf_exporter插件 ROS提供了SolidWorks转URDF的插件(下载链接)—sw2urdfSetup.exe,下载后点击安装,安装完成后就能在sol 阅读详情

相关推荐

SAP MM | 采购的自动寻源:货源清单(ME01)与配额协议(MEQ1)的应用与 MRP 逻辑

SAP MM模块中,当物料有多个供应商时,MRP生成的采购申请常因"固定供应商"字段为空而无法自动发单。系统寻源优先级为:配额协议>货源清单>采购信息记录。解决方案包括:1)货源清单指定唯一固定供应商;2)配额协议按比例精准拆分;3)配额协议长期分配更推荐。手工创建采购申请时配额协议无效。建议优先使用货源清单Fix标识或配额分配功能,避免拆分导致的单据碎片化。

RiverRia的专栏 826

C++之 AVL树,手撕代码!

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树。在先左后右调节平衡因子的时候比较复杂,因为subL,subR,ptr的平衡因子情况是都要进行改变的。因为插入了节点那么自插入节点之上的节点的平衡因子是一定要改变的那么我们就可以分为多种情况。

flyingcloud6的博客 611

保姆级教程:手把手教你用KITTI数据集跑通LIO-SAM(附EVO评估全流程)

本文提供了一份详细的KITTI数据集LIO-SAM实现教程,涵盖数据准备、算法适配和EVO评估全流程。通过解析KITTI数据集特性、解决LIO-SAM适配问题,并利用EVO工具进行精度评估,帮助读者快速掌握激光惯性SLAM技术。特别针对KITTI数据与LIO-SAM的兼容性问题提供了实用解决方案。

weixin_28737603的博客 303

【C++】手撕AVL树

相信大家肯定听过在C++大名鼎鼎的两颗树,这两颗树分别是AVL树和红黑树,学过的小伙伴听到都是瑟瑟发抖,像一些大厂中可能会考手撕AVL树或红黑树。学习这两棵树确实难度很大,正所谓难度越大动力就越大,那本篇我们学习这两棵树的一颗树--AVL树。⭐主体。

AAlykk的博客 2017

四、Burpsuite工具之proxy模块详解-intercept功能

过了一个年,感觉好久都没有更新了,今天就从burpsuite的更新开始吧。前面已经说过了burpsuite的安装和proxy代理的配置,今天说一下proxy模块中非常有用的intercept功能。intercept是拦截的意思,在这里相当于拦截请求,可以实时的对请求进行拦截,然后请求参数的篡改,再把请求放通,从而达到实时修改请求的功能intercept的总体界面如下图所示其中下图红框中是可以修改请求目标的相关信息。

SmileAssassn的博客 1663

一种基于因果干预的少样本学习的故障诊断模型

本文提出CIRNet,一种融合元学习与因果干预的少样本故障诊断框架。针对传统方法易学习伪相关特征的问题,CIRNet通过构建结构因果模型,采用类级和特征级双调整策略消除先验知识干扰。实验在多个轴承/齿轮数据集上验证其有效性,在跨组件任务中准确率最高达93.41%,特征可视化显示更好的类可分性。相比现有方法,CIRNet显著提升了模型泛化能力和可解释性。

hasakie的博客 955

手撕 AVL 树——C++ 高阶数据结构详解

带你一命通关 AVL 树 十五分钟手撕数据结构原理

qq_61500888的博客 3023

STM32堆栈溢出问题及解决方法

堆栈是用于存储函数调用和局部变量的内存区域,它是一种先进后出的数据结构。如果函数嵌套层级过深或者局部变量占用的内存空间过大,堆栈的大小可能不足以容纳这些信息,从而导致堆栈溢出。在一些特殊情况下,可以使用堆内存替代堆栈,以避免堆栈溢出问题。在STM32的工程中,可以通过修改链接脚本文件(.ld文件)中的堆栈大小参数来增加堆栈空间。通常,堆栈大小设置为RAM的一小部分,但需要根据具体应用的需求进行调整。堆栈溢出是嵌入式系统开发中常见的问题之一,特别是在使用STM32微控制器时。3.3 使用堆内存替代堆栈。

ZwcCsharp的博客 2593

【C++】手搓AVL树

带你逐步手搓AVL树!

有梦想的电信狗 1609

【图像超分】论文精读:Interpreting Super-Resolution Networks with Local Attribution Maps(LAM)

论文题目:Interpreting Super-Resolution Networks with Local Attribution Maps —— 用局部归因图解释超分辨率网络CVPR 2021!必读文章!必须会的可视化手段!很多文章中都会对比LAM!图像超分辨率 (SR) 技术发展迅速,受益于深度网络的发明及其连续突破。然而,众所周知,深度学习和深度神经网络很难解释。SR网络继承了这种神秘的性质,很少有作品试图理解它们。在本文中,我们对 SR 网络进行了归因分析。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 547

swl安装无法与服务器建立链接,如何解决?

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 备注:部分问题/疑难杂症搜集于互联网。

**My Coding Family** 2075

C++手撕AVL树

C++代码手撕AVL树,包含插入删除查找等。

Talon的博客 1226

【图像超分】论文精读:SRConvNet: A Transformer-Style ConvNet for Lightweight Image Super-Resolution

本文提出了一种轻量级图像超分辨率重建方法SRConvNet,结合了卷积神经网络和Transformer的优势。针对传统Transformer计算复杂度高的问题,设计了傅立叶调制注意力(FMA)替代自注意力机制,通过区域频率空间调制实现高效的全局依赖建模;同时引入动态混合层(DML),采用混合尺度动态卷积结合通道拆分机制增强局部特征提取。实验表明,SRConvNet在计算效率和参数数量上显著优于现有轻量级超分方法,同时保持了优异的重建性能。该方法为移动端部署提供了有效的解决方案,在精度与复杂度间取得了良好平衡

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 807

C++ AVL树(四种旋转,插入)

C++ AVL树(四种旋转,插入)

Wzs040810的博客 3513

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

AVL树手撕,超详细图文详解

摘要:《AVL树的结构与平衡操作》探讨了AVL树的节点结构及插入过程中的平衡维护机制。AVL树通过平衡因子(_bf)和父指针(_parent)确保平衡,节点结构包含键值对(_kv)和左右子树指针。插入时通过比较键值确定位置,随后更新平衡因子:右子树插入使_bf++,左子树插入使_bf--。当_bf为±2时触发旋转(左单旋/右单旋)以恢复平衡,旋转需处理子树链接并更新父节点关系。代码示例展示了节点定义、插入逻辑及旋转实现,强调通过调整子树高度差维持全局平衡性。关键词:AVL树、平衡因子、旋转、节点插入、数据结

asciiletters的博客 2078

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

AVL树 -- C++实现

/平衡因子 balance factorint _bf;

qq_72916130的博客 1874

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

★ C++进阶篇 ★ AVL树实现

★ C++进阶篇 ★ AVL树实现

2302_80328146的博客 2156

C++:AVL树

带你了解平衡二叉树之间的魅力

HZzzzzLu的博客 2598

【手撕STL】AVL树

AVL树AVL树的概念AVL树的旋转三级目录 AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,找到了解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。 AVL树的性质: 它的左右子树都是AVL树 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1) 如果一棵二叉搜索树是高

AI_ELF的博客 986

C++笔记:从零开始一步步手撕高阶数据结构AVL树

这篇博客探究了如何从无到有实现一个AVL树已经如何验证AVL树的平衡性

ljh1257的博客 2577

【C++】AVL树实现

本文详细介绍了AVL树的概念、实现及其自平衡机制。AVL树是一种自平衡二叉搜索树,通过控制左右子树的高度差来保证查找效率。文章首先介绍了AVL树的基本概念和平衡因子的作用,接着详细讲解了AVL树的插入操作,包括平衡因子的更新和四种旋转操作(右单旋、左单旋、左右双旋、右左双旋)的实现。此外,文章还介绍了AVL树的查找、平衡检测和删除操作,并通过代码示例展示了如何实现这些功能。

x_p96484685433的博客 1812

【C++】AVL树

大家都愿意盲从,好像世界上最安全的事就是让自己消失在多数之中。 

hsy1603914691的博客 2504

手撕AVL树——从失衡到平衡,彻底搞懂旋转机制

AVL树是最早发明的自平衡二叉搜索树,掌握它对理解平衡二叉树至关重要。本文从零开始,用最通俗的语言讲透AVL树的核心概念:什么是平衡因子?插入为什么会失衡?四种旋转(左旋/右旋/左右双旋/右左双旋)到底怎么转?更重要的是,我会带你一步步实现完整的C++代码,并在关键处配图解析。无论你是备战面试还是夯实基础,这篇文章都能帮你把AVL树吃得透透的!

燃于AC之乐的博客 921

C++之AVL树的介绍以及AVL树自我实现

AVL树是一种自平衡的二叉查找树,它的名字来源于它的发明者G.M. Adelson-Velsky和E.M. Landis。AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右⼦树都是AVL树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树,通过控制⾼度差去控制平衡。

zzh_Zao的博客 1819

C++学习之旅【C++AVL树的实现】

本文全面解析了C++中AVL树的实现原理与应用。AVL树是一种高度平衡的二叉搜索树,通过平衡因子控制左右子树高度差不超过1,确保操作效率为O(logN)。文章详细介绍了AVL树的概念、结构定义、插入过程及平衡因子更新机制,重点分析了四种旋转处理情况(左单旋、右单旋、左右双旋、右左双旋)来维护平衡性。通过图示和代码示例,帮助读者深入理解AVL树的自平衡特性及其在C++中的具体实现方法。

承于所学,探技术之渊;以代码求政,寻成长之道。 1143
上一篇: C++ 手写智能指针 shared_ptr
下一篇: 分布式项目学习笔记(一):protobuf 的安装以及使用
zsiming
博客等级 码龄7年 43粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值