C++----红黑树map和set的封装

news2025/3/11 10:45:08

一、红黑树

1.概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。

2.性质

  1. 每个结点不是红色就是黑色 
  2. 根节点是黑色的  
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续的红色节点,可以有连续的黑色节点)
  4. 每条路径均包含相同数目的黑色结点  
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

3.红黑树 VS AVL树

红黑树 vs AVL 树核心对比:

1.平衡条件差异

  • AVL 树:严格平衡(任意节点左右子树高度差≤1)
  • 红黑树:弱平衡(最长路径≤2 倍最短路径)

2.高度特性

  • AVL 树:高度 h=O (log n)
  • 红黑树:高度 h=O (2 log n) → 实际测试中通常为 1.2-1.4 倍 AVL 树高度

性能差异分析:

  1. 操作复杂度对比

    操作类型AVL树红黑树
    查找O(h)O(h)
    插入O(h)O(h)
    删除O(h)O(h)
  2. 实际效率表现

  • 查找:AVL 树略优(高度更低)
  • 插入 / 删除:红黑树更优(旋转次数少)
  • 综合性能:红黑树在动态场景更高效

效率差异原因:

1.旋转成本对比

  • AVL 树:每次插入可能需多次旋转(平均 1.02 次)
  • 红黑树:最多 3 次旋转(平均 0.2 次)
  • 旋转操作对缓存不友好(破坏空间局部性)

2.常数因子影响

  • 红黑树节点需维护颜色字段(增加少量内存)
  • AVL 树需维护平衡因子(同样增加内存)
  • 红黑树的颜色检查比 AVL 树的平衡因子计算更高效

4.实现思路与代码

节点结构定义

// 定义颜色枚举类型,包含红色和黑色两种颜色
enum Colour
{
    RED,
    BLACK
};

// 定义红黑树节点的模板结构体
template<class K, class V>
struct RBTreeNode
{
    // 指向左子节点的指针
    RBTreeNode<K, V>* _left;
    // 指向右子节点的指针
    RBTreeNode<K, V>* _right;
    // 指向父节点的指针,用于回溯操作,方便在插入和删除时调整树的结构
    RBTreeNode<K, V>* _parent;

    // 存储键值对,键类型为K,值类型为V
    pair<K, V> _kv;
    // 节点的颜色,使用前面定义的枚举类型
    Colour _col;

    // 构造函数,用于创建新节点
    // 节点颜色初始化为红色,因为插入红色节点对红黑树性质的破坏相对较小
    RBTreeNode(const pair<K, V>& kv)
        :_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_kv(kv)
        ,_col(RED)
    {}
};

// 定义红黑树的模板结构体
template<class K, class V>
struct RBTree
{
    // 重定义节点类型,方便后续使用
    typedef RBTreeNode<K, V> Node;
public:
    // 这里可以添加插入、删除、查找等操作的函数声明和实现
    // ......

private:
    // 红黑树的根节点指针,初始化为空指针
    Node* _root = nullptr;

public:
    // 记录旋转操作的次数,用于调试和性能分析
    int _rotateCount = 0;
};

1. 颜色枚举类型 Colour

定义了两种颜色:红色(RED)和黑色(BLACK)。红黑树通过节点的颜色来维护树的弱平衡性质。

2. 节点结构体 RBTreeNode

  • 指针成员
    • _left 和 _right 分别指向左右子节点,用于构建树的层次结构。
    • _parent 指向父节点,这在插入和删除操作中非常重要,因为在调整树的结构时,需要回溯到父节点甚至更高层的节点来进行旋转和颜色调整。
  • 键值对 _kv:存储实际的数据,键类型为 K,值类型为 V。红黑树可以作为一种关联容器,通过键来快速查找对应的值。
  • 颜色成员 _col:节点的颜色,使用 Colour 枚举类型表示。新节点默认初始化为红色,因为插入红色节点时,只要父节点不是红色,就不会破坏红黑树的性质,相比插入黑色节点,调整的复杂度更低。

3. 红黑树结构体 RBTree

  • 根节点指针 _root:指向红黑树的根节点,初始化为空指针,表示空树。
  • 旋转计数变量 _rotateCount:用于记录旋转操作的次数。旋转操作是红黑树调整结构的关键步骤,通过旋转可以在不破坏红黑树性质的前提下,平衡树的高度,减少查找、插入和删除操作的时间复杂度。这个变量可以用于调试和性能分析,帮助我们了解红黑树在不同操作下的调整情况。

插入

代码

    bool Insert(const pair<K, V>& kv)
    {
        //空树处理
	    if (_root == nullptr)
	    {
		    _root = new Node(kv);
    		_root->_col = BLACK;
			return true;
		}

        //寻找插入位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

        //插入新节点
		cur = new Node(kv);
		cur->_col = RED;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

        //平衡调整(关键步骤)
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)//情况一:父节点是祖父的左孩子
			{
				Node* uncle = grandfather->_right;
				
				if (uncle && uncle->_col == RED)// 叔叔存在且为红
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 叔叔不存在 或 存在且为黑
				{
					if (cur == parent->_left)
					{
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//		c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // 情况二:父节点是祖父的右孩子
			{
				Node* uncle = grandfather->_left;
				// uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;// 确保根节点为黑

		return true;
	}

思路分析

1. 空树处理

  • 若根节点 _root 为空,直接创建新节点作为根,颜色设为黑色。

2. 寻找插入位置

  • 从根节点开始遍历,比较键值:

    • 若插入键 > 当前节点键,向右子树查找

    • 若插入键 < 当前节点键,向左子树查找

    • 若键已存在,返回插入失败

3. 插入新节点

  • 创建新节点(颜色为红色),链接到父节点的左/右子节点,并设置父指针。

4. 平衡调整(关键步骤)

循环条件:父节点存在且为红色(仅需处理父为红的情况)。

情况一:父节点是祖父的左孩子

  • 叔叔存在且为红

    1. 变色:父节点和叔叔变黑,祖父变红。

    2. 继续向上处理:将祖父设为当前节点(父节点、祖父节点也要调整),继续向上调整。

  • 叔叔不存在或为黑

    • 当前节点是父的左孩子(LL型)

      1. 右旋祖父节点。

      2. 父变黑,祖父变红。

    • 当前节点是父的右孩子(LR型)

      1. 先左旋父节点,转为 LL 型。

      2. 再右旋祖父节点。

      3. 当前节点变黑,祖父变红。

情况二:父节点是祖父的右孩子

  • 叔叔存在且为红:处理同情况一(变色后上移)。

  • 叔叔不存在或为黑

    • 当前节点是父的右孩子(RR型)

      1. 左旋祖父节点。

      2. 父变黑,祖父变红。

    • 当前节点是父的左孩子(RL型)

      1. 先右旋父节点,转为 RR 型。

      2. 再左旋祖父节点。

      3. 当前节点变黑,祖父变红。

5. 确保根节点为黑

  • 最终将根节点颜色设为黑色。

旋转操作

左单旋和右单旋

private:
    void RotateL(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


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

			}

			cur->_parent = ppnode;
		}
	}


	void RotateR(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_left;
		Node* curright = cur->_right;

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

		Node* ppnode = parent->_parent;
		cur->_right = parent;
		parent->_parent = cur;

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

			cur->_parent = ppnode;
		}
	}

红黑树的左单旋和右单旋跟AVL树的思路一样,唯一不同的是最后不需要调整平衡因子。

平衡验证

代码

private:
    bool CheckColour(Node* root, int blacknum, int benchmark)
	{
		if (root == nullptr)
		{
			if (blacknum != benchmark)路径黑色节点数是否一致。
				return false;

			return true;
		}

		if (root->_col == BLACK)
		{
			++blacknum;
		}

        //是否存在连续红色节点。
		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "出现连续红色节点" << endl;
			return false;
		}

		return CheckColour(root->_left, blacknum, benchmark)
			&& CheckColour(root->_right, blacknum, benchmark);
	}

    bool IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;

		if (root->_col != BLACK)//确保根节点为黑色。
		{
			return false;
		}

		// 统计最左路径黑色节点数作为基准值。
		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;

			cur = cur->_left;
		}

		return CheckColour(root, 0, benchmark);//调用 CheckColour 递归验证。
	}

public:
	bool IsBalance()
	{
		return IsBalance(_root);
	}

	

思路分析

1.IsBalance 入口函数:

  • 确保根节点为黑色。

  • 统计最左路径黑色节点数作为基准值。

  • 调用 CheckColour 递归验证。

2.CheckColour 递归检查:

  • 路径黑色节点数是否一致。

  • 是否存在连续红色节点。

二、map和set的封装实现

1.红黑树改造

1.1 节点结构改造

template<class T>
struct RBTreeNode {
    RBTreeNode<T>* _left;
    RBTreeNode<T>* _right;
    RBTreeNode<T>* _parent;
    T _data;          // 存储数据(set存key,map存pair)
    Colour _col;      // 节点颜色
    
    RBTreeNode(const T& data)
        : _data(data), _col(RED), _left(nullptr), _right(nullptr), _parent(nullptr) {}
};

设计要点

  • 使用模板参数T统一存储数据类型

  • set实例化时T=K,map实例化时T=pair<const K,V>

  • 保持红黑树基础结构(三叉链、颜色标记)

1.2 红黑树模板参数设计

template<class K, class T, class KeyOfT>
class RBTree {
    // K: 键值类型
    // T: 节点数据类型
    // KeyOfT: 仿函数,用于提取比较键值

    typedef RBTreeNode<T> Node;
public:
	// 同一个类模板,传的不同的参数实例化出的不同类型
	typedef __TreeIterator<T, T*, T&> iterator;
	typedef __TreeIterator<T, const T*, const T&> const_iterator;

    //...(省略插入、查找、删除等操作)
};

关键参数说明

参数set实例化map实例化
KKey类型Key类型
TKey类型pair<const Key, Value>
KeyOfTSetKeyOfT(返回自身)MapKeyOfT(返回pair.first)

1.3 仿函数实现细节

// Set侧的键值提取
struct SetKeyOfT {
    const K& operator()(const K& key) {
        return key;  // 直接返回Key本身
    }
};

// Map侧的键值提取
struct MapKeyOfT {
    const K& operator()(const pair<K, V>& kv) {
        return kv.first;  // 仅提取pair中的Key部分
    }
};

作用

  • 统一比较接口,避免直接比较pair类型的对象

  • 保证比较逻辑的一致性(只使用Key部分进行比较)

2.迭代器系统实现

2.1 迭代器核心结构

template<class T, class Ptr, class Ref>
struct __TreeIterator {
    typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T, Ptr, Ref> Self;

	typedef __TreeIterator<T, T*, T&> Iterator;
    Node* _node;

    // 支持普通迭代器构造const迭代器
    __TreeIterator(const __TreeIterator<T, T*, T&>& it)
        : _node(it._node) {}

    // 解引用操作符
    Ref operator*() { return _node->_data; }

    // 成员访问操作符
    Ptr operator->() { return &_node->_data; }

    bool operator!=(const Self& s) const{
        return _node != s._node;
    }

    bool operator==(const Self& s) const{
        return _node == s._node;
    }

    Self& operator--(){
        if (_node->_left){
            Node* subRight = _node->_left;
            while (subRight->_right){
                subRight = subRight->_right;
            }

            _node = subRight;
        }else{
            // 孩子是父亲的右的那个节点
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
    }

    // 前置++操作符
    Self& operator++() {
        if (_node->_right) {
            // 右子树存在,找右子树最左节点
            Node* subLeft = _node->_right;
            while (subLeft->_left) {
                subLeft = subLeft->_left;
            }
            _node = subLeft;
        } else {
            // 右子树不存在,向上查找祖先(下一个访问的是孩子父亲是左的那个祖先)
            Node* cur = _node;
            Node* parent = cur->_parent;
            while (parent && cur == parent->_right) {
                cur = parent;
                parent = parent->_parent;
            }
            _node = parent;
        }
        return *this;
    }
};

遍历特性

  • 实现红黑树的中序遍历(升序访问)

  • 时间复杂度:均摊O(1)

  • 空间复杂度:O(1)(无需栈结构)

2.2 迭代器在树中的应用

template<class K, class T, class KeyOfT>
class RBTree {
    typedef RBTreeNode<T> Node;
public:
	// 同一个类模板,传的不同的参数实例化出的不同类型
	typedef __TreeIterator<T, T*, T&> iterator;
	typedef __TreeIterator<T, const T*, const T&> const_iterator;

    iterator begin(){
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return iterator(leftMin);
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return const_iterator(leftMin);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

    //省略其他操作
    
};

2.3 容器迭代器定义 

// map侧迭代器定义
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

// set侧迭代器定义
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

关键差异

容器迭代器类型可修改性
map普通迭代器可修改value,不可修改key
setconst迭代器完全不可修改元素

3.插入操作深度解析

3.1 插入函数改造

pair<iterator, bool> Insert(const T& data)
{
    // [1] 空树处理
    if (_root == nullptr) {
        _root = new Node(data);
        _root->_col = BLACK;
        return make_pair(iterator(_root), true);
    }

    // [2] 查找插入位置
    Node* parent = nullptr;
    Node* cur = _root;
    KeyOfT kot;
    while (cur) {
        parent = cur;
        if (kot(cur->_data) < kot(data))
            cur = cur->_right;
        else if (kot(cur->_data) > kot(data))
            cur = cur->_left;
        else
            return make_pair(iterator(cur), false);
    }

    // [3] 创建新节点
    cur = new Node(data);
    cur->_col = RED;  // 新节点初始为红色
    // ... 连接节点 ...

    // [4] 平衡调整(核心逻辑)
    while (parent && parent->_col == RED) {
        // 处理红红冲突
        // 包含颜色翻转、旋转等操作...
    }

    _root->_col = BLACK;  // 确保根节点为黑色
    return make_pair(iterator(newnode), true);
}

关键改进点

  • 返回值改为pair<iterator, bool>,便于获取插入位置

  • 使用KeyOfT仿函数统一比较逻辑

3.2 返回值处理

// map侧直接转发
pair<iterator, bool> insert(const pair<K, V>& kv) {
    return _t.Insert(kv);
}

// set侧需要处理const迭代器
pair<iterator, bool> insert(const K& key) {
    auto ret = _t.Insert(key);
    return pair<iterator, bool>(ret.first, ret.second);
}

特殊处理

  • set的iterator本质是const_iterator,防止修改Key值

  • 通过迭代器构造函数实现普通迭代器到const迭代器的转换(具体实现见迭代器核心结构处的代码)

4.容器完整实现

map完整实现

template<class K, class V>
class map {
    struct MapKeyOfT {
        const K& operator()(const pair<const K, V>& kv) {
            return kv.first;
        }
    };
    typedef RBTree<K, pair<const K, V>, MapKeyOfT> RBTree;
public:
    typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
    typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
    
    iterator begin() { return _t.begin(); }
    iterator end() { return _t.end(); }

    V& operator[](const K& key) {
        pair<iterator, bool> ret = insert(make_pair(key, V()));
        return ret.first->second;
    }

    pair<iterator, bool> insert(const pair<K, V>& kv) {
        return _t.Insert(kv);
    }
    
private:
    RBTree _t;
};

set完整实现

template<class K>
class set {
    struct SetKeyOfT {
        const K& operator()(const K& key) {
            return key;
        }
    };
    typedef RBTree<K, K, SetKeyOfT> RBTree;
public:
    typedef typename RBTree::const_iterator const_iterator;
    typedef typename RBTree<K, pair<const K, V>,SetKeyOfT>::const_iterator const_iterator;
    
    iterator begin() const { return _t.begin(); }
    iterator end() const { return _t.end(); }

    pair<iterator, bool> insert(const K& key) {
        auto ret = _t.Insert(key);
        return pair<iterator, bool>(ret.first, ret.second);
    }
    
private:
    RBTree _t;
};

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

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

相关文章

【报错】微信小程序预览报错”60001“

1.问题描述 我在微信开发者工具写小程序时&#xff0c;使用http://localhost:8080是可以请求成功的&#xff0c;数据全都可以无报错&#xff0c;但是点击【预览】&#xff0c;用手机扫描二维码浏览时&#xff0c;发现前端图片无返回且报错60001&#xff08;打开开发者模式查看日…

软考 数据通信基础——信道

信道特性 带宽 在模拟信号里频率的差&#xff0c;表示信道能通过的频率 在数字信号里表示最大传输速率&#xff0c;单位用bit/s 通常用W表示 波特率 即码元速率&#xff0c;码元可看作一个时间周期 码元速率B2W也可写成B1/T 码元种类n和码元信息量个数N存在以下关系 Nl…

Docker数据管理,端口映射与容器互联

1.Docker 数据管理 在生产环境中使用 Docker&#xff0c;往往需要对数据进行持久化&#xff0c;或者需要在多个容器之间进行数据共享&#xff0c;这必然涉及容器的数据管理操作。 容器中的管理数据主要有两种方式&#xff1a; 数据卷&#xff08;Data Volumns&#xff09;&a…

部署前后端项目

部署项目 liunx 软件安装 软件安装方式 在Linux系统中&#xff0c;安装软件的方式主要有四种&#xff0c;这四种安装方式的特点如下&#xff1a; 建议nginx、MySQL、Redis等等使用docker安装&#xff0c;会很便捷&#xff0c;这里只演示JDK、ngxin手动的安装 安装JDK 上述我…

1256:献给阿尔吉侬的花束--BFS多组输入--memset

1256&#xff1a;献给阿尔吉侬的花束--BFS多组输入--memset 题目 解析代码【结构体】用book标记且计步数的代码[非结构体法] 题目 解析 标准的BFS题目&#xff0c;在多组输入中要做的就是先找到这一组的起点和终点&#xff0c;然后将其传给bfs&#xff0c;在多组输入中最易忘记…

【JavaEE】SpringBoot快速上手,探秘 Spring Boot,搭建 Java 项目的智慧脚手架

1.Spring Boot介绍 在学习SpringBoot之前, 我们先来认识⼀下Spring &#xff0c;我们看下Spring官⽅的介绍 可以看到&#xff0c;Spring让Java程序更加快速, 简单和安全。 Spring对于速度、简单性和⽣产⼒的关注使其成为世界上最流⾏的Java框架。 Spring官⽅提供了很多开源的…

【C】初阶数据结构9 -- 直接插入排序

前面我们学习了数据结构二叉树&#xff0c;接下来我们将开启一个新的章节&#xff0c;那就是在日常生活中经常会用到的排序算法。 所谓排序算法就是给你一堆数据&#xff0c;让你从小到大&#xff08;或从大到小&#xff09;的将这些数据排成一个有序的序列&#xff08;这些数据…

Lottie与LottieFiles:快速为前端Web开发注入精美动画的利器

目录 Lottie与LottieFiles&#xff1a;快速为前端Web开发注入精美动画的利器 一、Lottie是什么&#xff1f;从GIF到JSON的动画技术演进 1、传统动画臃肿的Gif 2、Lottie的突破性创新 二、Lottie的核心组件解析&#xff08;Lottie的技术架构&#xff09; 1、Lottie核心三要…

音乐API

https://neteasecloudmusicapi.vercel.app/docs/#/https://neteasecloudmusicapi.vercel.app/docs/#/ 使用实例 所有榜单内容摘要 说明 : 调用此接口,可获取所有榜单内容摘要 接口地址 : /toplist/detail 调用例子 : /toplist/detail 获取歌单所有歌曲 说明 : 由于网易云…

从零搭建微服务项目Pro(第3-1章——本地/OSS图片文件存取)

前言&#xff1a; 在小型demo项目中&#xff0c;一般将图片音频等字节流文件存放本地数据库&#xff0c;但企业级项目中&#xff0c;由于数据量容量有限&#xff0c;需要借助OSS来管理大规模文件。 OSS&#xff08;对象存储服务&#xff0c;Object Storage Service&#xff0…

游戏引擎学习第147天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾 具体来说&#xff0c;我们通过隐式计算来解决问题&#xff0c;而不是像数字微分分析器那样逐步增加数据。我们已经涵盖了这个部分&#xff0c;并计划继续处理音量问题。不过&#xff0c;实际上我们现在不需要继续处理…

Spring boot启动原理及相关组件

优质博文&#xff1a;IT-BLOG-CN 一、Spring Boot应用启动 一个Spring Boot应用的启动通常如下&#xff1a; SpringBootApplication Slf4j public class ApplicationMain {public static void main(String[] args) {ConfigurableApplicationContext ctx SpringApplication.…

【Linux】信号处理以及补充知识

目录 一、信号被处理的时机&#xff1a; 1、理解&#xff1a; 2、内核态与用户态&#xff1a; 1、概念&#xff1a; 2、重谈地址空间&#xff1a; 3、处理时机&#xff1a; 补充知识&#xff1a; 1、sigaction&#xff1a; 2、函数重入&#xff1a; 3、volatile&…

微服务——网关、网关登录校验、OpenFeign传递共享信息、Nacos共享配置以及热更新、动态路由

之前学习了Nacos&#xff0c;用于发现并注册、管理项目里所有的微服务&#xff0c;而OpenFeign简化微服务之间的通信&#xff0c;而为了使得前端可以使用微服务项目里的每一个微服务的接口&#xff0c;就应该将所有微服务的接口管理起来方便前端调用&#xff0c;所以有了网关。…

comctl32!ListView_OnSetItem函数分析LISTSUBITEM结构中的image表示图标位置

第一部分&#xff1a; BOOL ListView_SetSubItem(LV* plv, const LV_ITEM* plvi) { LISTSUBITEM lsi; BOOL fChanged FALSE; int i; int idpa; HDPA hdpa; if (plvi->mask & ~(LVIF_DI_SETITEM | LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) { …

数据结构——多项式问题(顺序存储结构or链式存储结构)

补充&#xff1a;malloc函数&#xff1a; malloc 函数是 C 语言标准库中的一个重要函数&#xff0c;位于 <stdlib.h> 头文件中&#xff0c;主要用于在程序运行时动态分配内存。以下将详细介绍其用法。 前面的返回值指针可以自己定义&#xff0c;如 &#xff08;int*&am…

记录小白使用 Cursor 开发第一个微信小程序(一):注册账号及下载工具(250308)

文章目录 记录小白使用 Cursor 开发第一个微信小程序&#xff08;一&#xff09;&#xff1a;注册账号及下载工具&#xff08;250308&#xff09;一、微信小程序注册摘要1.1 注册流程要点 二、小程序发布流程三、下载工具 记录小白使用 Cursor 开发第一个微信小程序&#xff08…

vue2项目修改浏览器显示的网页图标

1.准备一个新的图标文件&#xff0c;通常是. ico格式&#xff0c;也可以是. Png、. Svg等格式 2.将新的图标文件(例如&#xff1a;faviconAt.png)放入项目的public文件夹中。如下图 public文件夹中的所有文件都会在构建时原样复制到最终的输出目录(通常是dist) 3. 修改vue项目…

【网络安全工程】任务10:三层交换机配置

CSDN 原创主页&#xff1a;不羁https://blog.csdn.net/2303_76492156?typeblog三层交换机是指在OSI&#xff08;开放系统互连&#xff09;模型中的第三层网络层提供路由功能的交换机。它不仅具备二层交换机的交换功能&#xff0c;还能实现路由功能&#xff0c;提供更为灵活的网…

侯捷 C++ 课程学习笔记:C++内存管理机制

内存管理从平地到万丈高楼 内存管理入门&#xff08;Memory Management 101&#xff09; 需要具有动态分配并使用memory&#xff08;存储&#xff08;器&#xff09;&#xff0c;&#xff08;计算机的&#xff09;内存&#xff09;&#xff0c;使用过C标准库的容器&#xff0…