『 C++ 』BinarySearchTree搜索二叉树

news2024/10/2 22:13:07

请添加图片描述


文章目录

    • 前言 🦕
    • 二叉搜索树的概念 🦕
    • 搜索二叉树的初始化 🦕
    • 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;
    }

};

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

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

相关文章

系列学习前端之第 2 章:一文精通 HTML

全套学习 HTMLCSSJavaScript 代码和笔记请下载网盘的资料&#xff1a; 链接: https://pan.baidu.com/s/1-vY2anBdrsBSwDZfALZ6FQ 提取码: 6666 HTML 全称&#xff1a;HyperText Markup Language&#xff08;超文本标记语言&#xff09; 1、 HTML 标签 1. 标签又称元素&#…

第二十一章总结博客

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmission …

区块链媒体:Web3.0时代的推广创新10爆款策略概览-华媒舍

随着Web3.0时代的到来&#xff0c;互联网推广正经历着一场创新的革命。在这个新的时代背景下&#xff0c;一系列全新的推广策略正在兴起&#xff0c;引领着市场的变革。本文将基于这一背景&#xff0c;为大家介绍Web3.0时代中的10大爆款推广策略概览。 1. 个性化推广 在Web3.0…

Linux:缓冲区的概念理解

文章目录 缓冲区什么是缓冲区&#xff1f;缓冲区的意义是什么&#xff1f;缓冲区的刷新方式 理解缓冲区用户缓冲区和内核缓冲区缓冲区在哪里&#xff1f; 本篇主要总结的是关于缓冲区的概念理解&#xff0c;以及再次基础上对文件的常用接口进行一定程度的封装 缓冲区 什么是缓…

基于ssm家庭理财系统源码和论文

基于ssm家庭理财系统源码和论文743 idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 环境&#xff1a; jdk8 tomcat8.5 开发技术 ssm 摘要 随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff…

Linux_CentOS_7.9配置oracle sqlplus、rman实现上下按键切换历史命令等便捷效率功能之简易记录

配置oracle sqlplus以及rman可以上下按键切换历史命令等便捷效率功能 设置前提是已经yum安装了rlwrap软件具体软件下载及配置参考文章http://t.csdnimg.cn/iXuVK su - oracleVim .bash_profile ## 文件中增加如下的别名设置 ---------------- alias sqlplusrlwrap sqlplus…

Android音量调节参考一

基于android 9平台分析。 在Android系统中&#xff0c;默认的设备(phone等)音量都是分开控制的&#xff0c;这些包括媒体、铃声、闹铃、蓝牙、通话通过音频流来区别不同的音量类型。每种流类型都定义最大音量、最小音量及默认音量&#xff0c;Android 9定了了11中音频流类型&am…

【MATLAB】MVMD信号分解+FFT+HHT组合算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 MVMD信号分解FFTHHT组合算法是一种强大的分析方法&#xff0c;结合了变分模态分解(MVMD)、快速傅里叶变换(FFT)和希尔伯特-黄变换(HHT)。 首先&#xff0c;使用MVMD将原始信号分解成多个…

几分钟在Ubuntu搭建本地Emlog博客网站并发布至公网无需购买域名服务器

文章目录 前言1. 网站搭建1.1 Emolog网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总结 前言 博客作为使…

Python-封装配置文件

Code [url] baidu http://www.baidu.com[value] send_value 百度[server] ip 220.181.111.188封装的格式可以套用 # 封装,类似函数调用 import configparserclass ReadConfigIni():def __init__(self,filename):self.cf configparser.ConfigParser()self.cf.read(filenam…

分布式搜索引擎03

1.数据聚合 聚合(aggregations)可以让我们极其方便的实现对数据的统计、分析、运算。例如: 什么品牌的手机最受欢迎? 这些手机的平均价格、最高价格、最低价格? 这些手机每月的销售情况如何? 实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近…

[Linux] nginx配置的主配置文件

一、六个模块的作用 全局块&#xff1a;全局配置&#xff0c;对全局生效&#xff1b; events块&#xff1a;配置影响 Nginx 服务器与用户的网络连接&#xff1b; http块&#xff1a;配置代理&#xff0c;缓存&#xff0c;日志定义等绝大多数功能和第三方模块的配置&#xff1b;…

STM32 cubeMX 呼吸灯实验

文章代码使用 HAL 库。 文章目录 一、1.PWM原理二、LED 原理图三、使用cubemx 配置 led四、PWM 相关函数五、PWM占空比占空比计算六、PWM 呼吸灯重要代码总结 呼吸灯 一、1.PWM原理 PWM全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;是一种常…

【Linux】进程通信之命名管道mkfifo

1.认识命名管道 匿名管道应用的一个限制就是只能在具有共同祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。如果我们想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作&#xff0c;它经常被称为命名管道。命名管道是一种特殊类型的文件 2.在命…

华清远见嵌入式学习——QT——作业1

作业要求&#xff1a; 代码&#xff1a; ①&#xff1a;头文件 #ifndef LOGIN_H #define LOGIN_H#include <QWidget> #include <QLineEdit> //行编辑器类 #include <QPushButton> //按钮类 #include <QLabel> //标签类 #include <QM…

(五) Python 代理模式

文章目录 5.1 代理模式概述5.1.1 代理介绍5.1.2 代理模式的作用 5.2 代理模式的UML类图5.3 了解不同类型的代理5.3.1虚拟代理5.3.2 远程代理5.3.3 保护代理5.3.4 智能代理 5.4 现实世界中的代理模式5.5 代理模式的优点5.6 门面模式和代理模式之间的比较 5.1 代理模式概述 5.1.…

华为鸿蒙开发——开发及引用静态共享包(HAR)、应用配置文件

文章目录 简述一、创建HAR模块二、编译HAR模块三、应用配置文件&#xff08;Stage模型&#xff09;四、应用配置文件&#xff08;FA模型&#xff09;1、配置文件的内部结构&#xff08;1&#xff09;app&#xff08;2&#xff09;deviceConfig&#xff08;3&#xff09;module …

GDPU 数据结构 天码行空13

文章目录 一、【实验目的】二、【实验内容】三、实验源代码四、实验结果五、实验总结 一、【实验目的】 (1) 理解插入排序算法的实现过程&#xff1b; &#xff08;2&#xff09;理解不同排序算法的时间复杂度及适用环境&#xff1b; &#xff08;3&#xff09;了解算法性能…

高精度时钟芯片SD2405

概要 SD2405是一款非常优秀的RTC解决方案&#xff0c;为了能让用户在Arduino上有一款方便易用的时钟模块。该模块是一款内置晶振&#xff0c;支持IIC串行接口的高精度时钟模块&#xff1b;内置一次性工业级电池&#xff0c;可保证外部掉电的情况下&#xff0c;可以继续工作5~8…

华为数通方向HCIP-DataCom H12-831题库(多选题:161-180)

第161题 以下关于IS-IS路由渗透的描述,正确的有哪些项? A、若要配置Level-2区域的路由向Level-1区域渗透,则需要在Level-1设备上配置命令import-routeisis level_-2 into level_-1 B、缺省情况下,Level-2区域无Level-1区域的路由信息,需要通过在Level-1-2设备上配置impor…