【数据结构】红黑树

文章目录

  • 1. 红黑树的概念与性质
    • 1.1 概念
    • 1.2 性质
  • 2. 红黑树的实现
    • 2.1 节点和结构的定义
    • 2.2 红黑树的节点插入(重点!!!!)
  • 3. 红黑树的验证与性能分析
    • 3.1红黑树的验证
    • 3.2红黑树的性能分析——与AVL树的对比
    • 3.3红黑树的应用

1. 红黑树的概念与性质

1.1 概念

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

image-20230518002619961

1.2 性质

红黑树的性质有以下几条,相较于AVL树使用平衡因子来限制绝对平衡,红黑树的性质是更加重要的,红黑树能够保证接近平衡的原理是通过这几条性质来对结构进行限制的。

  1. 每个节点的颜色只能是红色或者黑色
  2. 红黑树的根节点是只能是黑色
  3. 如果一个节点是红色的,那么他的孩子节点只能是黑色(这里只说了红节点的孩子不能是红色,没说黑节点的孩子不能是黑色
  4. 对于任意一个节点,从该节点开始到后叶子节点的简单路径上,黑色节点的个数是相同的(路径是指从该节点开始直到空节点为止,是把空节点也算作一个节点
  5. 每个叶子节点的颜色都是黑色的(这里的叶子节点指的是空节点)

那么问题来了,为什么这些性质就能够使最长路径的节点个数不会超过最短路径节点个数的两倍

首先,我们想一想:从一个节点,从它向下的所有到叶子节点的路径上,黑色节点的个数相同的情况下,如果让路径最短,那么就只能让所有节点都是黑色节点,假设有n个,那么最长的路径的情况就是红-黑-红,由于根节点和叶子节点都只能是黑色,所以最长的路径的情况就是一黑一红交替出现,长度最多是2*n,这样就能够确保没有一条路径会比其他路径长出两倍。

2. 红黑树的实现

2.1 节点和结构的定义

首先,由于红黑树是使用红色或者黑色来标志节点的,所以这里可以使用一个枚举类型来对颜色进行限制

enum Color
{
    RED,
    BLACK
};

然后考虑节点的结构,实际上节点的结构和AVL树基本相同,只是不需要平衡因子,而是变成了颜色标识,所以结构如下

template<class K, class V>
struct RBTreeNode
{
    pair<K,V> _kv;
    RBTreeNode* _left;
    RBTreeNode* _right;
    RBTreeNode* _parent;
    Color _col;//颜色
    RBTreeNode(const pair<K,V> kv)//构造函数
        :_kv(kv)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_col(RED)
    {}
};

现在节点的结构已经有了,然后要考虑的就是树的结构,树的结构和AVL树是一样的,只需要一个根节点成员变量即可

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;//为了方便使用,重定义一下节点
public:
    //...
private:
    Node* _root = nullptr;
}

2.2 红黑树的节点插入(重点!!!!)

红黑树的本质还是二叉搜索树,所以插入的过程和二叉搜索树一样,需要先找到插入位置,然后new一个节点插入进去。

然后对于红黑树的特殊结构,需要进行判断,判断插入之后的红黑树的结构是否符合限制条件即可

🤔️插入的节点的默认颜色应该是什么?

✅应该是红色,如果新插入的节点是黑色的话,就一定不会符合条件。因为在插入之前,树的结构肯定是满足红黑树性质的,所以黑色节点的个数是固定的,如果再次插入一个黑色节点的话,那么结构一定会被破坏,但是插入红色节点只是有可能破坏结构,所以这里选择默认插入节点的颜色是红色,然后再判断。

有了上面的基础,现在就可以进入到插入的重点部分——调节

这里我们假设parent是grandfather的左节点,对于另一种情况也是相同的处理方式,对称过去即可

1⃣️首先,已知了插入的节点是红色的,默认在插入之前树是一个红黑树,由于条件限制不能出现连续的红色节点,所以需要判断一下parent是否是红色,如果不是,那么就不用做任何操作,插入过程直接完成。

如果parent是红色节点的话,就去看uncle是否是红色,如果也是红色的话,那么就将parent和uncle的颜色变成黑色,然后grandfather变成红色,这样的话grandfather路径下的所有路径的黑色节点个数不会发生改变,也满足了不能出现连续红节点的性质。

image-20230330120556517

但是,此时改变了grandfather的颜色,不确定grandfather上面的节点是否满足性质,所以需要向上迭代,把grandfather当作新插入的节点来再次判断,直到满足性质或者到根节点。

❕这里已知cur和parent都是红色时,可以推测出grandfather一定是黑色

2⃣️如果uncle是黑色的话,就不能直接变成红色了,因为要保证每条路径下黑色节点的个数不变,所以如果走到这里,那就说明现在的结构是出现问题的,需要进行调整,这里的调整方式也是进行旋转,这里旋转的方式和AVL树的旋转也是相同的,所以就不赘述了,想了解的话可以看看我的这篇博客链接🔗。这里只需要用到左单旋和右单旋

这里可以再分为两类:

1⃣️cur是parent的左节点,也就是grandfather、parent、cur在一条直线上

image-20230518164146282

此时只需要对grandfather进行右单旋,让parent作为新的”根“节点,然后cur和grandfather分别作为parent的左右子树即可,然后调节一下节点的颜色,让“根”节点——parent变成黑色,其余两个变成红色,然后就完美解决了问题。

2⃣️cur是parent的右节点

image-20230518164250175

此时,可以参考之前对于AVL树的处理,只对grandfather进行一次旋转是不可能解决问题的,所以需要先对parent进行一次左单旋,,此时变成了上一种情况,然后再对grandfather进行一次右单旋,最终的结构就是cur作为parent和grandfather的父节点,然后再处理节点颜色:cur变为黑色,parent和grandfather变成红色。

然后以相同的方式处理parent是grandfather的右节点即可。

最后,再次把根节点的颜色置为黑色,防止前面的各种修改导致根节点的颜色不符合性质。

//红黑树插入代码
bool Insert(const pair<K,V>& kv)
{
  if(_root == nullptr)
  {
    _root = new Node(kv);
    _root->_col = BLACK;
    return true;
  }
  Node* cur = _root;
  Node* parent = nullptr;
  //找到插入位置
  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;
  //连接上
  cur->_parent = parent;
  if(parent->_kv.first > cur->_kv.first)
  {
    parent->_left = cur;
  }
  else
  {
    parent->_right = cur;
  }
  //判断颜色是否合法
  while(parent && cur->_parent->_col == RED)
  {
    Node* grandfather = parent->_parent;
    if(parent == grandfather->_left)//当parent是grandfather左节点
    {
      Node* uncle = grandfather->_right;
      //情况一:uncle存在且为红
      if(uncle && uncle->_col == RED)
      {
        grandfather->_col = RED;
        uncle->_col = parent->_col = BLACK;

        cur = grandfather;
        parent = cur->_parent;
      }
      //出现下面的情况就说明树的结构出现问题,需要对结构进行调整(旋转)
      else//uncle不存,或者在且为黑
      {
        //情况二:grandfather、parent、cur在一条直线上
        if(parent->_left == cur)
        {
          RotateR(grandfather);

          grandfather->_col = RED;
          parent->_col = BLACK;
        }
        //情况二:grandfather、parent、cur在一条折线上
        else
        {
          RotateL(parent);
          RotateR(grandfather);

          cur->_col = BLACK;
          grandfather->_col = RED;
        }
        break;
      }
    }
    else//当parent是grandfather右节点
    {
      Node* uncle = grandfather->_left;
      //情况一:uncle存在且为红
      if(uncle && uncle->_col == RED)
      {
        parent->_col = uncle->_col = BLACK;
        grandfather->_col = RED;

        cur = grandfather;
        parent = cur->_parent;
      }
      else//uncle不存在,或者存在且为黑
      {
        //情况二:grandfather、parent、cur在一条直线上
        if(parent->_right == cur)
        {
          RotateL(grandfather);

          grandfather->_col = RED;
          parent->_col = BLACK;
        }
        //情况二:grandfather、parent、cur在一条折线上
        else
        {
          RotateR(parent);
          RotateL(grandfather);

          grandfather->_col = RED;
          cur->_col = BLACK;
        }
        break;
      }
    }
  }
  _root->_col = BLACK;
  return true;
}
//左单旋
void RotateL(Node* parent)
{
  Node* subR = parent->_right;
  Node* subRL = subR->_left;
  Node* ppNode = parent->_parent;

  //处理subRL的部分
  parent->_right = subRL;
  if(subRL)
    subRL->_parent = parent;
  //处理parent和subR之间的关系
  subR->_left = parent;
  parent->_parent = subR;
  //处理subR的parent
  if(ppNode)//parent不是根节点的时候,需要处理parent的父亲节点
  {
    subR->_parent = ppNode;
    if(ppNode->_left == parent)//parent是左节点
    {
      ppNode->_left = subR;
    }
    else//parent是右节点
    {
      ppNode->_right = subR;
    }
  }
  else//parent是根节点的时候
  {
    _root = subR;
    subR->_parent = nullptr;
  }
}
//右单旋
void RotateR(Node* parent)
{
  Node* subL = parent->_left;
  Node* subLR = subL->_right;
  Node* ppNode = parent->_parent;

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

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

补充说明:红黑树节点的删除

同样的,这里不做讲解,有兴趣的小伙伴可以去参考一下《算法导论》或者《STL源码剖析》

同时,这里推荐一篇博客:博客链接

3. 红黑树的验证与性能分析

3.1红黑树的验证

红黑树首先一定是一个二叉搜索树,所以这里首先对树进行中序遍历,判断遍历结果是否是有序的,如果是有序的证明是二叉搜索树,然后再对其进行红黑树特有性质的判断;

红黑树没有办法直接判断,由于红黑树的结构是由多条性质来对结构进行限制的,所以这里可以通过对每条性质的判断来做到判断红黑树的结构

  1. 针对第一条性质,在实现的时候使用了枚举的方式就已经限制过了,所以不需要再次判断
  2. 对于第二条性质,直接判断即可
  3. 对于第五条性质,直接这样理解即可,因为空节点实际上没有颜色
  4. 对于第三条性质,这里建议去判断该节点的颜色与其父节点的颜色而不是子节点,因为父节点只有一个,更加方便
  5. 对于第四条性质,这里可以首先任选一条路径,计算出从根节点开始直到叶子节点的黑色节点个数ref,然后使用DFS遍历所有路径,计算黑色节点个数与ref比较,如果不相等就返回false。
//红黑树的验证代码
void InOrder(vector<pair<K,V>>& v, Node* root)//中序遍历
{
	if(root == nullptr)
    return;
  InOrder(v, root->_left);
  v.push_back(root->_kv);
  InOrder(v, root->_right);
}
bool isBSTree(Node* root)//判断是否是二叉搜索树
{
  vector<pair<K,V>> v;
  InOrder(v,root);
  for(size_t i = 0; i < v.size() - 1; ++i)
  {
    if(v[i].first > v[i+1].first)
      return false;
  }
  return true;
}
bool Check(Node* root, int blackNum, int ref)
{
  if(root == nullptr)//如果已经是空树,那么判断当前路径黑色节点个数与其他路径是否相等
  {
    if(blackNum != ref)
    {
      cout << "count of black is error" << endl;
      return false;
    }
    return true;
  }
  if(root->_col == RED && root->_parent->_col == RED)//判断当前节点与父节点的颜色是否都是红色
  {
    cout << "more red node" << endl;
    return false;
  }
  if(root->_col == BLACK)//如果当前节点是黑色,就在当前路径黑节点个数下加一
    ++blackNum;
  return Check(root->_left, blackNum, ref)
    && Check(root->_right, blackNum, ref);
}
bool isRBTree()
{
  if(_root == nullptr)//空树也是红黑树
    return true;
  if(isBSTree(_root))//判断是否是二叉搜索树
  {
    if(_root->_col != BLACK)//判断根节点是否是黑色,如果不是直接返回false
      return false;
    //计算出当前节点下的其中一个路径下黑色节点的个数
    int ref = 0;
    Node* left = _root;
    while(left)
    {
      if(left->_col == BLACK)
        ++ref;
      left = left->_left;
    }
    //判断是否满足对于任意节点下的所有路径下黑色节点个数是否相等
    return Check(_root, 0, ref);
  }
  else
    return false;
}

3.2红黑树的性能分析——与AVL树的对比

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

3.3红黑树的应用

  1. C++ STL库——map/set、mutil_map/mutil_set
  2. Java 库
  3. Linux内核
  4. 其他一些库

最后,附上本节源码

/***********RBTree.hpp****************/
//
//  RBTree.hpp
//  RBTree
//
//  Created by zht on 2023/5/14.
//

#pragma once
#include <iostream>
using namespace std;


enum Color
{
    RED,
    BLACK
};

template<class K, class V>
struct RBTreeNode
{
    pair<K,V> _kv;
    RBTreeNode* _left;
    RBTreeNode* _right;
    RBTreeNode* _parent;
    Color _col;
    RBTreeNode(const pair<K,V> kv)
        :_kv(kv)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_col(RED)
    {}
};

template<class K, class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    bool Insert(const pair<K,V>& kv)
    {
        if(_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;
            return true;
        }
        Node* cur = _root;
        Node* parent = nullptr;
        //找到插入位置
        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;
        //连接上
        cur->_parent = parent;
        if(parent->_kv.first > cur->_kv.first)
        {
            parent->_left = cur;
        }
        else
        {
            parent->_right = cur;
        }
        //判断颜色是否合法
        while(parent && cur->_parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            if(parent == grandfather->_left)//当parent是grandfather左节点
            {
                Node* uncle = grandfather->_right;
                //情况一:uncle存在且为红
                if(uncle && uncle->_col == RED)
                {
                    grandfather->_col = RED;
                    uncle->_col = parent->_col = BLACK;
                    
                    cur = grandfather;
                    parent = cur->_parent;
                }
                //出现下面的情况就说明树的结构出现问题,需要对结构进行调整(旋转)
                else//uncle不存,或者在且为黑
                {
                    //情况二:grandfather、parent、cur在一条直线上
                    if(parent->_left == cur)
                    {
                        RotateR(grandfather);
                        
                        grandfather->_col = RED;
                        parent->_col = BLACK;
                    }
                    //情况二:grandfather、parent、cur在一条折线上
                    else
                    {
                        RotateL(parent);
                        RotateR(grandfather);
                        
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
            else//当parent是grandfather右节点
            {
                Node* uncle = grandfather->_left;
                //情况一:uncle存在且为红
                if(uncle && uncle->_col == RED)
                {
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else//uncle不存在,或者存在且为黑
                {
                    //情况二:grandfather、parent、cur在一条直线上
                    if(parent->_right == cur)
                    {
                        RotateL(grandfather);
                        
                        grandfather->_col = RED;
                        parent->_col = BLACK;
                    }
                    //情况二:grandfather、parent、cur在一条折线上
                    else
                    {
                        RotateR(parent);
                        RotateL(grandfather);
                        
                        grandfather->_col = RED;
                        cur->_col = BLACK;
                    }
                    break;
                }
            }
        }
        _root->_col = BLACK;
        return true;
    }
    void RotateL(Node* parent)//左单旋
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* ppNode = parent->_parent;
        
        //处理subRL的部分
        parent->_right = subRL;
        if(subRL)
            subRL->_parent = parent;
        //处理parent和subR之间的关系
        subR->_left = parent;
        parent->_parent = subR;
        //处理subR的parent
        if(ppNode)//parent不是根节点的时候,需要处理parent的父亲节点
        {
            subR->_parent = ppNode;
            if(ppNode->_left == parent)//parent是左节点
            {
                ppNode->_left = subR;
            }
            else//parent是右节点
            {
                ppNode->_right = subR;
            }
        }
        else//parent是根节点的时候
        {
            _root = subR;
            subR->_parent = nullptr;
        }
    }
    void RotateR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        Node* ppNode = parent->_parent;
        
        if(subLR)
            subLR->_parent = parent;
        parent->_left = subLR;
        
        subL->_right = parent;
        parent->_parent = subL;
        if(ppNode)
        {
            subL->_parent = ppNode;
            if(ppNode->_left == parent)
                ppNode->_left = subL;
            else
                ppNode->_right = subL;
        }
        else
        {
            _root = subL;
            subL->_parent = nullptr;
        }
    }
    void InOrder(vector<pair<K,V>>& v, Node* root)
    {
        if(root == nullptr)
            return;
        InOrder(v, root->_left);
        v.push_back(root->_kv);
        InOrder(v, root->_right);
    }
    bool isBSTree(Node* root)
    {
        vector<pair<K,V>> v;
        InOrder(v,root);
        for(size_t i = 0; i < v.size() - 1; ++i)
        {
            if(v[i].first > v[i+1].first)
                return false;
        }
        return true;
    }
    bool Check(Node* root, int blackNum, int ref)
    {
        if(root == nullptr)
        {
            if(blackNum != ref)
            {
                cout << "count of black is error" << endl;
                return false;
            }
            return true;
        }
        
        if(root->_col == RED && root->_parent->_col == RED)
        {
            cout << "more red node" << endl;
            return false;
        }
        if(root->_col == BLACK)
            ++blackNum;
        
        return Check(root->_left, blackNum, ref)
            && Check(root->_right, blackNum, ref);
    }
    bool isRBTree()
    {
        if(_root == nullptr)
            return true;
        if(isBSTree(_root))
        {
            if(_root->_col != BLACK)//判断性质2
                return false;
            int ref = 0;
            Node* left = _root;
            while(left)
            {
                if(left->_col == BLACK)
                    ++ref;
                left = left->_left;
            }
            
            return Check(_root, 0, ref);
        }
        else
            return false;
    }
private:
    Node* _root = nullptr;
};
/***********Test.cpp****************/
#include <iostream>
#include <time.h>
#include "RBTree.hpp"
using namespace std;
void TestRBTree1()
{
//    int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
//    int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
    RBTree<int, int> t;
    for (auto e : a)
    {
        t.Insert(make_pair(e, e));
    }
    cout << t.isRBTree() << endl;
}
void TestRBTree2()
{
    srand(time(0));
    const size_t N = 10;
    RBTree<int, int> t;
    for (size_t i = 0; i < N; ++i)
    {
        size_t x = rand() % 100;
        //cout << x << ":";
        t.Insert(make_pair(x, x));
        //cout << t.isRBTree() << endl;
    }
    cout << t.isRBTree() << endl;
}
int main()
{
    TestRBTree1();
    TestRBTree2();
    return 0;
}

本节完。。。

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

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

相关文章

ECharts

ECharts 一、引言ECharts介绍快速上手 一、引言 ECharts介绍 ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。ECharts最初由百度团队开源&#xff0c;并于2018年初捐赠给A…

一文读懂!RK3668和RK3568有什么区别?

​ 从上图可以看出&#xff0c;RK3568和RK3566 CPU均为四核Cortex-A55架构&#xff0c;GPU为Mali-G522EE&#xff0c;内置NPU&#xff0c;可提供1T算力&#xff0c;支持DDR及CPU Cache全链路ECC等&#xff0c;RK366与RK3568最大区别的是RK3568具有PCIe接口、双千兆以太网和更…

如何轻松掌握接口测试——POST请求和COOKIE使用技巧?

目录 引言 请求方法&#xff1a; POST请求方法&#xff1a;添加资源&#xff08;对服务端已存在的资源也可以做修改和删除操作&#xff09; 实战练习 实战练习2 COOKIE&#xff1a; Status Code&#xff1a;协议状态码 接口文档 结语 引言 对于初学者或者没有接口测试…

box的符号距离函数

序 能用解析的方法算的&#xff0c;叫符号距离函数。只能数值解的&#xff0c;叫符号距离场。 它就是横平竖直的几个平面&#xff0c;点到平面的距离是很好算的。 初步认识 有个网页&#xff0c;可以玩一玩&#xff1a; About | Physics Simulation in Visual Computing (…

【LeetCode20】有效的括号——图解

​ 你好&#xff0c;欢迎来到我的博客&#xff01;作为一名程序员&#xff0c;我经常刷LeetCode题目来提升自己的编程能力。在我的博客里&#xff0c;我会分享一些我自己做过的题目和解题思路&#xff0c;希望能够帮助到大家。今天&#xff0c;我想和大家分享一道挑战性较高的题…

Flutter项目webview加载没有HTTPS证书的网页在Android和iOS设备上无法显示的解决方案

一、问题描述 Flutter项目使用谷歌官方webview库 webview_flutter&#xff0c;加载自签名证书、证书失效、无证书等HTTPS网页地址时&#xff0c;在Android或pc浏览器中提示证书失效&#xff0c;在iOS设备上为空白页&#xff0c;为了加载自签名证书的网页&#xff0c;需要饶过i…

Godot引擎 4.0 文档 - 循序渐进教程 - 脚本语言

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a; Scripting languages — Godot Engine (stable) documentation in English 脚本语言 本课将概述 Godot 中可用的脚本语言。您将了解每个选项的优缺点。在下一部分中&…

平板触控笔要原装的吗?苹果平替笔性价比高的推荐

与苹果的电容笔不同&#xff0c;市场上的电容笔只会给人一种倾斜的压感&#xff0c;并不会像苹果的电容笔那样&#xff0c;可以给人一种重力的压感。不过&#xff0c;如果你不一定要画画&#xff0c;那你就不用花很多钱去买一支苹果的原装电容笔了&#xff0c;只需一支平替电容…

postgresql数据库

官方文档:link 安装及简单操作 1 安装 sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql15-server sudo /usr/pgsql-15/bin/postgresql-15-setup initdb sudo …

2023.5.21 第五十四次周报

目录 前言 文献阅读:跨多个时空尺度进行预测的时空 LSTM 模型 背景 本文思路 本文解决的问题 方法论 SPATIAL 自动机器学习模型 数据处理 模型性能 代码 用Python编写的LSTM多变量预测模型 总结 前言 This week, I studied an article that uses LSTM to solve p…

MATLAB绘制动画(五)GIF

GIF这个文件大家就比较熟悉了&#xff0c;我们通常当做表情包的动图一般都是用GIF格式。 这是因为GIF格式的文件比较小&#xff0c;传输速度快。 用MATLAB生成GIF图像同样需要将图像保存下来&#xff0c;通过循环展示动画 代码如下&#xff1a; clc; clear; close all; set…

AMBER分子动力学模拟之结果分析(MMGB/PBSA)-- HIV蛋白酶-抑制剂复合物(4)

AMBER分子动力学模拟之结果分析(MMGB/PBSA)-- HIV蛋白酶-抑制剂复合物(4) 结合自由能计算 我们首先计算焙变&#xff0c;用到的是pbsa和gbsa方法。我们需要一下文件 三个top文件&#xff0c;pro.prmtop lig.prmtop com.prmtop&#xff1b;输入文件MM_GBSA.in&#xff1b;将要…

从桌面端到移动端,.NET MAUI为什么对WPF开发人员更简单?

.NET多平台应用程序UI&#xff08;. NET MAUI&#xff09;的市场吸引力与日俱增&#xff0c;这是微软最新的开发平台&#xff0c;允许开发者使用单个代码库创建跨平台应用程序。尽管很多WPF开发人员还没有跟上 .NET MAUI的潮流&#xff0c;但我们将在这篇文章中为大家展示他的潜…

【FAQ】视频编辑服务常见问题及解答

Q1问题描述 1、 访问贴纸等素材的时候提示“网络异常&#xff0c;请重试”怎么办&#xff1f; 2、 使用AI能力时&#xff0c;提示“errorCode:20124 errorMsg:Method not Allowed”&#xff1f; 解决方案 请做以下检查&#xff1a; 1、 在代码中检查鉴权信息是否已设置。如…

2023/5/21周报

目录 摘要 论文阅读 1、标题和现存问题 2、各个结构 3、基于GNN-LSTM-CNN 网络轨迹预测模型 4、实验准备 5、实验结果 深度学习 1、费舍尔判别 2、步骤具体化 3、GCN 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇基于GNN-LSTM-CNN网络的6G车辆轨迹预测算法的…

RabbitMQ如何保证顺序性

1. RabbitMQ消息顺序性说明 顺序性&#xff1a; 消息的顺序性是指消费者消费到消息和发送者发布的消息的顺序是一致的 举个例子&#xff0c;不考虑消息重复的情况下&#xff0c;如果生产者发布的消息分别为msg1、msg2、msg3 那么消费者必然也是按照 msg1、msg2、msg3 的顺序来…

【leetcode刷题总结】——代码随想录(链表总结)

代码随想录按照数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心->动态规划->图论->高级数据结构&#xff0c;再从简单刷起&#xff0c;做了几个类型题目之后&#xff0c;再慢慢做中等题目、困难题目。 以下是个人刷题总结&#xff0c;官…

Python初学小知识(十四):数据分析处理库Pandas

Python初学小知识&#xff08;十四&#xff09;&#xff1a;数据分析处理库Pandas 十八 Pandas1 文件读取1.1 读取csv1.2 读取txt1.3 读取excel&#xff08;xlsx&#xff09; 2 内容读取2.1 读取行2.2 读取列 3 数据处理3.1 加减乘除3.1.1 列 与 元素3.1.2 列 与 列 3.2 最值、…

张驰咨询:突破瓶颈降低成本-精益生产咨询的实践策略

在现代企业运营中&#xff0c;提高效率、优化流程是实现成功的关键因素之一。为了帮助企业在这方面取得突破性的进展&#xff0c;精益生产咨询成为了一种备受推崇的方法。本文将介绍精益生产咨询的基本原理、优势以及如何将其应用于企业实践中。 精益生产咨询是一种源于丰田生…

lwIP更新记02:网络接口标志(一个标志只做一件事)

从 lwIP-2.0.0 开始&#xff0c;网络接口 netif 的 up 标志修改为管理标志&#xff0c;up标志不再具有以前的 IP4 地址有效 含义。 什么是网络接口 netif &#xff1f; 网络接口 属于链路层范畴&#xff0c;它旨在对具体网络硬件、软件进行统一封装&#xff0c;并为协议栈上层&…