[数据结构 - C++] 红黑树RBTree

news2024/12/29 10:46:20

在这里插入图片描述

文章目录

  • 1、前言
  • 2、红黑树的概念
  • 3、红黑树的性质
  • 4、红黑树节点的定义
  • 5、红黑树的插入Insert
  • 6、红黑树的验证
  • 7、红黑树与AVL树的比较
  • 附录:

1、前言

我们在学习了二叉搜索树后,在它的基础上又学习了AVL树,知道了AVL树是靠平衡因子来调节左右高度差,从而让树变得平衡的。本篇我们再来学习一个依靠另一种平衡规则来控制的二叉搜索树——红黑树。

2、红黑树的概念

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

3、红黑树的性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
红色不能连续,黑色可以连续
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

概念中,说到没有一条路径会比其他路径长出俩倍,性质3与性质4相互牵制就可以保证这一点。

4、红黑树节点的定义

我们定义节点依然是三叉链,与AVL树不同的是红黑树没有平衡因子,而是保存一个代表节点颜色的属性。

enum Color
{
	RED,
	BLACK
};

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

	RBTreeNode(const pair<K, V>& _kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

这里我们定义的红黑树节点,颜色默认给的红色,但是这里是给红色还是黑色合适呢?
当插入的时候,我们新插入的节点颜色是黑色时,就会破坏性质3,新插入节点的这条路径的黑色节点数一定会比其他路径的黑色节点多一个,影响整棵树。
如果是红色,那插入的时候她的父节点可能是黑色,没有影响,可能是红色,那么就会出现连续的红色节点,但是它只会影响这一条路径。
这两种颜色插入,黑色是一定会影响,红色是可能会影响的,且黑色影响整棵树,红色影响它这一条路径,两害取其轻,我们选择红色,调整的话也比较容易调整。下面我们就来尝试看插入怎么写:

5、红黑树的插入Insert

红黑树的插入是在二叉搜索树插入基础上来修改的,因此大的方向分两步走:
1、找到插入的位置;
2、插入节点后,根据性质来调节平衡。

bool Insert(const pair<K, V>& kv)
{
    if (nullptr == _root)
    {
        _root = new Node(data);
        _root->_col = BLACK; // 性质2:根节点是黑色
        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;
        }
    }
    // 新增插入节点是红色只会影响父节点,如果是黑色影响所有路径
    // 所以 new 的节点为红色
    cur = new Node(kv);
    cur->_col = RED;
    if (parent->_kv.first < kv.first)
    {
        parent->_right = cur;
        cur->_parent = parent;
    }
    else
    {
        parent->_left = cur;
        cur->_parent = parent;
    }
	// 维护处理 ...
}

当来到这块时,已经插入了,要做的就是按照性质来检查和维护这棵树了。
1、当父亲是黑色,那么就不用维护,就结束了;
2、当父亲是红色,那么就违反了性质3(不能存在连续的红色节点),这时就需要调整了,调整也是要分情况讨论(约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)。
情况一:cur是红色,p为红色,g为黑色,u存在且为红色
这里看到的这棵树可能是完整的,也可能是子树:
在这里插入图片描述

如果是完整的树,那么改完之后,需要将g的颜色改为黑色。
如果g是子树,那么g就有父节点,这时g的颜色改为红色,父节点颜色可能也是红色,这时急需要继续向上调整了。
将p,u改为黑,g改为红,然后把g当作cur,继续向上调整。
情况二:cur是红色,p为红色,g为黑色,u不存在/u存在且为黑色
在这里插入图片描述

这里u有两种颜色,我们分开讨论:

  • 如果u不存在,那么cur一定是新增。因为u不存在g也是有一条右边路径的,这条路径就两个黑色节点空结点也是黑色,那么c就不存在,a、b都不存在,如果存在就是黑色节点,那么就打破了性质3。
  • 如果存在且为黑,那么这个抽象图就不全。因为父节是红色,叔叔为黑色,每条路径的黑色节点个数要相同,因此推测出cur之前应该是黑色,那么a、b就应该是红色,新增节点在a/b的孩子位置。
    整体就为下图:
    在这里插入图片描述

它会先经过情况一的调整方式,调整完变为情况二这样,然后再继续调整:
在这里插入图片描述

此时,就 以g为基点先右旋,然后将父节点颜色变为黑色,祖父节点颜色变为红色。(旋转不清楚的同学可以看看AVL树的)
情况三:cur是红色,p为红色,g为黑色,u不存在/u存在且为黑色
在这里插入图片描述

  • u不存在,cur是新增。因为每条路径的黑色节点个数相同,u不存在,u这条路径上两个黑色节点空结点也是黑色),推测出a、b、c都是不存在的,那么cur就是新增,如果存在只能是黑色节点,那么就打破了性质3。
  • 如果存在且为黑,这个抽象图依然是不完整的。因为父节点是红色,叔叔为黑色,每条路径的黑色节点相同,因此推测出a为黑色,cur之前也应该为黑色,那么b、c就应该是红色,新增节点在b/c的孩子位置。

图跟情况二中,u存在且为黑差不多:
在这里插入图片描述

会先经过情况一调整,变为情况三这样,然后进行调整:
在这里插入图片描述

此时,先以p为基点左旋,再以g为基点右旋,然后将cur节点变为黑色,祖父节点变为红色。
如果新增的父节点在右,叔叔节点在左,那么也是分以上三种情况,调整方式也是对应三种方式差不多,这里就不过多赘述,直接上代码:

bool Insert(const pair<K, V>& kv)
{
    if (nullptr == _root)
    {
        _root = new Node(data);
        _root->_col = BLACK; // 性质2:根节点是黑色
        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;
        }
    }
    
    // 新增插入节点是红色只会影响父节点,如果是黑色影响所有路径
    // 所以 new 的节点为红色
    cur = new Node(kv);
    cur->_col = RED;
    if (parent->_kv.first < kv.first)
    {
        parent->_right = cur;
        cur->_parent = parent;
    }
    else
    {
        parent->_left = cur;
        cur->_parent = parent;
    }

    // 不断向上调整的,所以得用while
    while (parent && parent->_col == RED)
    {
        // 父节点是祖父节点的左
        //      g
        //    p   u
        //  c 
        Node* grandfather = parent->_parent;
        // 1、父节点在祖父的左,即叔叔在右
        if (parent == grandfather->_left)
        {
            Node* uncle = grandfather->_right;
            // 1.1 叔叔存在并且为红
            if (uncle && uncle->_col == RED)
            {
                // 变色
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;

                // 向上调整
                cur = grandfather;
                parent = cur->_parent;
            }
            // 1.2 叔叔不存在 / 叔叔存在且为黑,处理方法一样
            else
            {
                if (cur == parent->_left) // 左边高的情况
                {
                    // 右单旋
                    //     g
                    //   p   u
                    // c
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else // 左边高右边高的情况
                {
                    // 双旋
                    //     g
                    //  p     u
                    //    c
                    RotateL(parent);
                    RotateR(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                // 此时不用祖父位置为黑色不用在网上调整了
                break;
            }
        }
        // 2、父节点在祖父的右,即叔叔在祖父的左
        else // parent == grandfather->_right
        {
            //     g
            //   u   p
            //         c
            Node* uncle = grandfather->_left;
            // 2.1 叔叔存在且叔叔为红色
            if (uncle && uncle->_col == RED)
            {
                // 变色
                parent->_col = uncle->_col = BLACK;
                grandfather->_col = RED;

                // 向上调整
                cur = grandfather;
                parent = cur->_parent;
            }
            // 2.2 叔叔不存在 / 叔叔存在且颜色为黑,处理方法一样
            else
            {
                if (cur == parent->_right) // 右边高的情况
                {
                    //     g
                    //   u   p
                    //         c
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else // 右边高,左边高
                {
                    //     g
                    //  u     p
                    //      c
                    RotateR(parent);
                    RotateL(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;
                }
                // 此时不用祖父位置为黑色不用在网上调整了
                break;
            }
        }
    }
    
    // 最后将根节点变为黑色
    _root->_col = BLACK;
    return true;
}

// 左单旋
void RotateL(Node* parent)
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    Node* parentParent = parent->_parent;

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

    if (_root == parent) // 父节点就是根节点
    {
        _root = subR;
        subR->_parent = nullptr;
    }
    else // 子树情况
    {
        if (parentParent->_left == parent)
        {
            parentParent->_left = subR;
        }
        else
        {
            parentParent->_right = subR;
        }
        subR->_parent = parentParent;
    }
}

// 右单旋
void RotateR(Node* parent)
{
    Node* parentParent = parent->_parent;
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

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

    if (_root == parent) // 父节点是根节点
    {
        _root = subL;
        subL->_parent = nullptr;
    }
    else // 子树情况
    {
        if (parentParent->_left == parent)
        {
            parentParent->_left = subL;
        }
        else
        {
            parentParent->_right = subL;
        }
        subL->_parent = parentParent;
    }
}

6、红黑树的验证

红色树的验证本质就是验证两方面:
1、是否为二叉搜索树(中序遍历是否有序);
2、是否满足5条性质。

void _InOrder(Node* pRoot)
{
    if (pRoot == nullptr)
        return;

    _InOrder(pRoot->_left);
    cout << pRoot->_data << " ";
    _InOrder(pRoot->_right);
}

bool IsBalance()
{
    if (_root == nullptr) return true;

    if (_root->_col == RED) return false;

    // 参考值
    int refValue = 0;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_col == BLACK) refValue++;

        cur = cur->_left;
    }
    // 检查每条路径黑色节点个数
    // 思路:以上面参考值为主,对比每条路径的黑色节点个数
    // 当走到空就说明该路径走完了,那么这个过程中记录下黑色节点个数,到空时与refValue对比
    // 这里传进去blacknum只能是传值,这样就不会影响上一层的blacknum了
    int blacknum = 0;
    // 检查连续红色节点与每条路径黑色节点个数
    return Check(_root, blacknum, refValue);
}
bool Check(Node* root, int blacknum, const int& refValue)
{
    if (root == nullptr)
    {
        if (blacknum != refValue)
        {
            cout << "存在黑色节点不相等的路径" << endl;
            return false;
        }

        return true;
    }

    // 反向检查,查看当前与父结点为红色(当前节点为红色就说明不是根节点,即存在父节点)
    if (root->_col == RED && root->_parent->_col == RED)
    {
        cout << "有连续的红色节点" << endl;
        return false;
    }

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

    return Check(root->_left, blacknum, refValue)
        && Check(root->_right, blacknum, refValue);
}

7、红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log_2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

附录:

enum Color
{
	RED,
	BLACK
};

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

	RBTreeNode(const pair<K, V>& _kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

template <class K, class V>
class RBTree
{
	typedef RBTreeNode<K,V> Node;
public:
	bool Insert(const pair<K, V>& kv)
    {
        if (nullptr == _root)
        {
            _root = new Node(data);
            _root->_col = BLACK; // 性质2:根节点是黑色
            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;
            }
        }
        
        // 新增插入节点是红色只会影响父节点,如果是黑色影响所有路径
        // 所以 new 的节点为红色
        cur = new Node(kv);
        cur->_col = RED;
        if (parent->_kv.first < kv.first)
        {
            parent->_right = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
    
        // 不断向上调整的,所以得用while
        while (parent && parent->_col == RED)
        {
            // 父节点是祖父节点的左
            //      g
            //    p   u
            //  c 
            Node* grandfather = parent->_parent;
            // 1、父节点在祖父的左,即叔叔在右
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                // 1.1 叔叔存在并且为红
                if (uncle && uncle->_col == RED)
                {
                    // 变色
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
    
                    // 向上调整
                    cur = grandfather;
                    parent = cur->_parent;
                }
                // 1.2 叔叔不存在 / 叔叔存在且为黑,处理方法一样
                else
                {
                    if (cur == parent->_left) // 左边高的情况
                    {
                        // 右单旋
                        //     g
                        //   p   u
                        // c
                        RotateR(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else // 左边高右边高的情况
                    {
                        // 双旋
                        //     g
                        //  p     u
                        //    c
                        RotateL(parent);
                        RotateR(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    // 此时不用祖父位置为黑色不用在网上调整了
                    break;
                }
            }
            // 2、父节点在祖父的右,即叔叔在祖父的左
            else // parent == grandfather->_right
            {
                //     g
                //   u   p
                //         c
                Node* uncle = grandfather->_left;
                // 2.1 叔叔存在且叔叔为红色
                if (uncle && uncle->_col == RED)
                {
                    // 变色
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
    
                    // 向上调整
                    cur = grandfather;
                    parent = cur->_parent;
                }
                // 2.2 叔叔不存在 / 叔叔存在且颜色为黑,处理方法一样
                else
                {
                    if (cur == parent->_right) // 右边高的情况
                    {
                        //     g
                        //   u   p
                        //         c
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else // 右边高,左边高
                    {
                        //     g
                        //  u     p
                        //      c
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    // 此时不用祖父位置为黑色不用在网上调整了
                    break;
                }
            }
        }
        
        // 最后将根节点变为黑色
        _root->_col = BLACK;
        return true;
    }
    
	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return true;
	}

	bool IsBalance()
	{
		if (_root == nullptr) return true;

		if (_root->_col == RED) return false;

		// 参考值
		int refValue = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK) refValue++;

			cur = cur->_left;
		}
		// 检查每条路径黑色节点个数
		// 思路:以上面参考值为主,对比每条路径的黑色节点个数
		// 当走到空就说明该路径走完了,那么这个过程中记录下黑色节点个数,到空时与refValue对比
		// 这里传进去blacknum只能是传值,这样就不会影响上一层的blacknum了
		int blacknum = 0;
		// 检查连续红色节点与每条路径黑色节点个数
		return Check(_root, blacknum, refValue);
	}

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

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	bool Check(Node* root, int blacknum, const int& refValue)
	{
		if (root == nullptr)
		{
			if (blacknum != refValue)
			{
				cout << "存在黑色节点不相等的路径" << endl;
				return false;
			}

			return true;
		}

		// 反向检查,查看当前与父结点为红色(当前节点为红色就说明不是根节点,即存在父节点)
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "有连续的红色节点" << endl;
			return false;
		}

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

		return Check(root->_left, blacknum, refValue)
			&& Check(root->_right, blacknum, refValue);
	}

	// 左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

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

		if (_root == parent) // 父节点就是根节点
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else // 子树情况
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}
	}

	// 右单旋
	void RotateR(Node* parent)
	{
		Node* parentParent = parent->_parent;
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		if (_root == parent) // 父节点是根节点
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else // 子树情况
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}
	}

	void _InOrder(Node* pRoot)
	{
		if (pRoot == nullptr)
			return;

		_InOrder(pRoot->_left);
		cout << pRoot->_data << " ";
		_InOrder(pRoot->_right);
	}

	size_t _Height(Node* pRoot)
	{
		if (pRoot == nullptr)
			return 0;

		int leftHeight = _Height(pRoot->_left);
		int rightHeight = _Height(pRoot->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

springboot+mysql马拉松赛事志愿者管理系统-计算机毕业设计源码93439

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对马拉松赛事志愿者管理等问题&#xff0c;对…

司铭宇老师: 房地产中介销售顾问培训:房产中介经纪人提升销售业绩的法宝

房地产中介销售顾问培训&#xff1a;房产中介经纪人提升销售业绩的法宝 一、了解客户需求 成功的沟通始于深入了解客户的需求和期望。房地产经纪人应该通过提问和倾听来收集信息&#xff0c;比如客户对房产的地理位置、价格范围、户型设计等的偏好。此外&#xff0c;了解客户的…

【算法专栏学习】成贤学院,程序员的福利站到了,判断子序列,经典算法实战。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

优思学院|怎样制作优秀的标准作业流程(SOP)?

我相信你一定去过众所周知的麦当劳。它在世界各地都有许多分店。不知道你是否曾在不同地方的麦当劳尝试过他们的美食&#xff0c;但你可能会好奇&#xff0c;为什么无论在世界哪个角落&#xff0c;麦当劳的食物口味都如此一致&#xff1f;它们是如何确保全球各地分店都能提供同…

中断——外部中断EXIT

终端可以分成外部中断和内部中断吗 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 野火中断章节有这样一句话 【F103在内核水平上搭载了一个异常响应系统&#xff0c; 支持为数众多的系统异常和外部中断。 其中系统异常有8个&#xff…

「 网络安全常用术语解读 」网络攻击者的战术、技术和常识知识库ATTCK详解

引言&#xff1a;随着网络攻击手段的不断升级和多样化&#xff0c;网络安全领域面临着越来越严峻的挑战。为了帮助网络安全专业人员更好地识别和防御网络攻击&#xff0c;MITRE公司创建了ATT&CK框架&#xff0c;以提供一个统一且结构化的方法来描述网络攻击者的行为和技巧。…

Git与GitHub零基础教学

大家好&#xff0c;我是星恒&#xff0c;这个帖子给大家分享的是git和github的全套教程&#xff0c;包含github初始&#xff0c;git常用命令以及基本使用&#xff0c;git的ssh链接github&#xff0c;github使用token登录&#xff0c;github和idea的配合&#xff0c;一些平时常用…

SpringCloudConfig+SpringCloudBus+Actuator+Git实现Eureka关键配置属性热更新(全程不重启服务)

文章目录 前言1.痛点2.解决方案3.具体实现3.1搭建热配置服务3.2编写配置文件3.3搭建版本控制仓库3.4Eureka-Client引入以下依赖3.5Eureka-Client微服务编写以下配置bootstrap.yml提前加载3.6分别编写测试Controller3.7测试效果3.8下线场景压测 4.SpringCloudBus优化 前言 在上…

IDEA(十)2022版本 Services中服务窗口不显示端口号解决

目录 一、问题描述二、问题分析三、解决方案3.1 设置启动参数【生效】3.2 方法二&#xff1a;设置环境变量【不生效】3.3 方法三&#xff1a;删除缓存【不生效】 四、补充&#xff1a;如何手动控制端口显示 一、问题描述 我们在使用 IDEA 的过程中&#xff0c;会发现在 Servic…

Midjourney V6超深度评测;开源人工智能:创新的前沿

&#x1f989; AI新闻 &#x1f680; Midjourney V6超深度评测&#xff1a;超现实主义细节爆表 摘要&#xff1a;国外网友Andre Kovalev对Midjourney V6进行了超深度评测&#xff0c;发现V6相较于V5.2在图片真实感、细节表现方面有较大提升。V6拥有超现实主义的特点&#xff…

FA-分配行重分配报错【APP-OFA-48313】

FA-重分配行报错 已存在行只能分多次转移调整 Ref1&#xff1a; APP-OFA-48313 You Cannot Create Identical Distribution Lines with Transfer (Doc ID 336894.1) APPLIES TO: Oracle Assets - Version 11.5.10.0 and later Information in this document applies to any…

ESP32-UDP通信 (Arduino)

ESP32配置UDP通信 介绍 用户数据报协议UDP UDP&#xff08;User Datagram Protocol&#xff09;是一种在计算机网络中常用的传输层协议&#xff0c;它与TCP&#xff08;Transmission Control Protocol&#xff09;一样属于传输层协议的一种。UDP主要用于在网络中传输数据&…

算法每日一题: 分割数组的最大值 | 动归 | 分割数组 | 贪心+二分

Hello&#xff0c;大家好&#xff0c;我是星恒 呜呜呜&#xff0c;今天给大家带来的又是一道经典的动归难题。 题目&#xff1a;leetcode 410给定一个非负整数数组 nums 和一个整数 k &#xff0c;你需要将这个数组分成 k_ 个非空的连续子数组。设计一个算法使得这 k _个子数组…

【Linux】第三十三站:日志

文章目录 一、实现一个简单的日志1.简介2.可变参数3.错误等级4.时间5.打印每一条参数6.与前面的一些代码搭配使用 二、完整代码 一、实现一个简单的日志 1.简介 我们运行代码的时候&#xff0c;我们希望有各种各样的运行时候的一些信息。这也就是日志 它一半有日志时间&…

Python 字符串不可变性的优缺点

随着计算机技术的快速发展&#xff0c;越来越多的编程语言被发明出来&#xff0c;每种编程语言都有自己独特的特点和优势。以我经常使用python写爬虫为例&#xff0c;在 Python 中&#xff0c;字符串是不可变的&#xff0c;这意味着一旦字符串被创建&#xff0c;就不能被修改。…

【机组】微程序控制单元实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a;一、 实验目…

Laykefu客服系统 任意文件上传漏洞复现

0x01 产品简介 Laykefu 是一款基于workerman+gatawayworker+thinkphp5搭建的全功能webim客服系统,旨在帮助企业有效管理和提供优质的客户服务。 0x02 漏洞概述 Laykefu客服系统/admin/users/upavatar.html接口处存在文件上传漏洞,而且当请求中Cookie中的”user_name“不为…

嵌入式C语言基本编写规范

最重要的一条规则 编写代码时最重要的一条规则是&#xff1a;检查周围的代码并尝试模仿它。 作为维护人员&#xff0c;如果收到的补丁明显与周围代码的编码风格不同&#xff0c;这是令人沮丧的。这是不尊重人的&#xff0c;就像某人穿着泥泞的鞋子走进一间一尘不染的房子。 …

RT-Thread 17. 中断发送信号量后线程去处理信号量

1. 代码 //sem.c #include <rtthread.h> #include <rtdevice.h> #include "drv_gpio.h"#define THREAD_PRIORITY 25 #define THREAD_TIMESLICE 5#define LED3_PIN GET_PIN(B, 3) ALIGN(RT_ALIGN_SIZE) static char thread3_stack[1024]; static struct…

2023 IoTDB Summit:北京城建智控科技股份有限公司高级研发主管刘喆《IoTDB在城市轨道交通综合监控系统中的应用》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…