「C++」红黑树的插入(手撕红黑树系列)

news2024/11/24 18:39:58
在这里插入图片描述

💻文章目录

  • 📄前言
  • 红黑树
    • 概念
    • 红黑树的结构
      • 红黑树节点的定义
      • 红黑树的定义
      • 红黑树的调整
    • 红黑树的迭代器
      • 迭代器的声明
      • operator( )++
      • opeartor--( )
    • 完整代码
  • 📓总结


📄前言

作为一名程序员相信你一定有所听闻红黑树的大名,像是手撕红黑树这样的名梗已经几乎传遍了程序员之间,如果你还不会“手撕”红黑树,那么本文将会教会你如何“手撕”红黑树。

红黑树

概念

红黑树,顾名思义是只有红色和黑色两种颜色的树,由 Rudolf Bayer 在1972年发明的。红黑树是一种高效的查找树,可以在 O ( l o g 2 n ) O(log_2n) O(log2n)的时间复杂度下进行查找、插入和删除,C++中的map和set的底层也是利用红黑树所构成,在深入学习红黑树前,先让我们学习一下它的特性吧。

红黑树的特性:

  1. 根节点为黑
    t2. 最长路径的长度不超过最短路径的长度的两倍
  2. 每条路径的黑色节点之和都相同
  3. 不能存在连续的红色节点
  4. 只存在红色或黑色的节点
  5. 中序遍历是有序的

红黑树的样例:
在这里插入图片描述在这里插入图片描述

从图例我们可以看出每条路径的黑色节点个数都是相同的并且没有连续的红色节点,只要满足这两条特性,红黑树的最长路径节点个数不会超过最短节点个数的两倍,从而维护了树的平衡。

红黑树的结构

红黑树节点的定义

在进入插入操作前,得先定义好树的节点。因为树的插入需要用到父节点、甚至祖父节点,所以为了方便插入,二叉树的节点新增了父节点的指针。

enum Color	//颜色的定义
{
    RED,	//0
    BLACK	//1
};

template <class _Value>
struct RBTreeNode		//红黑树节点的定义
{
    RBTreeNode<_Value>* _left;	//节点的左孩子
    RBTreeNode<_Value>* _right;	//节点的右孩子
    RBTreeNode<_Value>* _parent;	//节点的双亲
    Color _col;		//节点的颜色
    _Value _data;			//节点的数值

    RBTreeNode(const _Value& data = _Value())	//节点的构造函数
        :_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_data(data)
        ,_col(RED)	//默认设节点为红色
    {}
};

红黑树的定义

C++的红黑树在实现上为了同时让map和set复用,增加了一个keyofvalue的模板参数,用来解析需要比较的数值,如果不打算实现set和map可以不用写。

template<class _Key, class _Value, class _KeyOfValue>	
/*如果愿意还可以加上一个compare参数,来比较数值*/
class RBTree
{
public:
    typedef RBTreeNode<_Value> Node;
    /*这里暂时先把insert的返回值设为Node*,迭代器后面介绍时再补充*/
    Node* insert(const _Value data)		
    {
    	if(_root == nullptr)			//节点为空则新建
	    {
	        _root = new Node(data);
	        _root->_col = BLACK;		//红黑书性质规定根节点必须为黑
	        return _root;
	    }
	    
	    _KeyOfValue kot;		//用来解析数据的伪函数
	    Node* cur = _root;
	    Node* parent = nullptr;		
	    while(cur)			/*二叉树搜索树的经典搜索过程*/
	    {
	    	//工作原理:是data是pair类型则返回data.first,正常内置类型直接返回data
	        if(kot(cur->_data) < kot(data))		
	        {															
	            parent = cur;
	            cur = cur->_right;
	        }
	        else if(kot(cur->_data) > kot(data))
	        {
	            parent = cur;
	            cur = cur->_left;
	        }
	        else 
	            return cur;
	    }
	
	    cur = new Node(data);
	    Node* ret = cur;
	    cur->_parent = parent;		/*链接父节点*/
	    /*父节点链接子节点*/
	    if(kot(cur->_data) < kot(parent->_data))
	        parent->_left = cur;
	    else 
	        parent->_right = cur;	
	        
		/***************检查红黑树是否违反性质**************/
    }
}

红黑树的调整

红黑树的每次插入都需要检查其性质是否遭到了破坏,因为节点默认颜色为红色,所以当父节点为黑色时,则不需要调整。如果父节点为红色,违反了红黑树的性质,根据红黑树的情况,共有六种情况需要讨论,其中需要利用到祖父节点,根据父节点在祖父节点的左孩子/右孩子,又将6种情况划分为两类。

为了方便讨论,这里把当前节点作为cur,cur的父节点为p,cur的祖父节点为g,p的兄弟节点为u

  • 父节点是祖父节点的左孩子

    • 情况一:cur为红,p为红,g为黑,u存在且为红

      这种情况下,需要把p节点和u节点设为黑色,如果g节点为根节点则退出调整,否则将g节点设为红色,并把g赋值给cur,继续向上调整。
      在这里插入图片描述

      if(uncle && uncle->_col == RED)
      {
      	parent->_col = uncle->_col = BLACK;
      	grandParent->_col = RED;
      	
      	cur = grandParent;
      	parent = cur->_parent;
      }
      
    • 情况二:cur为红,p为红,g为黑,u不存在/存在且为黑,并且cur为p的左孩子

      这种情况下,需要对p节点进行右旋操作,并将p节点改为黑,cur和g节点改为红
      在这里插入图片描述

      if(uncle && uncle->_col == BLACK)
      {
           parent->_col = uncle->_col = BLACK;
           grandParent->_col = RED;
           
           cur = grandParent;
           parent = cur->_parent;
      }
      
    • 情况三:cur为红,p为红,g为黑,u不存在/存在且为黑,并且cur为p的左孩子
      在这种情况下,需要对双旋操作,先对p节点进行左旋,使得树变得极端左倾,然后再对g节点进行右倾恢复平衡,最后将g改为红,p改为黑。

      在这里插入图片描述

      else {
      	RotateL(parent);
       	RotateR(grandParent);
       	grandParent->_col = RED;
       	cur->_col = BLACK;
      }
      
  • 父节点是祖父节点的右孩子

    • 情况四:cur为红,p为红,g为黑,u存在且为红
      与情况一的处理一样
      在这里插入图片描述

      if(uncle && uncle->_col == BLACK)
      {
      		parent->_col = uncle->_col = BLACK;
      		grandParent->_col = RED;
      		cur = grandParent;
      		parent = cur->_parent;
      }
      
    • 情况五:cur为红,p为红,g为黑,u不存在/存在且为黑, 并且cur为p的左孩子
      这种情况下,需要对g节点进行左旋操作,并把p节点改黑、g节点改红。
      在这里插入图片描述

      if(cur == parent->_right)
      {
      	RotateL(grandParent);
      	parent->_col = BLACK;
      	grandParent->_col = RED;
      }
      
    • 情况六:cur为红,p为红,g为黑,u不存在/存在且为黑, 并且cur为p的右孩子
      这种情况下,需要对p节点进行右旋,使树变得极端右倾,然后对g节点进行左旋,最后将g节点改红、cur节点改黑。
      在这里插入图片描述

    else 
    {
    	RotateR(parent);
    	RotateL(grandParent);
    	grandParent->_col = RED;
    	cur->_col = BLACK;
    }
    

红黑树的迭代器

做完了树的插入,接下来就是红黑树的迭代器了。因为红黑树是平衡树,所以它的最小节点在树的最左侧,最大节点在树的最右侧,为此我们可以使用一个头节点,让其左右孩子指向最大最小节点,父节点指向跟节点。

在这里插入图片描述

迭代器的声明

template <class T, class Ref, class Ptr>	//Ref、Ptr用于const_iterator
struct _TreeIterator
{
    typedef RBTreeNode<T> Node;
    typedef _TreeIterator<T, Ref, Ptr> self;
    Node* _node;

	self& operator--();
	self& operator++();
}

operator( )++

找平衡树的下一个比当前节点大的节点,有两种情况

  • 当前节点存在右节点,则找右节点最左边的节点。
  • 不存在右节点,则返回父节点直到当前节点不是父节点的左节点
self& operator++() /*寻找下一个更大节点*/
{
	if(_node->_right)	
	{
	    Node* cur = _node->_right;
	    while(cur->_left)		/*寻找最左侧节点*/
	        cur = cur->_left;
	
	    _node = cur;
	}
	else 
	{
	    Node* cur = _node;		
	    Node* parent = cur->_parent;		
	    while(parent && cur == parent->_right)
	    {		/*右子树不存在,继续向上调整*/
	        cur = parent;
	        parent = parent->_parent;
	    }
	    _node = parent;
	}
	
	return *this;
}

opeartor–( )

寻找上一节点也分两种情况。

  • 当前节点左孩子存在,则找到左孩子的最右侧节点。
  • 当前节点不存在左孩子,则向上寻找直到当前节点不再是父节点的左孩子
self& operator--()
{
    Node* cur = _node;
    if(cur->_col == RED && cur->_parent->_parent == cur)
    {				//当前节点为头节点
        cur = cur->_right;
    }
    if(cur->_left)
    {		//左子树存在,在左子树寻找最大节点
        cur = cur->_left;
        while(cur->_right)
            cur = cur->_right;
    }
    else
    {		//向上调整
        Node* parent = cur->_parent;
        while(parent && cur == parent->_left)
        {
            cur = parent;
            parent = parent->_parent;
        }
        cur = parent;
    }
    _node = cur;

    return *this;
}

完整代码

template <class T, class Ref, class Ptr>
struct _TreeIterator		//迭代器
{
    typedef RBTreeNode<T> Node;
    typedef _TreeIterator<T, Ref, Ptr> self;
    typedef _TreeIterator<T, T&, T*> iterator;  
    Node* _node;

    _TreeIterator(Node* node)
        :_node(node)
    {}

    _TreeIterator(const iterator& _it) //构造函数,方便以后实现set中的inset函数中的pair拷贝
        :_node(_it._node)
    {
    }
    
    Ref operator*()const
    {
        return _node->_data;
    }

    Ptr operator->()const
    {
        return &operator*();
    }

    self& operator--()
    {
        Node* cur = _node;
        if(cur->_col == RED && cur->_parent->_parent == cur)
        {			
            cur = cur->_right;
        }
        if(cur->_left)
        {
            cur = cur->_left;
            while(cur->_right)
                cur = cur->_right;
        }
        else
        {
            Node* parent = cur->_parent;
            while(parent && cur == parent->_left)
            {
                cur = parent;
                parent = parent->_parent;
            }
            cur = parent;
        }
        _node = cur;

        return *this;
    }

    self&& operator--(int)
    {
        self tem = *this;
        Node* cur = _node;
        if(cur->_col == RED && cur->_parent->_parent == cur)
        {
            cur = cur->_right;
        }
        if(cur->_left)
        {
            cur = cur->_left;
            while(cur->_right)
                cur = cur->_right;
        }
        else
        {
            Node* parent = cur->_parent;
            while(parent && cur == parent->_left)
            {
                cur = parent;
                parent = parent->_parent;
            }
            cur = parent;
        }
        _node = cur;

        return tem;
    }

    self& operator++()
    {
        if(_node->_right)
        {
            Node* cur = _node->_right;
            while(cur->_left)
                cur = cur->_left;

            _node = cur;
        }
        else 
        {
            Node* cur = _node;
            Node* parent = cur->_parent;
            while(parent && cur == parent->_right)
            {
                cur = parent;
                parent = parent->_parent;
            }
            _node = parent;
        }

        return *this;
    }

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

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

template<class K, class T, class KeyOfT>	//可是选择加上 class compare
class RBTree
{
public:
	typedef RBTreeNode<T> Node;
    typedef _TreeIterator<T, T&, T*> iterator;
    typedef _TreeIterator<T, const T&, const T*> const_iterator;		
	RBTree()
	{		//提前开好头节点
		_root = new Node;
		_root->_left = _root;
		_root->_right = _root;
	}

    const_iterator begin() const 
    {
        return const_iterator(LeftMost());
    }

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

    iterator begin()
    {
        return iterator(LeftMost());
    }

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

    std::pair<iterator, bool> Insert(const T& data);		//上文insert返回值设为了Node*,但实际应该是这个

    // 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
    iterator Find(const K& data);
    const_iterator Find(const K& data) const;
    
    // 获取红黑树最左侧节点
	Node* LeftMost()const;

    // 中序遍历
    void InOrder() 
    {
        _InOrder(GetRoot());
        std::cout << std::endl;
    }
    
    // 获取红黑树最右侧节点
	Node* RightMost()const;
    
    // 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
	bool IsValidRBTRee();
private:
	bool _IsValidRBTRee(Node* pRoot, size_t blackCount, const size_t pathBlack);
    // 左单旋
	void RotateL(Node* pParent);
    // 右单旋
	void RotateR(Node* pParent);
    // 为了操作树简单起见:获取根节点
	Node*& GetRoot() const { return _root->_parent; }
    
    void _InOrder(Node* root);

    void rebalance(Node*& cur, Node*& parent)		//红黑树的平衡调整
    {
        while (parent != _root && parent->_col == RED)
        {
            Node* grandParent = parent->_parent;
            if(parent == grandParent->_left)
            {
                Node* uncle = grandParent->_right;
                if(uncle && uncle->_col == RED)
                {
                     parent->_col = uncle->_col = BLACK;
                     grandParent->_col = RED;

                     cur = grandParent;
                     parent = cur->_parent;
                }
                else 
                {
                    if(cur == parent->_left)
                    {   //右旋
                        RotateR(grandParent);
                        parent->_col = BLACK;
                        grandParent->_col = RED;
                    }
                    else 
                    {   //双旋
                       RotateL(parent);
                       RotateR(grandParent);
                       grandParent->_col = RED;
                       cur->_col = BLACK;
                    }
                    break;
                }
            }
            else 
            {
                Node* uncle = grandParent->_left;

                if(uncle && uncle->_col == BLACK)
                {
                     parent->_col = uncle->_col = BLACK;
                     grandParent->_col = RED;
                     cur = grandParent;
                     parent = cur->_parent;
                }
                else 
                {
                    if(cur == parent->_right)
                    {
                        RotateL(grandParent);
                        parent->_col = BLACK;
                        grandParent->_col = RED;
                    }
                    else 
                    {
                       RotateR(parent);
                       RotateL(grandParent);
                       grandParent->_col = RED;
                       cur->_col = BLACK;
                    }
                    break;
                }
            }
        }
        GetRoot()->_col = BLACK;
    }
private:
	Node* _root = nullptr;
    KeyOfT kot;
};


template <class K, class T, class KeyOfT>
typename RBTree<K, T, KeyOfT>::const_iterator RBTree<K, T, KeyOfT>::Find(const K& data) const
{
    Node* cur = GetRoot();
    while(cur)
    {
        if(kot(cur->_data) < data)
        {
            cur = cur->_right;
        }
        else if(kot(cur->_data) > data)
        {
            cur = cur->_left;
        }
        else 
        {
            return cur;
        }
    }

    return nullptr;
}

template <class K, class T, class KeyOfT>
typename RBTree<K, T, KeyOfT>::iterator RBTree<K, T, KeyOfT>::Find(const K& data) 
{
    Node* cur = GetRoot();
    while(cur)
    {
        if(kot(cur->_data) < data)
        {
            cur = cur->_right;
        }
        else if(kot(cur->_data) > data)
        {
            cur = cur->_left;
        }
        else 
        {
            return cur;
        }
    }

    return nullptr;
}

template <class K, class T, class KeyOfT>
void RBTree<K, T, KeyOfT>::_InOrder(Node* root)	//中序遍历
{
    if(!root)   
        return;

    _InOrder(root->_left);
    std::cout << root->_data << " ";
    _InOrder(root->_right);
}


template <class K, class T, class KeyOfT>
typename RBTree<K, T, KeyOfT>::Node* RBTree<K, T, KeyOfT>::LeftMost()const	//最左节点
{
    return _root->_left;
}

template <class K, class T, class KeyOfT>
typename RBTree<K, T, KeyOfT>::Node* RBTree<K, T, KeyOfT>::RightMost()const	//最右节点
{
    return _root->_right;
}


template <class K, class T, class KeyOfT>
bool RBTree<K, T, KeyOfT>::IsValidRBTRee()		//检查树的性质是否被破坏
{
    if(!GetRoot() || GetRoot()->_col == RED)  return false;
    
    size_t pathBlack = 0;
    Node* cur = GetRoot();
    while(cur)
    {
        if(cur->_col == BLACK)
            ++pathBlack;	//计算路径黑色节点的总个数
        cur = cur->_left;
    }
    int blackCount = 0;

    return _IsValidRBTRee(GetRoot(), blackCount, pathBlack);
}

template <class K, class T, class KeyOfT>
bool RBTree<K, T, KeyOfT>::_IsValidRBTRee(Node* pRoot, size_t blackCount, const size_t pathBlack)
{
    if(!pRoot)
    {
        if(blackCount != pathBlack)
        {
            std::cout << "有连续的红色结点" << std::endl;
            return false;
        }
        return true;
    }

    if(pRoot->_col == RED && pRoot->_parent->_col == RED)
    {
        std::cout << "有连续的红色结点" << std::endl;
        return false;
    }

    if(pRoot->_col == BLACK)
        ++blackCount;

    return _IsValidRBTRee(pRoot->_left, blackCount, pathBlack)
        && _IsValidRBTRee(pRoot->_right, blackCount, pathBlack);
}


template <class K, class T, class KeyOfT>   
std::pair<typename RBTree<K, T, KeyOfT>::iterator, bool> RBTree<K, T, KeyOfT>::Insert(const T& data)
{
    if(GetRoot() == nullptr)
    {
        Node* node = new Node(data);
        node->_col = BLACK;
        node->_parent = _root;
        _root->_parent = node;
        _root->_left = _root->_parent;
        _root->_right = _root->_parent;

        return std::make_pair(iterator(GetRoot()), true);
    }

    Node* cur = GetRoot();
    Node* parent = nullptr;
    while(cur)
    {
        if(kot(cur->_data) < kot(data))
        {
            parent = cur;
            cur = cur->_right;
        }
        else if(kot(cur->_data) > kot(data))
        {
            parent = cur;
            cur = cur->_left;
        }
        else 
            return std::make_pair(iterator(cur), false);
    }

    cur = new Node(data);
    Node* ret = cur;		//记录新增的节点,因为在调整后,节点可能会丢失
    cur->_parent = parent;

    if(kot(cur->_data) < kot(parent->_data))
    {
        if (parent == _root->_left)	//更新最小节点
            _root->_left = cur;

        parent->_left = cur;
    }
    else 
    {
        if(parent == _root->_right)	//更新最大节点
            _root->_right = cur;

        parent->_right = cur;
    }

    rebalance(cur, parent);

    return std::make_pair(ret, true);
}

template <class K, class V, class KeyOfT>
void RBTree<K, V, KeyOfT>::RotateL(Node* parent)	//左旋
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    Node* parentParent = parent->_parent;

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

    if(GetRoot() == parent)
    {
        GetRoot() = subR;
    }
    else 
    {
        if(parent == parentParent->_left)
        {
            parentParent->_left = subR;
        }
        else 
        {
            parentParent->_right = subR;
        }
    }
}

template <class K, class V, class KeyOfT>
void RBTree<K, V, KeyOfT>::RotateR(Node* parent)	//右旋
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    Node* parentParent = parent->_parent;

    subL->_right = parent;
    subL->_parent = parentParent;

    parent->_left = subLR;
    parent->_parent = subL;

    if(subLR)
        subLR->_parent = parent;

    if(parent == GetRoot())
        GetRoot() = subL;
    else
    {
        if(parent == parentParent->_left)
            parentParent->_left = subL;
        else
            parentParent->_right = subL;
    }
} 

📓总结

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

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

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

相关文章

【区块链】产品经理的NFT初探

常见的FT如比特币&#xff08;BTC&#xff09;&#xff0c;以太币&#xff08;ETH&#xff09;等&#xff0c;两个代币之间是完全可替换的。而NFT具有唯一性&#xff0c;不可以互相替换。本文作者对NET的发展现状、相关协议、应用场景等方面进行了分析&#xff0c;一起来看一下…

修复debain/ Ubuntu 中的“密钥存储在旧版 trust.gpg 密钥环中”问题

如果您在 Ubuntu 22.04 及更高版本中使用 PPA 或添加外部存储库&#xff0c;您很可能会看到如下消息&#xff1a; W: https://packagecloud.io/slacktechnologies/slack/debian/dists/jessie/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg),…

springcloud nacos配置优先级研究及配置管理最佳实践

目录 背景工具版本SpringCloud配置存放位置及相应优先级代码中nacosjar包外挂 多种配置共同存在时的优先级项目配置管理最佳实践无nacos的情况有nacos的情况 参考文献 背景 公司有很多应用是基于SpringBoot/SpringCloud开发。由于在配置文件中经常会涉及数据库账号密码之类的敏…

Gson的用法详解

一、简介 Gson&#xff08;又称Google Gson&#xff09;是Google公司发布的一个开放源代码的Java库&#xff0c;主要用途为序列化Java对象为JSON字符串&#xff0c;或反序列化JSON字符串成Java对象。 Gson官网&#xff1a;gson Gson源码地址&#xff1a;google/gson 二、依赖…

Long-Context下LLM模型架构全面介绍

深度学习自然语言处理 原创作者&#xff1a;cola 随着ChatGPT的快速发展&#xff0c;基于Transformer的大型语言模型(LLM)为人工通用智能(AGI)铺平了一条革命性的道路&#xff0c;并已应用于知识库、人机界面和动态代理等不同领域。然而&#xff0c;存在一个普遍的限制:当前许多…

Linux处理文件常见命令

目录 1 cp 2 rm 3 zip与unzip 3.1 zip 3.2 unzip 4 cd 5 ls 6 chmod 7 scp 7.1 文件在你操作的机器上&#xff0c;你要传给另一个机器 7.1.1 文件 7.1.2 文件夹 7.2 文件在另一个机器上&#xff0c;你要把文件搞到你操作的机器上 7.2.1 文件 7.2.…

NX二次开发UF_CURVE_create_arc_3point 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_create_arc_3point Defined in: uf_curve.h int UF_CURVE_create_arc_3point(tag_t point1, tag_t point2, tag_t point3, UF_CURVE_limit_p_t limit_p [ 2 ] , tag_t supp…

开发知识点-CSS样式

CSS样式 fontCSS 外边距 —— 围绕在元素边框的空白区域# linear-gradient() ——创建一个线性渐变的 "图像"# transform ——旋转 元素![在这里插入图片描述](https://img-blog.csdnimg.cn/20191204100321698.png)# rotate() [旋转] # 边框 (border) —— 围绕元素内…

无分类编址 CIDR

在域名系统出现之后的第一个十年里&#xff0c;基于分类网络进行地址分配和路由IP数据包的设计就已明显显得可扩充性不足&#xff08;参见RFC 1517&#xff09;。为了解决这个问题&#xff0c;互联网工程工作小组在1993年发布了一新系列的标准——RFC 1518和RFC 1519——以定义…

时尚和美容网站的技术 SEO:提示和最佳实践

如果你对美容和时尚感兴趣&#xff0c;做了一个网站&#xff0c;但不知道如何在上面做技术SEO&#xff1f;此外&#xff0c;时尚和美容网站的技术 SEO 没有任何特别的指南&#xff01; 我们听到了你的声音&#xff01;但首先&#xff0c;请记住&#xff0c;技术性SEO不是在一两…

Docker监控Weave Scope的安装和使用

1.本地安装Weave Scope 1&#xff09;创建文件夹。 mkdir /usr/local/bin/scope 2&#xff09;从本地上传文件。 rz scope.bin以资源形式已上传到文章开篇。 3&#xff09;修改scope.bin文件为可执行文件。 chmod 755 /usr/local/bin/scope/scope.bin 4&#xff09;执行sco…

使用 SwiftUI 创建一个灵活的选择器

文章目录 前言可选择协议自定义化FlexiblePicker 逻辑FlexiblePicker 视图总结 前言 最近&#xff0c;在我正在开发一个在 Dribbble 上找到的设计的 SwiftUI 实现时&#xff0c;我想到了一个点子&#xff0c;可以通过一些酷炫的筛选器扩展该项目以缩小结果列表。 我决定筛选视…

2023.11.28-电商平台建设03 - 大数据调优手段

1.优化手段 1.1分桶表 HIVE的分桶本质上就是MR的分区操作 建表语句: create table 表名(字段 类型,.... ) clustered by(分桶字段) [sorted by (字段 [asc | desc])] into N buckets --- 定义分桶表核心语句 row format...... 分桶的作用 1) 进行数据采样工作 1.1) …

Vim多行编辑

Vim多行编辑 Ctrlq进入多行编辑模式&#xff0c;然后上下选择要编辑的行 按下I或者Shifti&#xff0c;进入编辑模式 编辑的时候多行不会同时变化&#xff0c;不要担心&#xff0c;确实是多行编辑 编辑完成&#xff0c;想要结束多行编辑&#xff0c;按下Esc&#xff0c;此时…

BGP综合实验(IP)

实验要求&#xff1a; 实验思路&#xff1a; 1.划分IP地址&#xff1a; 将172.16.0.0/16的网段划分为172.16.0.0/24的多个网段&#xff0c;因为在实际工程当中&#xff0c;24的网段更符合用户网段&#xff0c;因此先将网段划分为172.16.0.0 /24的多个子网掩码为24的网段&…

使用Pytorch从零开始构建扩散模型-DDPM

知识回顾: [1] 生成式建模概述 [2] Transformer I&#xff0c;Transformer II [3] 变分自编码器 [4] 生成对抗网络&#xff0c;高级生成对抗网络 I&#xff0c;高级生成对抗网络 II [5] 自回归模型 [6] 归一化流模型 [7] 基于能量的模型 [8] 扩散模型 I, 扩散模型 II 引言 去噪…

算法 离散化

整数离散化 适用条件 适用于有序的整数序列该序列的值域很大&#xff0c;该序列的数的个数很少使用的是数的相对大小而非绝对大小 算法思路 原数组 a &#xff1a; 数组下标&#xff1a;0 1 2 3 4 数组元素&#xff1a;1 2 2 5 109 映射数组 &#xff1a; 数组下标&…

gRPC Java、Go、PHP使用例子

文章目录 1、Protocol Buffers定义接口1.1、编写接口服务1.2、Protobuf基础数据类型 2、服务器端实现2.1、生成gRPC服务类2.2、Java服务器端实现 3、java、go、php客户端实现3.1、Java客户端实现3.2、Go客户端实现3.3、PHP客户端实现 4、运行效果 本文例子是在Window平台测试&a…

从 0 到 1 开发一个 node 命令行工具

G2 5.0 推出了服务端渲染的能力&#xff0c;为了让开发者更快捷得使用这部分能力&#xff0c;最写了一个 node 命令行工具 g2-ssr-node&#xff1a;用于把 G2 的 spec 转换成 png、jpeg 或者 pdf 等。基本的使用如下&#xff1a; $ g2-ssr-node g2png -i ./bar.json -o ./bar.…

【Intel FPGA】D5005 使用笔记

项目总目标&#xff0c;在AFU中实现xx算法DDR 1.FPGA device &#xff1a;1SX280HN2F43E2VG 2 .硬件架构图 3.DDR信息 4.FIM &#xff08;FPAG Interface Manager&#xff09; The FIM contains the FPGA logic to support the accelerators, including the PCIe IP core, …