算法与数据结构:红黑树

     ACM大牛带你玩转算法与数据结构-课程资料

 本笔记属于船说系列课程之一,课程链接:

哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/cheese/play/ep66799?csource=private_space_class_null&spm_id_from=333.999.0.0

你也可以选择购买『船说系列课程-年度会员』产品『船票』,畅享一年内无限制学习已上线的所有船说系列课程:船票购买入口icon-default.png?t=N7T8https://www.bilibili.com/cheese/pages/packageCourseDetail?productId=598

做题网站OJ:HZOJ - Online Judge

Leetcode :力扣 (LeetCode) 全球极客挚爱的技术成长平台

 红黑树

建议看这篇文章时看看二叉排序树和AVL,不然理解不了其中的东西:二叉排序树和AVL树

        红黑树是如何构建的,并且他是如何进行像AVL树可以将左右子树的节点进行平衡的,就是基于它的5条性质:

  1. 每个节点是红色或黑色。
  2. 根节点是黑色。
  3. 所有叶子节点都是黑色(这里的叶子节点是指树中所有的空节点)。
  4. 每个红色节点的两个子节点都是黑色(从每个红色节点到叶子节点的路径上不能有两个连续的红色节点)。
  5. 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

性质1:他对于平衡二叉树种的每个节点多了一个属性,也就是颜色每个节点非黑及红;

性质2:红黑树的最顶端的根节点一定是黑色的,因为如果根节点是红色会将后续的插入和删除操作变得更复杂。

性质3:注意:这里的空节点是上一篇文章中创建AVL树用到的一个虚拟空节点NIL节点,为什么需要用到NIL这个虚拟空节点是一个编程技巧,让我们编程的时候方便进行一些判断和操作,在后续的删除操作会用到对NIL节点进行一些操作为了编程时进行更方便的进行平衡操作。

性质4:红色节点只能接黑色节点,这个性质就是为了保证红黑树的平衡

性质5:这条性质也是为了保证红黑树的平衡,并且在计算时需要加上那个虚拟空节点,虚拟空节点的颜色是黑色

        在实现红黑树时,一定要心里铭记这5条性质!!!!

红黑的难点:

        红黑树的难点,就是基于二叉排序树的删除和插入操作后的平衡操作。

        我的老师教给我的平衡终极大法:

        插入调整时站在祖父节点看,什么意思就是当前插入节点如果违反了性质4,因为插入的节点是红色节点,为什么是红色节点在后续会说,那么插入的节点是红色节点,违反了性质4,那么它的父节点那么一定也是红色,然后我们还需要往上找到它父节点的父节点也就是祖父节点,然后进行来调整操作;

        删除调整站在父节点看,同理插入调整怎么来进行看的。

        插入操作:

        问题:插入节点应该是什么颜色?

        答:红色,为什么呢?因为如果插入的节点是黑色,那么一定为违反性质5因为在没有插入黑色节点之前每条路径的黑色节点的数量是相同的,插入一个黑色节点那么这条路径就一定会多一个黑色节点,那么就需要进行平衡操作,而插入红色节点如果它的父节点是黑色,那么没有违反性质4性质5那么就不需要平衡,如果父节点是红色就需要平衡操作,那么平衡的概率是50%,而插入黑色是100%需要调整,所以选择插入红色节点。

插入调整分为两种情况:

        情况1:

        下面白色就当黑色节点

        插入节点是x,它的父节点20是红色的,然后口诀是什么,找到它的祖父节点来看,那么它的祖父节点是18,那么它的叔叔节点是15并且也是红色的。

        那么如何进行调整,记住调整一定是围绕的那5条性质并且主要是性质4性质5,那么直接将祖父节点变为红色,叔叔节点和父节点变为黑色。

     通过调整发现,这颗子树已经变得平衡。

        情况2:

        发生失衡节点是节点10,并它的叔叔节点是黑色的情况,那么找到它的祖父节点,往下看去它和AVL树种LL类型是否很像,那么我们就称这种类型为LL类型:

        然后通过确定图中节点的颜色

        黑色:18,20,5,16,14

        红色:15,10

        每条路径的黑色节点都为2个

        17节点的颜色是不确定的,因为图中的子树是没有画完全的,它是一颗子树,它现在的祖父节点上面还可能会有节点,17节点下面也可能会有节点,所以17节点的颜色是无法确定的,为什么图中17是红色的,是为了让各位读者看起来这颗子树没有违反性质5

        然后确定了每个节点的颜色以后,那么就需要进行平衡操作,如何进行平衡操作:

        先对祖父节点进行一个右旋:

        然后进行调整颜色,为了每条路劲的黑色节点都为2个,而且让这个子树满足性质4性质5,将10节点和18节点变为红色,15节点也就是当前的根节点变为黑色,或者将现在的根节点变为红色,10和18节点变为黑色都可以,并且不影响。这里我用的是红色下沉,也就是根为黑,子树为红:

        通过调整颜色后,可以发现目前的子树满足性质4性质5

        那么处理完LL类型,那么LR类型如何去处理呢,那么就是通过AVL中学到的技巧,通过父节点的一个左旋将LR类型变为LL类型来处理就可以了:

        那么这里通过25节点一个左旋,就变为了LL类型,就可以通过LL类型方式相同处理:

        RR和RL处理,对称过来就可以,就不详细展开说了。

        删除操作:

        问题:删除节点的颜色会引发红黑树失衡?

        答:那么就有6种情况:

  •  红,度为0,直接删除没有任何影响,删除后不会违反性质4和性质5,不会影响平衡;
  •  黑,度为0,删除度为0的黑色节点就会用到上面提到的NIL虚拟空节点了,删除度为0的黑色节点后,会用一个NIL去代替它的位置对吧,然后将NIL节点的颜色变为双重黑,这里是不是违反了第1条性质节点非黑及红,那么删除调整的难点就在这里,如何进行调整这个双重黑
  •  红,度为1,由于性质5,每条路上的黑色节点数量相同,并且红色节点只能接黑色节点,那么红色节点要么不接节点,要么必须接2个黑色节点,所以红色节点并且度为1的情况不存在;
  •  黑,度为1,红色度为1的情况,如果当前节点度为1那么为了维护性质4和5,那么它的子孩子一定是红色,然后直接删除当前节点把它唯一子孩子放在当前节点的位置,并且把它的唯一子孩子改为黑色,这样就删除了度为1的黑色节点;
  •  度为2的节点,通过二叉排序树的删除操作,可以将删除度为2的节点变为删除度为0和1的节点;

        那么下面的情况就是对于双重黑的处理:

        蓝色表示双重黑节点,白色表示黑色节点

        注意:但是图中有些节点的颜色是不确定的

        情况1:

        它的兄弟节点是黑色,并且兄弟节点的子孩子都是黑色,这里的子树是没有违反性质4和性质5的,现在的目的就是为了去除这个双重黑,并且让这颗子树平衡。

        那么图中节点的颜色:

        双重黑:33

        黑色:27,25,28

        父节点30的颜色是不确定的。

        那么如何进行调整呢,33节点双重黑,就去一重黑,变为黑色节点

        那么父节点增加一重黑,如果为红色变为黑色,如果为黑色变为双重黑

        兄弟节点去一重黑,变为红色

        这里就有人会有疑问那如果当前子树根节点为黑色,那么调整后他为双重黑了,也没有去掉双重黑呀违反了性质1啊。

        二叉排序树删除节点是不是递归进行删除的,现在只是在递归到这个子树进行平衡,那么在后续的回溯过程中,当前的父节点他也可能会变为其他节点孩子,那么就进行下次的平衡处理就可以了,最后如果到了整棵树的根节点处了,那么直接给他减去一重黑即可。

        情况2:

        这里处理的就是RR和LL类型,下面详细解释的是RR类型:

        当前情况中,双重黑节点的兄弟节点是黑色,并且他兄弟节点的右节点是红色,那么就称这种情况叫做RR类型;

        然后如果兄弟节点的左节点也是红色,那么优先考虑RR类型,如果右节点为黑色那么就是RL类型。同理如果对称过来优先考虑LL类型;

        图中现在每条路径的中的黑色节点数量都为2个

        然后确定一下当前图中节点的颜色

        双重黑:27

        黑色:40,56,80

        红色:72

        不确定:30,36,56,80

        RR类型,那么就父节点直接一个左旋:

        然后为了满足性质5:

        36,30节点的颜色是不确定的,如果36为红色30也为红色就违反了性质4,所以必须把30号节点变为黑色;

        27减少一重黑,变为黑色;

        那么现在40号节点右子树路劲上每条路都少一个黑色节点来满足性质5,那么就将72变为黑色,那么每条路径上的黑色节点都为3个了:

        这里红黑树平衡了,但是最开始每条路径的中的黑色节点数量都为2个,因为要考虑的是整棵树,如果这颗子树平衡后他目前的每条路多了一个黑色节点,那么它上面还有节点的话往下来看,这边子树路径中多了一个黑色节点,而另外的子树路径中刚好少了一个节点,那么就违反了性质5,所以需要将现在的节点40改为红色,保持最开始子树中每条路径的黑色节点数。

        然后如果最开始图中的父节点30颜色为黑色,那么每条路劲都有3个黑色节点,那么这里兄弟节点40就变为黑色;

        所以这里兄弟变的颜色为之前父节点的颜色。

        

        总结:

        当前情况RR类型:

        通过父节点的左旋,

        将双重黑节点去一重黑,

        兄弟节点通过旋转到达父节点的位置,那么新根节点的颜色变为之前根节点的颜色

        最后旋转后的根节点的左右孩子都变为黑色。

        LL类型同理对称后一样操作。

        情况3:

        这种情况,就是兄弟节点为黑色节点,并且兄弟节点的左节点为红色节点,那么这种类型就是RL类型:

        每条路劲黑色节点数量为2

        确定的节点颜色

        双重黑:27

        黑色:40,35,37,72(如果72是红色那么它一定是RR类型,RR类型优先级高于RL类型)

        红色:36

        那么RL类型,想办法给他变为RR类型,先对兄弟节点进行一个右旋:

        然后为了维护性质5:

        36变为黑色

        40变为红色

        也就是之前的兄弟节点变红,新兄弟节点变黑

        OK变为了RR类型情况2的类型,那么直接用情况2进行处理。

        然后会发现,36和40这两个节点在情况2中也进行颜色的改变,所以在编程的时候,我们不会在RL变RR类型时进行对颜色的改变,对于上面兄弟节点旋转后的颜色改变,只是为了理解这个过程。

        总结情况3:

        通过一个兄弟节点的右旋,然后之前的兄弟节点变红,新兄弟节点变黑,就可以得到RR类型,也就是情况2。

        情况4:

      兄弟节点为红色,那么兄弟节点一定为红色,那么父节点一定为黑色,35,40也一定为黑色。

        所以确定颜色的节点:

        双重黑:27

        黑色:30,35,40

        红色:39

        每条路劲黑色节点为3个

        那么通过父节点一个左旋,变为图中如下情况

        然后维护性质5,并且之前每条路劲黑色节点为3个,那么进行调色:

        39变为黑色,30变为红色:

        然后通过发现,27双重黑节点的兄弟节点为黑色节点了,那么就可以进行情况1,情况2,情况3进行操作判断了。

        总结情况4:

        也就是最开始的兄弟节点变黑,最开始的父节点变红,然后进行一个父节点的左旋,然后通过新父节点的往下看可以得到情况1,2,3。

代码实现: 

        通过对于插入和删除的每种情况的分析:

        对于这个总结需要自己去捋捋,每个人分析的方式是不一样的。

        然后最后是代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define RED    0
#define BLACK  1
#define DBLACK 2
#define NIL (&__NIL)
#define K(n) (n->key)
#define L(n) (n->lchild)
#define R(n) (n->rchild)
#define C(n) (n->color)

typedef struct Node {
    int key, color; // 0 red, 1 black, 2 double black
    struct Node *lchild, *rchild;
} Node;

Node __NIL;

__attribute__((constructor))
void init_NIL() {
    NIL->key   = -1;
    NIL->color = BLACK;
    NIL->lchild = NIL->rchild = NIL;
    return ;
}

Node *getNewNode(int key) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key   = key;
    p->color = RED;
    p->lchild = p->rchild = NIL;
    return p;
}

//判断当前节点的左右孩子是否有红色
bool has_red_node(Node *root) {
    return root->lchild->color == RED || root->rchild->color == RED;
}

//左旋操作
Node *left_rotate(Node *root) {
    Node *new_root = root->rchild;
    root->rchild = new_root->lchild;
    new_root->lchild = root;
    return new_root;
}

//右旋操作
Node *right_rotate(Node *root) {
    Node *new_root = root->lchild;
    root->lchild = new_root->rchild;
    new_root->rchild = root;
    return new_root;
}

//平衡操作
Node *insert_maintain(Node *root) {
    int flag = 0;
    //站在祖父节点来看
    //判断是在左子树还是右子树失衡
    //左子树失衡flag = 1
    if (C(L(root)) == RED && has_red_node(L(root))) flag = 1;
    //右子树失衡flag = 2
    if (C(R(root)) == RED && has_red_node(R(root))) flag = 2;
    if (flag == 0) return root;
    //如果当前祖父节点的左右孩子都为红色
    //那么就是情况1,也就是父节点和叔叔节点都是红色的情况
    if (C(L(root)) == RED && C(R(root)) == RED) goto red_up_maintain;
    //这里就是L类型
    if (flag == 1) {
        //判断是否是LR类型
        if (C(R(L(root))) == RED) {
            L(root) = left_rotate(L(root));
        }
        root = right_rotate(root);
    } 
    //这里就是R类型
    else {
        //判断是否是RL类型
        if (C(L(R(root))) == RED) {
            R(root) = right_rotate(R(root));
        }
        root = left_rotate(root);
    }
red_up_maintain:
    //通过旋转或者判断之后
    //现在子树最顶上的三个节点进行修改颜色
    C(root) = RED;
    C(L(root)) = C(R(root)) = BLACK;
    return root;
}

//二叉树排序树的基本插入方法
Node *__insert(Node *root, int key) {
    if (root == NIL) return getNewNode(key);
    if (root->key == key) return root;
    if (key < root->key) root->lchild = __insert(root->lchild, key);
    else root->rchild = __insert(root->rchild, key);
    //最后返回平衡后的根节点
    return insert_maintain(root);
}

//红黑树的插入方法
Node *insert(Node *root, int key) {
    //封装
    root = __insert(root, key);
    //性质2根节点一定是黑色
    root->color = BLACK;
    return root;
}

//找到当前节点的前驱节点
Node *predecessor(Node *root) {
    Node *temp = root->lchild;
    while (temp->rchild != NIL) temp = temp->rchild;
    return temp;
}

Node *erase_maintain(Node *root) {
    //站在父节点来看
    //如果当前节点的左右孩子没有双重黑说明平衡
    if (C(L(root)) != DBLACK && C(R(root)) != DBLACK) return root;
    //如果兄弟节点为红色, 情况4
    if (has_red_node(root)) {
        //最开始的父节点变红色
        root->color = RED;
        if (root->lchild->color == RED) {
            //如果兄弟节点在左,就右旋
            root = right_rotate(root);
            //然后对旋转后双重黑节点的父节点,进行调整
            root->rchild = erase_maintain(root->rchild);
        } else {
            //对称同理
            root = left_rotate(root);
            root->lchild = erase_maintain(root->lchild);
        }
        //最开始的兄弟节点变黑色
        root->color = BLACK;
        return root;
    }
    //情况1,兄弟节点的子孩子都为黑
    //直接将父节点加一重黑
    //父节点的左右孩子减一重黑
    if ((root->lchild->color == DBLACK && !has_red_node(root->rchild)) 
        || (root->rchild->color == DBLACK && !has_red_node(root->lchild))) {
        root->color += 1;
        root->lchild->color -= 1;
        root->rchild->color -= 1;
        return root;
    }
    //判断双重黑在那个子树
    if (root->rchild->color == DBLACK) {
        //直接减一重黑
        root->rchild->color = BLACK;
        //情况3
        //如果不是LL类型,那么就需要进行对兄弟节点的左旋
        //然后不进行节点颜色处理
        if (root->lchild->lchild->color != RED) {
            root->lchild = left_rotate(root->lchild);
        }
        //因为旋转后兄弟节点的颜色要变为父节点的颜色
        root->lchild->color = root->color;
        //然后对于父节点的右旋
        root = right_rotate(root);
    } else {
        //对称同理
        root->lchild->color = BLACK;
        if (root->rchild->rchild->color != RED) {
            root->rchild = right_rotate(root->rchild);
        }
        root->rchild->color = root->color;
        root = left_rotate(root);
    }
    //情况2的统一处理
    //通过旋转后那,当前根节点的左右孩子都要变为黑色
    root->lchild->color = root->rchild->color = BLACK;
    return root;
}

//二叉树排序树的删除操作
Node *__erase(Node *root, int key) {
    if (root == NIL) return root;
    if (key < root->key) {
        root->lchild = __erase(root->lchild, key);
    } else if (key > root->key) {
        root->rchild = __erase(root->rchild, key);
    } else {
        //在这里需要加黑
        if (root->lchild == NIL || root->rchild == NIL) {
            //度为0和1的节点一起处理
            //获取到当前节点的子孩子,如果没有子孩子,那么这里就体现处理NIL虚拟空节点的用处了
            Node *temp = root->lchild == NIL ? root->rchild : root->lchild;
            temp->color += root->color;
            free(root);
            return temp;
        }
        //将删除度为2的节点,变为删除度为0或1的节点进行处理
        Node *temp = predecessor(root);
        root->key = temp->key;
        root->lchild = __erase(root->lchild, temp->key);
    }
    //删除调整平衡
    return erase_maintain(root);
}

//红黑树的删除操作
Node *erase(Node *root, int key) {
    //封装
    root = __erase(root, key);
    //根节点一定是黑色,为了防止双重黑跑到根节点来
    root->color = BLACK;
    return root;
}

void clear(Node *root) {
    if (root == NIL) return ;
    clear(root->lchild);
    clear(root->rchild);
    free(root);
    return ;
}

void output(Node *root) {
    if (root == NIL) return ;
    printf("(%d| %d; %d, %d)\n", 
        C(root), K(root),
        K(L(root)), K(R(root))
    );
    output(root->lchild);
    output(root->rchild);
    return ;
}

int main() {
    srand(time(0));
    #define MAX_N 10
    Node *root = NIL;
    for (int i = 0; i < MAX_N; i++) {
        int x = rand() % 100;
        printf("\ninsert %d to red black tree : \n", x);
        root = insert(root, x);
        output(root);
    }
    int x;
    while (~scanf("%d", &x)) {
        printf("\nerase %d from red black tree\n", x);
        root = erase(root, x);
        output(root);
    }
    return 0;
}

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

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

相关文章

【计算机毕设】基于Spring Boot的课程作业管理系统 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 课程作业管理系统旨在为教师和学生提供一个便捷的平台&#xff0c;用于发布、提交和评定课程作业。本系统旨在提高作业管理的效率&#xff0c;促进教…

NetMizer 日志管理系统前台RCE漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 一、产品介绍 NetMizer日志管理系统是一个与NetMizer流量管理设备配合…

nginx的安装002

之前001讲述了nginxyum安装现在讲一下nginx如何本地离线安装 操作系统&#xff1a; CentOS Stream 9 操作步骤&#xff1a; 首先访问nginx官网&#xff0c;下载。 用wget命令下载&#xff0c; [rootlocalhost ~]# wget -c https://nginx.org/download/nginx-1.26.0.tar.gz …

【网络原理】HTTP|认识请求“报头“|Host|Content-Length|Content-Type|UA|Referer|Cookie

目录 认识请求"报头"(header) Host Content-Length Content-Type User-Agent(简称UA) Referer &#x1f4a1;Cookie&#xff08;最重要的一个header&#xff0c;开发&面试高频问题&#xff09; 1.Cookie是啥&#xff1f; 2.Cookie怎么存的&#xff1f; …

在PostGIS中检查孤线(Find isolated lines in PostGIS)

场景 在PostGIS中有一张线要素表,需要检查该表中的孤线,并且进行自动纠正的计算。 其中孤线定义为两端端点都不在任何其他线的顶点上。 本文介绍在PostGIS中的线要素点,通过函数计算指定线要素表中的孤线,并计算最接近的纠偏位置。 In PostGIS, there is a table of line …

07-操作元素(键盘和鼠标事件)

在前面的文章中重点介绍了一些元素的定位方法&#xff0c;定位到元素后&#xff0c;就需要操作元素了。本篇总结了web页面常用的一些操作元素方法&#xff0c;可以统称为行为事件。 一、简单操作 点击按钮&#xff08;鼠标左键&#xff09;&#xff1a;click()清空输入框&…

git冲突

git冲突的产生&#xff1a; 首先用户A新建一个文件conflict&#xff0c;并在里面添加内容 然后通过add,commit,push将该文件上传到远端仓库 然后用户B通过pull将程序拉下来之后&#xff0c;也在这个文档里面进行编辑&#xff0c;并且内容不一样 如果这个时候其中一个人push&…

PAT-1004 成绩排名(java实现)

这一关感觉还没第三关难&#xff0c;思路很清晰 题目 1004 成绩排名 读入 n&#xff08;>0&#xff09;名学生的姓名、学号、成绩&#xff0c;分别输出成绩最高和成绩最低学生的姓名和学号。 输入格式&#xff1a; 每个测试输入包含 1 个测试用例&#xff0c;格式为 第 1 行…

加密金字塔的秘密:「高层」的回报你无法想象

原文标题&#xff1a;《The Secrets of the Crypto Pyramid!》 撰文&#xff1a;DUO NINE⚡YCC 编译&#xff1a;Chris&#xff0c;Techub News 本文来源香港Web3科技媒体&#xff1a;Techub News 意外成为一名 KOL 让我有机会深入了解这个领域的运作机制。在这个行业的幕后…

优化基础(二):线性组合、仿射组合、锥组合、凸组合、线性集合、仿射集合、锥集合、凸集合的理解

文章目录 前言组合线性组合 (linear combination)仿射组合 (affine combination)锥组合 (conic combination)凸组合 (convex combination) 集合仿射集合凸集合 练习&#xff1a;哪个图形是凸的&#xff0c;哪个是仿射的&#xff1f;参考资料 前言 组合侧重于描述由一些基点生成…

ai虚拟主播自动切换的实现

前段时间,看到b站突然冒出很多ai主播,输入数字切换小姐姐.感觉挺有趣.思考了以下决定手动实现一下. 然后就陷入长达5天的踩坑中 由于是自建的webrtc服务器,很自然的想直接收流转发,这也是最优的方案, 然而实际上遇到许多不是很友好的bug, 然后再想使用rtp转发,依然不理想. 最后…

使用香橙派 AI pro 开发板生成卡通版的东契奇

引言 AI应用如火如荼&#xff0c;而嵌入式设备与AI的结合也是不可或缺的重要场景。香橙派 AI pro 开发板正是面向嵌入式AI场景而生。 这篇文章记录了对香橙派 AI pro开发板的试用体验。介绍了开发板的基础操作&#xff0c;以及对其中一个AI应用样例的体验&#xff0c;初步感受…

Docker安装Redis(云服务器)

准备&#xff1a; 在云服务器中开启6370端口号 docker run -d --name redis -p 6379:6379 redis 这条命令使用docker运行一个名为"redis"的容器&#xff0c;映射容器的6379端口到主机的6379端口&#xff0c;并且使用redis镜像来运行容器。REDIS是一个开源的内存数据…

记 Codes 开源免费研发管理平台 —— 日报与工时融合集中式填报的创新实现

继上一回合生成式全局看板的创新实现后&#xff0c;本篇我们来讲一讲日报与工时融合集中式填报的创新实现。 市面上所有的研发管理软件&#xff0c;大多都有工时相关功能&#xff0c;但是却没有日报功能&#xff0c;好像也没什么问题&#xff0c;但是在使用过程中体验非常不…

RandLA-Net 训练自定义数据集

https://arxiv.org/abs/1911.11236 搭建训练环境 git clone https://github.com/QingyongHu/RandLA-Net.git搭建 python 环境 , 这里我用的 3.9conda create -n randlanet python3.9 source activate randlanet pip install tensorflow2.15.0 -i https://pypi.tuna.tsinghua.e…

IDEA 安装BPMN插件-Activiti BPMN visualizer

IDEA安装BPMN插件 idea 18版本之前idea 18版本之后安装插件 推荐使用 Activiti BPMN visualizer插件注意 创建bpmn文件使用可视化面板 在可视化面板中右键可创建各种节点每个节点上都有连线 删除 设置的按钮 保存图片 idea 18版本之前 可以通过搜索插件actiBPMN直接安装 idea…

VBA代码解决方案第十四讲 如何利用VBA检查单元格中是否含有公式

《VBA代码解决方案》(版权10028096)这套教程是我最早推出的教程&#xff0c;目前已经是第三版修订了。这套教程定位于入门后的提高&#xff0c;在学习这套教程过程中&#xff0c;侧重点是要理解及掌握我的“积木编程”思想。要灵活运用教程中的实例像搭积木一样把自己喜欢的代码…

如何格式化只读U盘?

U盘只读无法格式化&#xff0c;该怎么处理&#xff1f;别担心&#xff01;本文将向你提供一些实用方法&#xff0c;助你解决U盘写保护的难题。这些方法能有效帮助你解除U盘的只读状态&#xff0c;从而可以顺利进行格式化和其他操作。 不能格式化只读U盘 “我购买了一个U盘&…

64位和32位对C++ 对long类型的使用造成程序崩溃、内存泄漏问题。

系列文章目录 1、理解32位和64位下long类型和int类型不同 2、理解release和debug版本编译的可执行程序的区别 3、谨慎在64位下对long类型与int类型去赋值和相互转换 文章目录 系列文章目录前言一、int、long类型二、使用步骤1.示例代码 前言 编译环境&#xff1a;qt -c、linu…

代码随想录算法训练营第三十二 | ● 122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 讲解链接&#xff1a;https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%90%8E%E6%9C%80%E5%A4%A7%E5%8C%96%E7%9A%84%E6%95%B0%E7%BB%84%E5%92%8C.html 简单思路&#xff1a;逐个计算连续两天的股票差值&#xff0c;sum初始为零&…