『 C++ 』BinarySearchTree搜索二叉树

请添加图片描述


文章目录

    • 前言 🦕
    • 二叉搜索树的概念 🦕
    • 搜索二叉树的初始化 🦕
    • Insert( )插入函数 🦕
      • 👾 InsertR( ) 插入函数(递归)
    • InOrder( ) 中序遍历打印 🦕
    • Find( ) 查找函数 🦕
      • 👾 Find( ) 查找函数(递归)
    • Erase( ) 删除函数 🦕
      • 👾 Erase( ) 函数(递归)
    • 默认成员函数 🦕
      • 👾 构造函数
      • 👾 拷贝构造函数
      • 👾 赋值运算符重载
      • 👾 析构函数
    • 总代码 🦕

前言 🦕

请添加图片描述
二叉树顾名思义即为一个树中节点的度不大于2的有序树,是树中结构中较为简单的一种;

其特点为一个节点中分为两个子树;

通常可以以二叉树进行分治的思路对问题进行解决(递归或非递归);

但在实际中普通二叉树由于在数据存储方面中并没有明显的特性,所以在实际中普通二叉树的应用场景并不多;

BinarySearchTree二叉搜索树,顾名思义这种类型的二叉树对搜索有着比较大的优势;


二叉搜索树的概念 🦕

请添加图片描述
二叉搜索树又称搜索二叉树,也可以称为排序树,顾名思义这棵树有着对数据进行搜索的优势;

该二叉树的性质为:

  • 如果这棵树的左子树不为空,则左子树上的节点值都小于根节点的值;
  • 如果这棵树的右子树不为空,则右子树上的节点值都小于根节点的值;

二叉搜索树的左右子树也同时满足该条件;

该二叉树的节点若是使用中序遍历的方式进行打印则将会以顺序的方式进行打印,所以这棵树也被称为排序树;

在这里插入图片描述

同时搜索二叉树中的数据还具有唯一性,即同一种的数据只能进行一次插入;


搜索二叉树的初始化 🦕

请添加图片描述
在实现当中搜索二叉树也应该使用类模板确保可以进行泛型编程;

搜索二叉树的初始化与普通二叉树的初始化相当,都需要两个类:

  • struct BSTNode
    该类用来对搜索二叉树的各个节点进行封装;
    template<class K>//一般情况下二叉树内的模板参数习惯以K代替(key);
    struct BSTNode{
      //构造函数
      BSTNode(const K& key = K())// 给定缺省参数确保可以正常进行初始化
      :_pleft(nullptr),//初始化列表初始化参数
      _pright(nullptr),
      _key(key)
      {}
    
      BSTNode<K> *_pleft;//左子树
      BSTNode<K> *_pright;//右子树
      K _key;//数据
    };
    
  • class BinarySearchTree
    该类主要用于实现二叉树的整体(包括各个成员函数)
    template<class K>
    class BinarySearchTree{
    typedef BSTNode<K> Node;//对节点进行typedef重命名
    public:
      BinarySearchTree(){}//构造函数
      ~BinarySearchTree(){}//析构函数
      BinarySearchTree(const BinarySearchtree<K> &node){}//拷贝构造函数
      BinarySearchTree<K>& operator=(BinarySearchTree<K> node){}//赋值运算符重载
      bool Insert(const K& key){}//数据插入
      void InOrder(){}//中序遍历打印数据
      bool Find(const K&key){}//查找数据
      bool Erase(const K&key){}//删除数据
    
    protected:
        
    private:
      Node* _proot = nullptr;//头节点
    };
    

Insert( )插入函数 🦕

请添加图片描述
插入函数的实现在搜索二叉树中一般为顺照搜索二叉树的特性进行遍历;

当遍历节点至空时则表示在该处进行插入;

  • 若是需要插入的数据大于当前节点的数据则向当前节点的右子树进行遍历;
  • 若是需要插入的数据小于当前节点的数据则向当前节点的左子树进行遍历;
  • 若是需要插入的数据等于当前节点的数据则返回false(唯一性);
  • 遍历至nullptr处则表示当前不存在节点,可进行插入,插入结束后返回true;

在这里插入图片描述

 bool Insert(const K& key){
    if(_proot == nullptr){//如果root节点为空说明为空树,空树进行第一次插入
      _proot = new Node(key);
      return true;//插入成功进行返回
    }

    Node* cur = _proot;
    Node* tmp;

    while(cur!=nullptr){
      tmp = cur;
      if(key>cur->_key){
        tmp = cur;
        cur = cur->_pright;
      }
      else if(key<cur->_key){
        tmp = cur;
        cur = cur->_pleft;
      }
      else{
        //相同 返回false
          return false;
      }
  }
    if(key>tmp->_key){//链接在右边
      cur = new Node(key);
      tmp->_pright = cur;
    }
    else{//链接在左边
      cur = new Node(key);
      tmp->_pleft = cur;
    }
   return true;
  }

该函数的插入还存在一个链接的问题;

链接的问题采用类似双指针的方式,一个指针进行遍历确认是否进行插入,另一个指针用来保存该节点的父节点位置方便两个节点进行链接;


👾 InsertR( ) 插入函数(递归)

请添加图片描述
一般来说在类中成员函数的递归比较麻烦的是对于数据的传参问题;
所以为了解决这个问题建议在自定义类型中定义递归函数时尽量写一个子函数用于递归,并使用成员函数来调用此子函数;

该函数的递归思路与非递归的思路相当,唯一不同的是在递归中除了要思考节点的插入问题同时还需要注意节点与节点之间的链接问题,在递归当中子函数的节点参数可以使用Node*& root来代替;

由于引用算是一个变量的别名,意义即为将一个指针的别名传入下一层的递归当中,将新节点插入该节点即完成插入的节点链接问题;

  bool InsertR(const K&key){//主函数
    return _InsertR(key,_proot);//调用函数进行递归
  }

    //----------------------------------

  bool _InsertR(const K&key,Node*& root){//子函数
     
     if(root == nullptr) {
     root = new Node(key);
       return true;
     }
     if(root->_key < key){
       return _InsertR(key,root->_pright);
     }
     else if(root->_key>key){
       return _InsertR(key,root->_pleft);
     }
     else return false;
   }

InOrder( ) 中序遍历打印 🦕

请添加图片描述
该函数以递归的方式进行中序遍历,使用子函数递归,主函数调用的方式即可;

  void InOrder(){//主函数
   _InOrder(_proot);
   cout<<endl;
  }
    //----------------------------------
  void _InOrder(Node *root){//子函数
    if(root == nullptr){
      return;
    }
  
    _InOrder(root->_pleft);
    cout<<root->_key<<" ";
    _InOrder(root->_pright);
  }

Find( ) 查找函数 🦕

请添加图片描述
该函数使用非递归以搜索二叉树的规则进行遍历即可;

找到数据则返回true否则返回false;

  bool Find(const K& key){
    Node*pcur = _proot;
    while(pcur){
      if(key>pcur->_key)  pcur = pcur->_pright;
      
      else if(key<pcur->_key) pcur = pcur->_pleft;
      
      else return true;
    }
    return false;
  }

👾 Find( ) 查找函数(递归)

请添加图片描述
该函数建立子函数进行递归即可;

  bool FindR(const K& key){//主函数
    return _FindR(key,_proot);
  }
    //----------------------------------
  bool _FindR(const K&key,Node*root){//子函数
    if(root == nullptr) return false;  
    if(root->_key == key) return true;
    if(root->_key>key) return _FindR(key,root->_pleft);
    else  return _FindR(key,root->_pright); 
  }  

Erase( ) 删除函数 🦕

请添加图片描述
删除函数在搜索二叉树中是一个比较复杂的函数;

复杂的问题在于:

  1. 该如何删除节点;
  2. 删除节点后如何对剩余节点进行链接;

同时从一张图的分析中可以分析出搜索二叉树删除节点的几种情况:

  1. 删除的节点不存在子树;

在这里插入图片描述

当所删除的节点不存在左右子树时直接将节点进行删除即可;


  1. 删除的节点左子树为空或者右子树为空;

在这里插入图片描述

当所删除的节点的左子树或者右子树为空时,可采用托孤的方式对节点进行链接;

即所删除节点的父节点与所删除的子节点进行链接;


  1. 删除的节点左右子树都不为空;

在这里插入图片描述

当所删除的节点其左右子树都不为空时,由于每个节点只能管理两个子树(左右子树),而所删除的节点存在左右两棵子树,故不能采用托孤的方式对所删除的节点的子树进行管理;

在这种情况下即可采用伪删除法的方式对节点进行伪删除;

伪删除即为选择一个符合接管该节点的左右子树规则的节点,将其值赋值给当前节点,并将选择的节点进行删除 (符合该种条件的节点一般为该节点左子树的最大值或该节点右子树的最小值);

以该图为例若是删除节点8,则可以使用节点7进行代替接管其左右子树,并将原节点7进行删除从而达到伪删除的目的;

实现:

 bool Erase(const K& key){

    if(nullptr == _proot) return false;
    //找到这个key值
    Node* pcur = _proot;
    Node* parent = _proot;
    while(pcur){
      //进行遍历找到对应的key值
      if(key>pcur->_key){
        parent = pcur;
        pcur = pcur->_pright;
      }
      else if(key<pcur->_key){
        parent = pcur;
        pcur = pcur->_pleft;
      }
      else{
        //已经找到了对应的key值,需要进行删除
         
        
        //左为空的情况
        if(pcur->_pleft == nullptr){
        
          //判断极端情况(左为空或者右为空的情况下所删节点为根)
          if(pcur == _proot){
              _proot = pcur->_pright;
              delete pcur;
              return true;
          }
          
            if(parent->_pleft == pcur){
            parent->_pleft = pcur->_pright;
          }
          else{
            parent->_pright = pcur->_pright;
          }
          delete pcur;
          return true;
        }

          //右为空的情况
        else if(pcur->_pright == nullptr){

          //判断极端情况(左为空或者右为空的情况下所删节点为根)
          if(pcur == _proot){
              _proot = pcur->_pleft;
              delete pcur;
              return true;
          }

          if(parent->_pleft == pcur){
            parent->_pleft = pcur->_pleft;
          }
          else {
            parent->_pright = pcur->_pleft;
          }
          delete pcur;
          return true;
        }

         //左右都不为空
        else{
           /*
            * 找到左子树的最右子树或右子树的最左子树
            */

            Node*tmp = pcur->_pright; //右子树的最左子树(最小值)
            Node*tmp_parent = pcur;
            while(tmp->_pleft){
   tmp_parent = tmp;
              tmp = tmp->_pleft;    
            }

            /*
             * 找到符合条件的值,进行伪删除法;
             */
            pcur->_key = tmp->_key;
              //说明该子树存在孩子,需要对该子树进行托孤
              //由于所找的数据为该节点右子树的最小值,说明找到的这个最小值不存在左子树,只需要对右子树进行托孤;
              if(tmp_parent->_pleft == tmp){
                tmp_parent->_pleft = tmp->_pright;
                delete tmp;
              }
              else{
                tmp_parent->_pright = tmp->_pright;
                delete tmp;
              }
            return true;
        }

      }
    }
    //未找到key值,pcur指针已经为空,返回false;
    return false;
  }

👾 Erase( ) 函数(递归)

请添加图片描述
对于Erase()函数的递归方式与其非递归的方式都有异曲同工之处;

唯一不同的是为了节点链接的方便在用于递归的子函数当中使用指针引用的方式完成节点的链接;

  bool EraseR(const K&key){//主函数
    return _EraseR(key,_proot);
  }

    //----------------------------------


bool _EraseR(const K& key, Node*& root){//子函数

    if(root == nullptr) return false;

    if(key>root->_key) return _EraseR(key,root->_pright);
    else if(key<root->_key) return _EraseR(key,root->_pleft);
    else{
    
      Node *cur = root;
      //左为空
      if(root->_pleft == nullptr){
        root = root->_pright;
        delete cur;
        return true;
      } 
        
      //右为空 
      else if(root->_pright == nullptr){
        root = root->_pleft;
        delete cur;
        return true;
      }

      //左右都不为空
      else{
         cur = root->_pleft; 
         while(cur->_pright) cur = cur->_pright;
         root->_key = cur->_key;
         _EraseR(cur->_key,root->_pleft);
         return true; 
      } 
    }
}

默认成员函数 🦕

👾 构造函数

请添加图片描述
搜索二叉树的构造函数即对节点进行初始化即可,在不存在其他的构造函数的情况下可以使用默认生成的构造函数,因为由于在定义成员变量时给定了成员变量了缺省参数_proot = nullptr;

若是存在其他构造函数 (拷贝构造函数也为构造函数的一种) 时则需要手写这个构造函数;
或是使用default关键字:

BinarySearchTree() = default;

👾 拷贝构造函数

请添加图片描述
拷贝构造函数则可以使用递归的方式逐个对节点进行拷贝从而达到深拷贝的效果;

  BinarySearchTree(const BinarySearchTree<K> &node){//主函数
     _proot =  _CopyNode(node._proot);
  }
    //----------------------------------
  Node* _CopyNode(const Node* node){//子函数
    if(node == nullptr) {
      return nullptr;
    }
    Node*newnode = new Node(node->_key);
    newnode->_pleft = _CopyNode(node->_pleft);
    newnode->_pright = _CopyNode(node->_pright);
    return newnode;
  }

👾 赋值运算符重载

请添加图片描述
赋值运算符重载一般采用现代的写法:

即为利用自定义类型在进行传值传参时会进行拷贝构造的特性完成对该自定义类型的拷贝,再进行交换与返回;

  BinarySearchTree<K>& operator=( BinarySearchTree<K> node){//在传值传参过程中调用拷贝构造生成了临时拷贝(深拷贝)
    swap(_proot,node._proot);
    return *this;
  }

👾 析构函数

请添加图片描述
析构函数采用父子函数的方式,利用子函数作递归,主函数调用该子函数即可;

void _Destroy(Node*& root){
  if(root == nullptr) return;
  _Destroy(root->_pleft);
  _Destroy(root->_pright);
  delete root;
  root = nullptr;
}
    //----------------------------------
    
~BinarySearchTree(){
  _Destroy(_proot);
}

总代码 🦕

请添加图片描述

#pragma once 

#include<iostream>

using namespace std;

template<class K>
struct BSTNode{

  BSTNode(const K& key = K())
  :_pleft(nullptr),
  _pright(nullptr),
  _key(key)
  {}

  BSTNode<K> *_pleft;
  BSTNode<K> *_pright;
  K _key;
};



template<class K>
class BinarySearchTree{
  typedef BSTNode<K> Node;
  public:

  /*
   * 默认成员函数
   */ 


  //--------------------------------
  
  //构造函数
  
  BinarySearchTree()=default;
          /*强制生成默认构造函数*/

  //--------------------------------

  //拷贝构造

  BinarySearchTree(const BinarySearchTree<K> &node){
     _proot =  _CopyNode(node._proot);
  }

  //--------------------------------
  
  //赋值重载操作符
  
  BinarySearchTree<K>& operator=( BinarySearchTree<K> node){
    swap(_proot,node._proot);
    return *this;

  }

  //--------------------------------

  //析构函数 - 使用递归方法 后序遍历进行Destory
    
  ~BinarySearchTree(){
    _Destroy(_proot);
  }

  //--------------------------------
  bool Insert(const K& key){
    if(_proot == nullptr){//如果root节点为空说明为空树,空树进行第一次插入
      _proot = new Node(key);
      return true;//插入成功进行返回
    }

    Node* cur = _proot;
    Node* tmp;

    while(cur!=nullptr){
      tmp = cur;
      if(key>cur->_key){
        tmp = cur;
        cur = cur->_pright;
      }
      else if(key<cur->_key){
        tmp = cur;
        cur = cur->_pleft;
      }
      else{
        //相同 返回false
          return false;
      }
  }
    if(key>tmp->_key){//链接在右边
      cur = new Node(key);
      tmp->_pright = cur;
    }
    else{//链接在左边
      cur = new Node(key);
      tmp->_pleft = cur;
    }
   return true;
  }
 
  //-----------------------

  void InOrder(){
    /*
     * 自定义类型成员函数若是需要进行递归时
     * 尽量设置一个子函数来方便递归并使用原成员函数进行调用
     * 
     * 在该自定义类型当中,由于传的参数为private私有成员变量
     * 私有成员变量不方便直接进行传参,所以为了方便传参_proot
     * 在private访问限定符的域当中设置一个子函数用来递归
     * 通过原函数去调用这个私有成员函数
     */
   _InOrder(_proot);
   cout<<endl;
  }

  //-----------------------

  bool Find(const K& key){
    Node*pcur = _proot;
    while(pcur){
      if(key>pcur->_key)  pcur = pcur->_pright;
      
      else if(key<pcur->_key) pcur = pcur->_pleft;
      
      else return true;
    }
    return false;
  }



  //-----------------------
  
  bool Erase(const K& key){

    if(nullptr == _proot) return false;
    //找到这个key值
    Node* pcur = _proot;
    Node* parent = _proot;
    while(pcur){
      //进行遍历找到对应的key值
      if(key>pcur->_key){
        parent = pcur;
        pcur = pcur->_pright;
      }
      else if(key<pcur->_key){
        parent = pcur;
        pcur = pcur->_pleft;
      }
      else{
        //已经找到了对应的key值,需要进行删除
         
        
        //左为空的情况
        if(pcur->_pleft == nullptr){
         
          if(pcur == _proot){
              _proot = pcur->_pright;
              delete pcur;
              return true;
          }
          
            if(parent->_pleft == pcur){
            parent->_pleft = pcur->_pright;
          }
          else{
            parent->_pright = pcur->_pright;
          }
          delete pcur;
          return true;
        }

          //右为空的情况
        else if(pcur->_pright == nullptr){

          //判断极端情况(左为空或者右为空的情况下所删节点为根)
          if(pcur == _proot){
              _proot = pcur->_pleft;
              delete pcur;
              return true;
          }

          if(parent->_pleft == pcur){
            parent->_pleft = pcur->_pleft;
          }
          else {
            parent->_pright = pcur->_pleft;
          }
          delete pcur;
          return true;
        }

         //左右都不为空
        else{
           /*
            * 找到左子树的最右子树或右子树的最左子树
            */

            Node*tmp = pcur->_pright; //右子树的最左子树(最小值)
            Node*tmp_parent = pcur;
            while(tmp->_pleft){
              tmp_parent = tmp;
              tmp = tmp->_pleft;    
            }

            /*
             * 找到符合条件的值,进行伪删除法;
             */
            pcur->_key = tmp->_key;
              //说明该子树存在孩子,需要对该子树进行托孤
              //由于所找的数据为该节点右子树的最小值,说明找到的这个最小值不存在左子树,只需要对右子树进行托孤;
              if(tmp_parent->_pleft == tmp){
                tmp_parent->_pleft = tmp->_pright;
                delete tmp;
              }
              else{
                tmp_parent->_pright = tmp->_pright;
                delete tmp;
              }
            return true;
        }
      }
    }
    //未找到key值,pcur指针已经为空,返回false;
    return false;
  }

  //-----------------------

  bool FindR(const K& key){
    return _FindR(key,_proot);
  }
  //-----------------------

  bool InsertR(const K&key){
    return _InsertR(key,_proot);
  }

  //-----------------------
  
  bool EraseR(const K&key){
    return _EraseR(key,_proot);
  }
  
  //-----------------------
  
  private:
  /*
   * 私有成员包括成员函数以及成员变量
   */


   Node *_proot = nullptr; 

  //-----------------------

  protected:


  void _InOrder(Node *root){
    if(root == nullptr){
      return;
    }
  
    _InOrder(root->_pleft);
    cout<<root->_key<<" ";
    _InOrder(root->_pright);
  }
  //-----------------------
    bool _FindR(const K&key,Node*root){
      if(root == nullptr) return false;
      
      if(root->_key == key) return true;

      if(root->_key>key) return _FindR(key,root->_pleft);
      else  return _FindR(key,root->_pright);
    
    
    }  
  //-----------------------

    bool _InsertR(const K&key,Node*& root){
      //纯高效法
      /*
       * 该方法重点在于使用了一个引用来引用指针,
       * 指针既可以为判断条件(为nullptr),
       * 也不缺乏其为上一个节点的左\右子树;
       * */
      if(root == nullptr) {
        root = new Node(key);
        return true;
      }

      if(root->_key < key){
        return _InsertR(key,root->_pright);
      }

      else if(root->_key>key){
        return _InsertR(key,root->_pleft);
      
      }


      else return false;

    }
		//-----------------------

		bool _EraseR(const K& key, Node*& root){

			if(root == nullptr) return false;

			if(key>root->_key) return _EraseR(key,root->_pright);
			else if(key<root->_key) return _EraseR(key,root->_pleft);
			else{
        /*
         * 在该递归删除中同样利用到了指针引用的方式 // 具体能够指针引用是因为递归每次都会开辟出新的栈帧
         * 解决了大部分的判断问题(具体表现在左为空或右为空)
         * 在判断两个子树都不为空的条件下,具体的思路是利用了循环找到可以进行伪删除法的数,再重新调用该子函数完成删除
         *  其想法可以理解为将问题划分为子问题
         *  (将左右均不为空的方式在进行一系列操作后将其最终以左为空或者右为空的问题进行解决);
         * */
        
        Node *cur = root;
          //左为空
        if(root->_pleft == nullptr){
          root = root->_pright;
          delete cur;
          return true;
        } 
        
          //右为空 
        else if(root->_pright == nullptr){
          root = root->_pleft;
          delete cur;
          return true;
        }

          //左右都不为空
        else{
         cur = root->_pleft; 
         while(cur->_pright) cur = cur->_pright;
         root->_key = cur->_key;
         _EraseR(cur->_key,root->_pleft);
         return true; 
        }  


			}
		}

		//-----------------------

    Node* _CopyNode(const Node* node){
      if(node == nullptr) {
        return nullptr;
      }
      Node*newnode = new Node(node->_key);
      newnode->_pleft = _CopyNode(node->_pleft);
      newnode->_pright = _CopyNode(node->_pright);
      return newnode;
    }

    //------------------------

    void _Destroy(Node*& root){
      if(root == nullptr) return;
      _Destroy(root->_pleft);
      _Destroy(root->_pright);
      delete root;
      root = nullptr;
    }

};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Ajax】发送get请求获取接口数据

编写html实现通过Ajax发送get请求并获取数据 代码实现 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…

Libsvm中grid.py文件的解读

1.导入相关文件 这里重点讲一下 _ _all_ _ [find_parameters] &#xff1a; _all__ [find_parameters] 是 Python 中用于定义模块级别的变量 __all__ 的语法&#xff0c; __all__ 是一个包含模块中应该被公开&#xff08;即可以通过 from module import * 导入&#xff09;的…

【开源】基于Vue+SpringBoot的快乐贩卖馆管理系统

项目编号&#xff1a; S 064 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S064&#xff0c;文末获取源码。} 项目编号&#xff1a;S064&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 搞笑视频模块2.3 视…

CentOS上安装和配置Apache HTTP服务器

在CentOS系统上安装和配置Apache HTTP服务器可以为您的网站提供可靠的托管环境。Apache是开源的Web服务器软件&#xff0c;具有广泛的支持和强大的功能。下面是在CentOS上安装和配置Apache HTTP服务器的步骤&#xff1a; 步骤一&#xff1a;安装Apache HTTP服务器 打开终端&am…

总结一篇本地idea配合阿里云服务器使用docker

idea打包打镜像发到阿里云服务器 先说一下使用docker desktop软件怎么使用 1.下载docker desktop官网&#xff0c;先注册个账号吧&#xff0c;后面桌面软件登录会用到&#xff08;当然&#xff0c;配合这个软件使用需要科学上网&#xff09; 安装这个要配合wsl使用&#xf…

电脑搜不自己的手机热点,其余热点均可!

一、现象&#xff1a; 之前可正常连接&#xff0c;突然间发现收不到自己的WiFi信号&#xff0c;其余人均可收到。通过重复手机电脑关机、改变热点设置中的频段等方式均没解决&#xff0c;同事电脑和手机可搜索到我的WiFi。 二、问题&#xff1a; WiF驱动程序更新 三&#x…

ingress介绍和ingress通过LoadBalancer暴露服务配置

目录 一.ingress基本原理介绍 1.将原有用于暴露服务和负载均衡的服务的三四层负载均衡变为一个七层负载均衡 2.controller和ingress 3.通过下面这个图可能会有更直观的理解 二.为什么会出现ingress 1.NodePort存在缺点 2.LoadBalancer存在缺点 三.ingress三种暴露服务的…

『TypeScript』从零开始编写你的第一个TypeScript程序

&#x1f4e3;读完这篇文章里你能收获到 了解TypeScript及为什么使用TypeScriptTypeScript的安装过程编写第一个HelloTs程序 文章目录 一、TypeScript简介1. 什么是TypeScript&#xff1f;2. 为什么选择使用TypeScript&#xff1f;2.1 静态类型检查2.2 更好的代码维护性2.3 更…

Prometheus 配置文件和标签 Pmsql

1.Prometheus配置文件 Prometheus可以通过命令行或者配置文件的方式对服务进行配置。 命令行方式一般用于不可变的系统参数配置&#xff0c;例如存储位置、要保留在磁盘和内存中的数据量等&#xff1b;配置文件用于定义与数据动态获取相关的配置选项和文件等内容。命令行方式…

Sql Server关于表的建立、修改、删除

表的创建&#xff1a; &#xff08;1&#xff09;在“对象资源管理器”面板中展开“数据库”节点&#xff0c;可以看到自己创建的数据库&#xff0c;比如Product。展开Product节点&#xff0c;右击“表”节点&#xff0c;在弹出的快捷菜单中选择“新建表”项&#xff0c;进入“…

【keil备忘录】2. stm32 keil仿真时的时间测量功能

配置仿真器Trace内核时钟为单片机实际的内核时钟&#xff0c;需要勾选Enable设置&#xff0c;设置完成后Enable取消勾选也可以&#xff0c;经测试时钟频率配置仍然生效&#xff0c;此处设置为48MHZ: 时间测量时必须打开register窗口&#xff0c;否则可能不会计数 右下角有计…

AMD 发布新芯片MI300,支持训练和运行大型语言模型

AMD 宣布推出 MI300 芯片&#xff0c;其 Ryzen 8040移动处理器将于2024年用于笔记本电脑。 AMD官方网站&#xff1a;AMD ׀ together we advance_AI AMD——美国半导体公司专门为计算机、通信和消费电子行业设计和制造各种创新的微处理器&#xff08;CPU、GPU、主板芯片组、电…

【AIGC】prompt工程从入门到精通

注&#xff1a;本文示例默认“文心大模型3.5”演示&#xff0c;表示为>或w>&#xff08;wenxin)&#xff0c;有时为了对比也用百川2.0展示b>&#xff08;baichuan) 有时候为了模拟错误输出&#xff0c;会用到m>&#xff08;mock)表示&#xff08;因为用的大模型都会…

SLAM算法与工程实践——SLAM基本库的安装与使用(3):Pangolin库

SLAM算法与工程实践系列文章 下面是SLAM算法与工程实践系列文章的总链接&#xff0c;本人发表这个系列的文章链接均收录于此 SLAM算法与工程实践系列文章链接 下面是专栏地址&#xff1a; SLAM算法与工程实践系列专栏 文章目录 SLAM算法与工程实践系列文章SLAM算法与工程实践…

网络管理相关

管理功能分为管理站manager和代理agent两部分。 网络管理&#xff1a; 网络管理系统中&#xff0c;每一个网络节点都包含有一组与管理有关的软件&#xff0c;叫做网络管理实体NME。 管理站的另外一组软件叫做网络管理应用NMA&#xff0c;提供用户接口&#xff0c;根据用户命令显…

http与apache

目录 1.http相关概念 2.http请求的完整过程 3.访问浏览器背后的原理过程 4.动态页面与静态页面区别 静态页面&#xff1a; 动态页面&#xff1a; 5.http协议版本 6.http请求方法 7.HTTP协议报文格式 8.http响应状态码 1xx&#xff1a;提示信息 2xx&#xff1a;成功…

css 十字分割线(含四等分布局)

核心技术 伪类选择器含义li:nth-child(2)第2个 lili:nth-child(n)所有的lili:nth-child(2n)所有的第偶数个 lili:nth-child(2n1)所有的第奇数个 lili:nth-child(-n5)前5个 lili:nth-last-child(-n5)最后5个 lili:nth-child(7n)选中7的倍数 border-right: 3px solid white;borde…

ssh安装和Gitee(码云)源码拉取

文章目录 安装ssh服务注册码云公钥设置码云账户SSH公钥安装git客户端和git-lfs源码获取 安装ssh服务 更新软件源&#xff1a; sudo apt-get update安装ssh服务 sudo apt-get install openssh-server检查ssh是否安装成功 which ssh输出&#xff1a; /usr/bin/ssh启动ssh 服…

AI并行计算:CUDA和ROCm

1 介绍 1.1 CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是Nvidia于2006年推出的一套通用并行计算架构&#xff0c;旨在解决在GPU上的并行计算问题。其易用性和便捷性能够方便开发者方便的进行GPU编程&#xff0c;充分利用GPU的并行能力&#xff0…

PHP对接企业微信

前言 最近在做项目中&#xff0c;要求在后台管理中有企业微信管理的相关功能。相关准备工作&#xff0c;需要准备好企业微信账号&#xff0c;添加自建应用&#xff0c;获得相应功能的权限&#xff0c;以及agentid、secre等。 参考文档&#xff1a; 企业微信开发文档 功能实现 因…