C++进阶——AVL树的实现

news2025/3/18 14:56:40

1、AVL的概念

1.1 AVL 树的发明

AVL 树由 G.M. Adelson-Velsky 和 E.M. Landis 在 1962 年的论文《An algorithm for the organization of information》中提出。他们的设计目标是解决二叉搜索树在动态操作(插入、删除)中可能退化为链表的问题。

1.2 AVL 树的定义

AVL 树是一种自平衡二叉搜索树,满足以下性质:

  • 它是一棵空树者:

    • 它的左右子树是 AVL 树

    • 左右子树的高度差(平衡因子)的绝对值 <= 1

1.3 平衡因子

平衡因子(Balance Factor)是 AVL 树的核心概念:

  • 定义:某个节点平衡因子 = 右子树的高度 - 左子树的高度

  • 平衡因子取值范围-101

  • 如果某个节点的平衡因子的绝对值超过 1,则该节点不平衡,需要通过旋转操作进行调整。

1.4 为什么高度差 <= 1 ?

高度差为 0 表示左右子树高度相等,这种理想状态在某些情况下无法实现的。

  • 例如:

    • 当树的节点数为 2 时,高度差只能为 1。

    • 当树的节点数为 4 时,高度差也只能为 1。

  • 如果强制要求高度差为 0,会导致树的结构过于严格,难以在动态操作中保持平衡。

1. 5 AVL 树的性能

AVL 树的性能优势主要体现在其高度平衡性:

  • 高度控制AVL 树高度 始终保持 O(log⁡N)

  • 操作效率插入删除查找等操作的时间复杂度均为 O(log⁡N)

2、AVL树的实现

2.1 AVL树的结构

	template<class K,class V>
	struct AVLTreeNode
	{
		pair<K, V> _kv;
		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;

		int _bf; // balance factor

		AVLTreeNode(const pair<K,V>& kv)
			:_kv(kv)
			,_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
		{ }
	};

	template<class K,class V>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> Node;
	public:
        // ……
    private:
        Node* _root = nullptr;
    }

2.2 AVL树的插入

2.2.1 AVL树插入一个值的大概过程

1. 按二叉搜索树规则进行插入一个值

2. 新增结点只会影响祖先(或部分祖先)结点的高度,也就是会影响祖先(或部分祖先)结点的平衡因子,所以更新 从新增结点->根结点路径上的平衡因子。实际中最坏情况下要更新到根,有些情况更新到中间就可以结束了。

3. 更新平衡因子过程中出现不平衡(平衡因子的绝对值超过 1),对不平衡子树 旋转,旋转后本质调平衡的同时,降低了子树的高度,不会再影响上一层,所以插入结束。

2.2.2 平衡因子的更新
更新原则:

1. 平衡因子的定义

平衡因子 = 右子树高度 - 左子树高度

2. 影响平衡因子的条件

只有子树高度变化 才会影响当前节点的平衡因子

3. 插入节点对平衡因子的影响

  • 如果新增节点parent右子树parent的平衡因子++

  • 如果新增节点parent左子树parent的平衡因子--

更新情况:

1. 更新后parent的平衡因子 等于 0

因为插入前,就是平衡搜索树,平衡因子只能是-1,0,1,所以一定是从-1或1变成0,子树整体的

高度不变结束更新

2. 更新后parent的平衡因子 等于 1-1

一定是从0变成-1或1,子树整体的高度变化继续更新

3. 更新后parent的平衡因子等于 2 -2

一定是从-1变成-2或1变成2,parent高的子树更高了,不平衡了。

通过旋转平衡(即降低树的高度),并使子树的根平衡因子为0

-1->-2(旋转)->0,或1->2(旋转)->0,相当于子树的高度没变结束更新

4. 更新到根节点

如果更新到根节点,且根节点的平衡因子为 1 -1结束更新。因为根的parent为nullptr,不用更

新了。

2.2.3 插入的代码实现
		bool Insert(const pair<K, V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}

			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (kv.first > cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kv.first < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(kv);
			if (kv.first > parent->_kv.first)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			cur->_parent = parent;

			// 更新平衡因子
			while (parent) // parent = nullptr,为根节点的父亲,结束更新
			{
				if (cur == parent->_left)
					--parent->_bf;
				else
					++parent->_bf;

				if (parent->_bf == 0)
				{
					break;
				}
				else if (parent->_bf == -1 || parent->_bf == 1)
				{
					cur = parent;
					parent = cur->_parent;
				}
				else if (parent->_bf == -2 || parent->_bf == 2)
				{
                    // 旋转
					break;
				}
				else
				{
					assert(false);
				}
			}

			return true;
		}

2.3 旋转

2.3.1 旋转的原则

1. 保持搜索树的规则

2. 让旋转的树从不平衡变平衡,其次降低旋转树的高度

旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。

2.3.2 右单旋

即左侧高,右侧的parent旋下来。平衡因子同号。

a,b,c子树为抽象表示,实际上有非常多种情况。

a子树自己不旋转,高度+1subLRparent的左,再将parentsubL的右

注意:

h = 0时,subLRnullptr

parent如果是局部子树的根,就改变连接关系,如果是,就改变根

2.3.3 右单旋的代码实现
		void RotateR(Node* parent)
		{
			Node* pParent = parent->_parent;
			Node* subL = parent->_left;
			Node* subLR = subL->_right;

			parent->_left = subLR;
			if (subLR)
				subLR->_parent = parent;

			subL->_right = parent;
			parent->_parent = subL;
			subL->_parent = pParent;
			if (pParent == nullptr) // 当pParent == nullptr时,_root == parent
			{
				_root = subL;
			}
			else
			{
				if (pParent->_left == parent)
					pParent->_left = subL;
				else
					pParent->_right = subL;
			}

			subL->_bf = 0;
			parent->_bf = 0;
		}
2.3.4 左单旋

即右侧高,左侧的parent旋下来。平衡因子同号。

a子树自己不旋转,高度+1subRLparent的右,再将parentsubR的左

注意:

h = 0时,subRLnullptr

parent如果是局部子树的根,就改变连接关系,如果是,就改变根

2.3.5 左单旋的代码实现
		void RotateL(Node* parent)
		{
			Node* pParent = parent->_parent;
			Node* subR = parent->_right;
			Node* subRL = subR->_left;

			parent->_right = subRL;
			if (subRL)
				subRL->_parent = parent;

			subR->_left = parent;
			parent->_parent = subR;
			subR->_parent = pParent;
			if (pParent == nullptr)
				_root = subR;
			else
			{
				if (pParent->_left == parent)
					pParent->_left = subR;
				else
					pParent->_right = subR;
			}

			subR->_bf = 0;
			parent->_bf = 0;
		}
2.3.6 左右双旋

即中间高,右侧的parent旋下来。平衡因子异号。

从过程上,是对subL进行左旋parent进行右旋

从结果上,subLRsubLsubLRparentsubLR自己做

subLRsubLparent

平衡后分三种情况,即有三种情况的平衡因子

2.3.7 左右双旋的代码实现
		void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int _bf = subLR->_bf;

			RotateL(subL);
			RotateR(parent);

			if (_bf == 0)
			{
				parent->_bf = 0;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (_bf == 1)
			{
				parent->_bf = 0;
				subL->_bf = -1;
				subLR->_bf = 0;
			}
			else if (_bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}
2.3.8 右左双旋

即中间高,左侧的parent旋下来。平衡因子异号。

从过程上,是对subR进行右旋parent进行左旋

从结果上,subRLparentsubRLsubRsubRL自己做

subRLparentsubR

平衡后分三种情况,即有三种情况的平衡因子

2.3.9 右左双旋的代码实现
		void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int _bf = subRL->_bf;

			RotateR(subR);
			RotateL(parent);

			if (_bf == 0)
			{
				parent->_bf = 0;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (_bf == 1)
			{
				parent->_bf = -1;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (_bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

2.4 AVL树的查找

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_kv.first)
					cur = cur->_right;
				else if (key < cur->_kv.first)
					cur = cur->_left;
				else
					return cur;
			}
			return nullptr;
		}

2.5 AVL树的平衡检测

		bool _IsBalanceTree(Node* root, int& height) { // 后序
			// 空树是 AVL 树
			if (nullptr == root) {
				height = 0;
				return true;
			}

			// 递归检查左子树
			int leftHeight = 0;
			bool isLeftBalanced = _IsBalanceTree(root->_left, leftHeight);

			// 递归检查右子树
			int rightHeight = 0;
			bool isRightBalanced = _IsBalanceTree(root->_right, rightHeight);

			// 当前节点的高度
			height = max(leftHeight, rightHeight) + 1;

			// 计算平衡因子
			int diff = rightHeight - leftHeight;

			// 检查平衡因子是否异常
			if (abs(diff) >= 2) {
				cout << root->_kv.first << " 高度差异常" << endl;
				return false;
			}

			// 检查当前节点的平衡因子是否正确
			if (root->_bf != diff) {
				cout << root->_kv.first << " 平衡因子异常" << endl;
				return false;
			}

			// 如果左右子树都是 AVL 树,且当前节点也平衡,则整棵树是 AVL 树
			return isLeftBalanced && isRightBalanced;
		}

2.6 AVL树的删除

2.7 AVL的代码实现

2.7.1 AVLTree.h
#pragma once

#include <iostream>
#include <assert.h>

using namespace std;

namespace Lzc
{
	template<class K,class V>
	struct AVLTreeNode
	{
		pair<K, V> _kv;
		AVLTreeNode<K, V>* _left;
		AVLTreeNode<K, V>* _right;
		AVLTreeNode<K, V>* _parent;

		int _bf; // balance factor

		AVLTreeNode(const pair<K,V>& kv)
			:_kv(kv)
			,_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
		{ }
	};

	template<class K,class V>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> Node;
	public:
		bool Insert(const pair<K, V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}

			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (kv.first > cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kv.first < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(kv);
			if (kv.first > parent->_kv.first)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			cur->_parent = parent;

			// 更新平衡因子
			while (parent)
			{
				if (cur == parent->_left)
					--parent->_bf;
				else
					++parent->_bf;

				if (parent->_bf == 0)
				{
					break;
				}
				else if (parent->_bf == -1 || parent->_bf == 1)
				{
					cur = parent;
					parent = cur->_parent;
				}
				else if (parent->_bf == -2 || parent->_bf == 2)
				{
					if (parent->_bf == -2 && cur->_bf == -1)
						RotateR(parent);
					else if (parent->_bf == 2 && cur->_bf == 1)
						RotateL(parent);
					else if (parent->_bf == -2 && cur->_bf == 1)
						RotateLR(parent);
					else if (parent->_bf == 2 && cur->_bf == -1)
						RotateRL(parent);
					else
						assert(false);

					break;
				}
				else
				{
					assert(false);
				}
			}

			return true;
		}

		void RotateR(Node* parent)
		{
			Node* pParent = parent->_parent;
			Node* subL = parent->_left;
			Node* subLR = subL->_right;

			parent->_left = subLR;
			if (subLR)
				subLR->_parent = parent;

			subL->_right = parent;
			parent->_parent = subL;
			subL->_parent = pParent;
			if (pParent == nullptr) // 当pParent == nullptr时,_root == parent
			{
				_root = subL;
			}
			else
			{
				if (pParent->_left == parent)
					pParent->_left = subL;
				else
					pParent->_right = subL;
			}

			subL->_bf = 0;
			parent->_bf = 0;
		}

		void RotateL(Node* parent)
		{
			Node* pParent = parent->_parent;
			Node* subR = parent->_right;
			Node* subRL = subR->_left;

			parent->_right = subRL;
			if (subRL)
				subRL->_parent = parent;

			subR->_left = parent;
			parent->_parent = subR;
			subR->_parent = pParent;
			if (pParent == nullptr)
				_root = subR;
			else
			{
				if (pParent->_left == parent)
					pParent->_left = subR;
				else
					pParent->_right = subR;
			}

			subR->_bf = 0;
			parent->_bf = 0;
		}

		void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int _bf = subLR->_bf;

			RotateL(subL);
			RotateR(parent);

			if (_bf == 0)
			{
				parent->_bf = 0;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (_bf == 1)
			{
				parent->_bf = 0;
				subL->_bf = -1;
				subLR->_bf = 0;
			}
			else if (_bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

		void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int _bf = subRL->_bf;

			RotateR(subR);
			RotateL(parent);

			if (_bf == 0)
			{
				parent->_bf = 0;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (_bf == 1)
			{
				parent->_bf = -1;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (_bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else
			{
				assert(false);
			}
		}

		void InOrder()
		{
			_InOrder(_root);
		}

		int Height()
		{
			return _Height(_root);
		}

		int Size()
		{
			return _Size(_root);
		}

		bool IsBalanceTree()
		{
			int height = 0;
			return _IsBalanceTree(_root, height);
		}

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_kv.first)
					cur = cur->_right;
				else if (key < cur->_kv.first)
					cur = cur->_left;
				else
					return cur;
			}
			return nullptr;
		}

	private:
		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_InOrder(root->_right);
		}

		int _Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			int LeftHeight = _Height(root->_left);
			int RightHeight = _Height(root->_right);

			return LeftHeight > RightHeight ? LeftHeight + 1 : RightHeight + 1;
		}

		int _Size(Node* root)
		{
			if (root == nullptr)
				return 0;
			return _Size(root->_left) + _Size(root->_right) + 1;
		}

		bool _IsBalanceTree(Node* root, int& height) { // 后序
			// 空树是 AVL 树
			if (nullptr == root) {
				height = 0;
				return true;
			}

			// 递归检查左子树
			int leftHeight = 0;
			bool isLeftBalanced = _IsBalanceTree(root->_left, leftHeight);

			// 递归检查右子树
			int rightHeight = 0;
			bool isRightBalanced = _IsBalanceTree(root->_right, rightHeight);

			// 当前节点的高度
			height = max(leftHeight, rightHeight) + 1;

			// 计算平衡因子
			int diff = rightHeight - leftHeight;

			// 检查平衡因子是否异常
			if (abs(diff) >= 2) {
				cout << root->_kv.first << " 高度差异常" << endl;
				return false;
			}

			// 检查当前节点的平衡因子是否正确
			if (root->_bf != diff) {
				cout << root->_kv.first << " 平衡因子异常" << endl;
				return false;
			}

			// 如果左右子树都是 AVL 树,且当前节点也平衡,则整棵树是 AVL 树
			return isLeftBalanced && isRightBalanced;
		}

		Node* _root = nullptr;
	};
}
2.7.2 Test.cpp
#include "AVLTree.h"
#include <vector    >

namespace Lzc
{
    void TestAVLTree1() {
        AVLTree<int, int> t;
        // 常规的测试用例
         int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
        // 特殊的带有双旋场景的测试用例
        // int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
        for (auto e : a) {
            t.Insert({ e, e });
        }
        t.InOrder();
        cout << t.IsBalanceTree() << endl;
    }

    // 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
    void TestAVLTree2() {
        const int N = 100000;
        vector<int> v;
        v.reserve(N);
        srand(time(0));
        for (size_t i = 0; i < N; i++) {
            v.push_back(rand() + i);
        }

        // 测试插入性能
        size_t begin2 = clock();
        AVLTree<int, int> t;
        for (auto e : v) {
            t.Insert(make_pair(e, e));
        }
        size_t end2 = clock();
        cout << "Insert:" << end2 - begin2 << endl;

        // 测试平衡性、高度和大小
        cout << "IsBalanceTree:" << t.IsBalanceTree() << endl;
        cout << "Height:" << t.Height() << endl;
        cout << "Size:" << t.Size() << endl;

        // 测试查找性能
        size_t begin1 = clock();
        // 查找已存在的值
        /*for (auto e : v) {
            t.Find(e);
        }*/

        // 查找随机值
        for (size_t i = 0; i < N; i++) {
            t.Find((rand() + i));
        }
        size_t end1 = clock();
        cout << "Find:" << end1 - begin1 << endl;
    }
}

int main()
{
     Lzc::TestAVLTree1();
     // Lzc::TestAVLTree2();
	return 0;
}

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

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

相关文章

打包当前Ubuntu镜像 制作Ubuntu togo系统

我的系统的基本情况说明&#xff1a; 我原来的系统的具体型号如下&#xff1a; uname -rLinux Engine 5.15.0-134-generic #145~20.04.1-Ubuntu SMP Mon Feb 17 13:27:16 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux我原来的硬盘以及分区策略如下&#xff1a; 可以看到我的分区…

系统架构设计师—案例分析—架构设计

文章目录 经典架构风格对比面向对象架构风格/显示调用风格优点缺点举例 事件驱动的系统/隐式调用风格优点缺点举例 基于规则的系统架构风格优点缺点举例 管道过滤器风格优点缺点举例 仓库风格优点缺点举例 解释器风格优点缺点举例 分层架构风格优点缺点举例 经典架构风格对比 …

基于javaweb的SpringBoot智能相册管理系统图片相册系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

Android 14 Telephony 网络选择功能介绍

一、总体介绍 (一)功能 手动搜网的流程:用户通过UI触发,调用TelephonyManager的API,比如startNetworkScan,然后这个请求会传递到RIL层,通过AT命令与基带通信,进行网络扫描。结果返回后,经过TelephonyRegistry通知应用层。中间可能涉及IPC,比如Binder通信,因为应用和…

深入解析音频编解码器(Audio CODEC):硬件、接口与驱动开发

音频编解码器&#xff08;Audio CODEC&#xff09;是音频处理系统中的核心组件&#xff0c;负责 模拟信号与数字信号的相互转换&#xff0c;广泛应用于 智能音箱、嵌入式系统、消费电子产品 等设备。本篇文章将从 硬件结构、接口解析、驱动开发 和 软件配置 等方面&#xff0c;…

深度学习【迭代梯度下降法求解线性回归】

梯度下降法 梯度下降法是一种常用迭代方法&#xff0c;其目的是让输入向量找到一个合适的迭代方向&#xff0c;使得输出值能达到局部最小值。在拟合线性回归方程时&#xff0c;我们把损失函数视为以参数向量为输入的函数&#xff0c;找到其梯度下降的方向并进行迭代&#xff0…

[Lc14_priority_queue] 最后一块石头重量 | 数据流中的第 K 大元素 | 前K个高频单词 | 数据流的中位数

目录 1.最后一块石头的重量 题解 2.数据流中的第 K 大元素 题解 3.前K个高频单词 题解 代码 ⭕4.数据流的中位数 题解 在C中&#xff0c;使用标准库中的priority_queue&#xff0c;默认情况下它是一个最大堆&#xff08;即大堆排序&#xff09;&#xff0c;这意味着最…

熔断和降级的区别,具体使用场景有哪些?

熔断与降级的核心区别在于触发条件和应用目标&#xff0c;具体差异及使用场景如下&#xff1a; 一、核心区别 对比维度熔断降级触发原因下游依赖服务故障&#xff08;如超时、异常率过高&#xff09;触发系统整体负载过高或流量洪峰管理目标层级框架级保护&#xff08;无业务优…

利用hexo+github部署属于自己的个人博客网站(2025年3月所写)

利用hexogithub部署属于自己的个人博客网站 前情提要&#xff1a;如果你出现了莫名其妙的报错&#xff0c;可能与权限有关&#xff0c;可以以管理员的身份运行git bash或者cmd 本篇博客仅限于利用hexo搭建博客&#xff0c;并且部署到github上面&#xff0c;让自己可以有一个访…

pandas学习笔记(一)——基础知识和应用案例

pandas学习笔记 基础语法参考菜鸟教程&#xff1a;https://www.runoob.com/pandas/pandas-tutorial.html # jupyter import pandas as pd import matplotlib from matplotlib import pyplot as plt import numpy as npmatplotlib.use(TkAgg)data {timestamp: [1, 2, 3, 4, 5…

【AI 大模型】RAG 检索增强生成 ⑤ ( 向量数据库 | 向量数据库 索引结构和搜索算法 | 常见 向量数据库 对比 | 安装并使用 向量数据库 chromadb 案例 )

文章目录 一、向量数据库1、向量数据库引入2、向量数据库简介3、向量数据库 索引结构和搜索算法4、向量数据库 应用场景5、传统数据库 与 向量数据库 对比 二、常见 向量数据库 对比三、向量数据库 案例1、安装 向量数据库 chromadb2、核心要点 解析① 创建数据库实例② 创建数…

解决single cell portal点击下载但跳转的是网页

Single cell RNA-seq of Tmem100-lineage cells in a mouse model of osseointegration - Single Cell Portal 想下载个小鼠数据集&#xff1a; 点击下载跳转为网页&#xff1a; 复制bulk download给的链接无法下载 bulk download给的原链接&#xff1a; curl.exe "http…

基于 Prometheus + Grafana 监控微服务和数据库

以下是基于 Prometheus Grafana 监控微服务和数据库的详细指南&#xff0c;包含架构设计、安装配置及验证步骤&#xff1a; 一、整体架构设计 二、监控微服务 1. 微服务指标暴露 Spring Boot 应用&#xff1a; xml <!-- 添加 Micrometer 依赖 --> <dependency>…

CAN总线的CC帧和FD帧之间如何仲裁

为满足CAN总线日益提高的带宽需求&#xff0c;博世公司于2012年推出CAN FD&#xff08;具有灵活数据速率的CAN&#xff09;标准&#xff0c;国际标准化组织&#xff08;ISO&#xff09;2015年通过ISO 11898-1:2015标准&#xff0c;正式将CAN FD纳入国际标准&#xff0c;以示区别…

SpringBoot 第一课(Ⅲ) 配置类注解

目录 一、PropertySource 二、ImportResource ①SpringConfig &#xff08;Spring框架全注解&#xff09; ②ImportResource注解实现 三、Bean 四、多配置文件 多Profile文件的使用 文件命名约定&#xff1a; 激活Profile&#xff1a; YAML文件支持多文档块&#xff…

Excel(函数篇):COUNTIF与CONUTIFS函数、SUMIF与SUMIFS函数、ROUND函数、MATCH与INDEX函数、混合引用与条件格式

目录 COUNTIF和COUNTIFS函数COUNTIF函数COUNTIFS函数SUMIF和SUMIFS函数SUMIF函数SUMIFS函数SUMIFS函数与控件实现动态年月汇总ROUND、ROUNDUP、ROUNDDOWN函数单元格混合引用条件格式与公式,标记整行数据MATCH和INDEX函数COUNTIF和COUNTIFS函数 COUNTIF函数 统计下“苏州”出现…

虚拟定位 1.2.0.2 | 虚拟定位,上班打卡,校园跑步模拟

Fake Location是一款运行于安卓平台上的功能强大、简单实用的虚拟定位软件。它能够帮助用户自定义位置到地图上的任意地方&#xff0c;以ROOT环境运行不易被检测&#xff0c;同时也支持免ROOT运行。提供路线模拟、步频模拟、WIFI模拟等方式&#xff0c;支持反检测。 大小&…

【最大异或和——可持久化Trie】

题目 代码 #include <bits/stdc.h> using namespace std;const int N 6e510; //注意这里起始有3e5&#xff0c;又可能插入3e5 const int M N * 25;int rt[N], tr[M][2]; //根&#xff0c;trie int idx, cnt, br[M]; //根分配器&#xff0c;点分配器&#xff0c;点的相…

C# WPF编程-启动新窗口

C# WPF编程-启动新窗口 新建窗口&#xff1a; 工程》添加》窗口 命名并添加新的窗口 这里窗口名称为Window1.xaml 启动新窗口 Window1 win1 new Window1(); win1.Show(); // 非模态启动窗口win1.ShowDialog(); // 模态启动窗口 模态窗口&#xff1a;当一个模态窗口被打开时&a…

Python 实现大文件的高并发下载

项目背景 基于一个 scrapy-redis 搭建的分布式系统&#xff0c;所有item都通过重写 pipeline 存储到 redis 的 list 中。这里我通过代码演示如何基于线程池 协程实现对 item 的中文件下载。 Item 结构 目的是为了下载 item 中 attachments 保存的附件内容。 {"crawl_tim…