Avl树(有详细图解)

news2024/11/20 10:24:24

目录

介绍

引入

概念 

特点 

模拟实现

思路

插入

旋转 

左旋

无子树

有子树

右旋

无子树

有子树

左右旋

引入(也就是有子树版本的抽象图解)

解决方法(也就是左右旋) 

总结

无子树(也就是curright的位置就是newnode)

有子树 

模型高度解释

旋转 

更新三个节点的bf

右左旋

无子树

有子树

旋转

更新三个结点的bf

注意点

代码


介绍

引入

map和set的底层都是按照二叉搜索树来实现的

  • 但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N)
  • 因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现

概念 

  • AVL树是一种自平衡二叉搜索树,其名称源自其发明者Adelson-Velsky和Landis
  • AVL树通过确保各个节点之间的高度差不超过1,来保证在最坏情况下,树的高度仍为O(log n) (其中n是树中节点的数量),这样可以保持最好的效率

特点 

  • 自平衡:在插入或删除节点时,AVL树会自动执行旋转操作来保持平衡。这些旋转操作包括左旋、右旋、左右旋和右左旋等,通过这些旋转可以调整节点的平衡因子,使其满足平衡条件(不超过1)

  • 二叉搜索树性质:AVL树仍然是二叉搜索树,即左子树的所有节点的值都小于根节点的值,右子树的所有节点的值都大于根节点的值

模拟实现

思路

插入

  • avl树中保持平衡的关键就在于平衡因子(bf)
  • 这里bf=右子树高度-左子树高度
  • 主要就是每次插入时,需要更新平衡因子 (但只需要沿着newnode的位置往上就行,因为它改变的只是newnode所在那一分支)

  • 只要当前结点bf=0,就可以不再更新了(它此时变为0,说明原来是1/-1)
  • 那么让1/-1变为0,这一分支的高度是不变的,那么以这个为子树的树,高度也不会变,平衡因子也就不变

  • 然后,如果结点bf=2/-2,就要开始旋转了
  • 而旋转分为4种,需要判断cur和cur父亲的bf,来区分使用哪种旋转方式 

  • 旋转后的子树,其根结点bf是0,那么他也是从在插入新结点前的1/-1变成了0,所以也就不需要往上更新了
  • 这样,插入步骤就结束了

旋转 

左旋
无子树

左旋实际上就是 -- ​​​​​​​让p成为cur的左子树,cur的右边不受影响

有子树

  • 虽然都有子树,但我们无法知道到底是多少个,到底是什么形态
  • (这些其实我们都不关心,我们只要知道一个相对大小就行,毕竟重点还是在p和cur上)
  • 有子树和无子树都是差不多的,只不过是多了子树,但是p和cur的bf仍然是2和1,只有这样才会触发左旋
  • 然后设cur左子树高度为h,那么可以推出curright高度就是h+1,p左树是h
  • 旋转后 -- cur的原左子树成为了p的右子树(因为本身curleft就处于p的右树范围),p左树不变,cur右树也不变
  • 只要记住 -- 左旋是p成为cur的左树,那么原左树就成为了p的右树
  • 旋转后的bf:(都为0)
右旋

和左旋非常像,只不过是换了个方向

无子树

旋转后,p成为cur的右子树

有子树

和左旋一个思路,最终得到了这么一副抽象图:

然后就是旋转了 -- cur原右子树成为p的左子树(本身就是p左树范围内),p右子树不变

计算bf后,两个结点的bf变成了0,其他的都不变

两种旋转真的非常像,只要记住左旋是p成为cur的左子树,右旋是p成为cur的右子树,就行了

左右旋
引入(也就是有子树版本的抽象图解)

上面有这样一种情况,它是需要被右旋的:

但是如果这个新结点是在curright下呢???

这种情况不能使用单独的右旋(因为单右旋没有用):

会发现右旋后,这支子树的根结点的bf还是2(只是把原子树的左右颠倒了)

 

解决方法(也就是左右旋) 

然后,我们经过对比可以发现,实际上他和右旋之间就差一个拐拐 :

所以可以考虑将那个拐角处掰正,也就是对应的将cur那支子树进行左旋:

这样,就可以和原来的p拼接起来,达到右旋的条件

最终经过右旋后,就可以完成我们的需求(这里只是抽象演示,并没有细画cur的情况,有子树中会有详细版)

总结

相当于对cur左旋是进行右旋的预热,最终其实还是经过对p右旋完成的 

无子树(也就是curright的位置就是newnode)

先对cur进行左旋,然后对p右旋:

最终newnode(也就是curright成为了这一支子树的根结点)

有子树 

首先,在无子树情况中,我们会发现,最终cur的右结点成为了根,那么我们就需要额外画出这个结点

其次,新结点放在curright的左/右子树都可以,只不过最后处理这三个重要结点的bf时,需要单另处理

先抽象好模型(设curright右子树高度为h,从而可以推出其他高度)

模型高度解释

你可能会好奇,为什么curright的左右子树高度被写成是相等的? (至少我好奇了,然后我画了画,发现是有人家的理由的)

  • 这里还是设右子树高度为h
  • 如果原先左子树高度是h+1(高度差不能超过1嗷)
  • 那新加一个结点就会变成h+2,会让curright的bf变成-2,那么这里不会触发p的左右旋,而是curright的旋转(左旋或者左右旋,具体看新结点的位置)

  • 如果原先左子树高度是h-1(高度差不能超过1嗷)
  • 那新加一个结点就会变成h,会让curright的bf变成0,那bf的更新到curright就停止了,不会让p更新到2/-2,所以依然不可以

那么就可以得到,curright的两个子树(如果有),那一定高度相等,才能触发p的左右旋

旋转 

继续接下来的步骤,抽象好模型后,对cur进行左旋

不要忘记前面介绍的左旋了嗷(这里的cur成为curright的左子树,原左树成为cur的右子树)

然后和p连接在一起(也就是修改p的指向和两个结点的_parent指向)

接下来就是旋转的最后一步辣,对p进行右旋

这里是将p作为curright的右子树,原右子树成为p的左树(都是之前的步骤)

之后就是最重要的一步,对三个结点的bf重新赋值(虽然进行了旋转,但更新到的bf实际并不准确,因为右旋和左右旋用到的模型本身就不同)

更新三个节点的bf

这里的更新也得分情况

像这里用到的图解,是新结点插入在curright的左树部分

(下面是全部的旋转变换过程)

  • 那么由于要对cur左旋的缘故,curright中带有新结点的这一支给了cur的右树,那么cur右边和左边平衡了,cur的bf=0
  • 但p不一样,p的左树是被分配的curright的右树,其高度只有h(注意看图嗷看图),所以p的bf=1

如果新结点插入在curright的右树部分

  • 道理和上面的一样
  • 因为curright的右树给了p的左边:
  • 所以p的bf=0(平衡了)
  • 而cur的右边是原curright的左边,高度是0,所以cur的bf=-1

只要过程熟悉,知道是怎么变的,bf的更新也素手到擒来噜

右左旋

和左右旋非常像,只不过是换了个方向

无子树

同样是有拐,也是一样要把那个拐掰正,也就是对cur进行右旋(让cur成为curleft的右子树)

最后就可以美美对p进行左旋噜

然后curleft成为了这一支的根结点

有子树

由于在左右旋中,我们已经分析了模型高度,所以我们直接画出右左旋的抽象模型(本质一样的)

旋转

接下来就是相似的操作,对curleft进行右旋

然后和p连接

就变成了标准的左旋状态,对p进行左旋:

最后就是:curleft的右结点通过右旋给了cur的左边,左结点经过左旋给了p的右边

更新三个结点的bf

这里用到的图解,是新结点插入在curleft的左树部分

像一直在重复说的那样(臣妾已经说倦了)

  • 因为右旋,cueleft的右子树给了cur的左树,所以cur的左边是h,而右边是自己的h+1,所以cur的bf=1
  • 因为左旋,curleft的左子树给了p的右子树,而右子树正是新结点插入位置,所以p的左边是h+1,右边是原先自己的h+1,相等了,所以p的bf=0

当新结点插入在curleft的右树部分

经过旋转后:

会发现:

  • 新结点所在的右子树,在右旋中给了cur的左子树,所以cur的左右相等了,cur的bf=0
  • 而给p的curleft右树高度仍是h,而p的左子树是自己的h+1,所以p的bf=-1​​​​​​​

注意点

一定要写对插入的逻辑,还有 各种结点指向 / bf数值 的更新,超级重要!!!

  • 亲身经历,本来以为对了,测试也是对的,过了几天加了点测试代码(因为实在是不好看这个代码到底写的对不对,又多又杂),结果就挂了
  • 找了半天的错,发现旋转代码没问题,是我插入里面的"往上更新bf"逻辑错了
  • 我是先一直更新到根结点,然后才找bf=2/-2的结点
  • 就导致,旋转完后子树的bf是对的,它父结点的bf错了,本来就不应该更新它的!(我哭死)

然后就是旋转里面,"原子树根结点的父结点"的指向要注意不要漏掉,要更新啊!!!

其他的好像就没啥了,只要多多写备注,就应该没啥问题

代码

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <cassert>
#include <cstdlib>

namespace my_AvlTree
{
    template <class T>
    struct AvlTreeNode
    {
        AvlTreeNode(const T &data = T())
            : _left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _bf(0)
        {
        }

        AvlTreeNode<T> *_left;
        AvlTreeNode<T> *_right;
        AvlTreeNode<T> *_parent;
        T _data;
        int _bf; // 节点的平衡因子
    };

    // AVL: 二叉搜索树 + 平衡因子的限制
    template <class T>
    class AvlTree
    {
        typedef AvlTreeNode<T> Node;

    public:
        AvlTree()
            : _root(nullptr)
        {
        }

        // 在AVL树中插入值为data的节点
        bool Insert(const T &data);

        // AVL树的验证
        bool IsAvlTree()
        {
            return _IsAvlTree(_root);
        }

        void levelOrder();
        size_t Height()
        {
            return _Height(_root);
        }

    private:
        // 根据AVL树的概念验证pRoot是否为有效的AVL树
        bool _IsAvlTree(Node *root);
        size_t _Height(Node *root);
        // 右单旋
        void RotateR(Node *parent);
        // 左单旋
        void RotateL(Node *parent);
        // 右左双旋
        void RotateRL(Node *parent);
        // 左右双旋
        void RotateLR(Node *parent);

    private:
        Node *_root;
    };

    template <class T>
    void AvlTree<T>::levelOrder()
    {
        Node *root = _root;
        std::vector<std::vector<T>> arr;
        std::queue<Node *> q;
        if (root != nullptr)
        {
            q.push(root);
        }
        while (!q.empty())
        {
            std::vector<T> tmp;  // 存储一层的结点
            int size = q.size(); // 此时队列内的元素就是上一层的结点个数
            for (int i = 0; i < size; ++i)
            {
                tmp.push_back(q.front()->_data);
                if (q.front()->_left)
                { // 有子树就放进队列中
                    q.push(q.front()->_left);
                }
                if (q.front()->_right)
                {
                    q.push(q.front()->_right);
                }
                q.pop(); // 出掉这个父结点
            }
            arr.push_back(tmp);
        }
        for (auto &i : arr)
        {
            for (auto &j : i)
            {
                std::cout << j << " ";
            }
            std::cout << std::endl;
        }
    }

    template <class T>
    bool AvlTree<T>::Insert(const T &data)
    {
        if (_root == nullptr)
        {
            _root = new Node(data);
        }
        else
        {
            Node *cur = _root, *parent = cur, *newnode = nullptr;
            // 找到插入位置
            while (cur)
            {
                if (data > cur->_data)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else if (data < cur->_data)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else
                {
                    return false;
                }
            }
            // 插入+改父亲bf
            newnode = new Node(data);
            if (data < parent->_data)
            {
                parent->_left = newnode;
                --parent->_bf;
                newnode->_parent = parent;
            }
            else
            {
                parent->_right = newnode;
                ++parent->_bf;
                newnode->_parent = parent;
            }

            // std::cout << "parent:" << parent->_data << " "
            //           << "newnode:" << newnode->_data << std::endl;

            // 维护bf
            cur = parent; //注意,这里不能直接定义cur的父结点,因为不能保证cur不为空(如果此时只有俩结点,就为空了!!!)
            while (cur != _root)
            {
                Node *pnode = cur->_parent;
                // 更新bf
                if (pnode->_left == cur)
                {
                    --pnode->_bf;
                }
                else
                {
                    ++pnode->_bf;
                }
                // 判断是否继续往上更新
                if (cur->_bf == 0)
                {
                    break;
                }
                else
                {
                    if (pnode->_bf == 2 || pnode->_bf == -2)
                    {
                        // pnode就是不合法的结点,然后判断它该怎么调整
                        if (pnode->_bf == -2 && cur->_bf == -1)
                        {
                            // 右单旋
                            RotateR(pnode);
                        }
                        if (pnode->_bf == 2 && cur->_bf == 1)
                        {
                            // 左单旋
                            RotateL(pnode);
                        }
                        if (pnode->_bf == -2 && cur->_bf == 1)
                        {
                            // 左右双旋
                            RotateLR(pnode);
                        }
                        if (pnode->_bf == 2 && cur->_bf == -1)
                        {
                            // 右左双旋
                            RotateRL(pnode);
                        }
                        break;
                    }
                    else
                    {
                        cur = pnode;
                        pnode = pnode->_parent;
                    }
                }
            }
        }
        return true;
    }

    template <class T>
    bool AvlTree<T>::_IsAvlTree(Node *root)
    {
        if (root == nullptr)
        {
            return true;
        }
        int left_height = _Height(root->_left);
        int right_height = _Height(root->_right);
        if (right_height - left_height != root->_bf) //不要太相信自己写的bf计算方法
        {
            std::cout << right_height << std::endl;
            std::cout << left_height << std::endl;
            std::cout << root->_bf << std::endl;
            std::cout << "平衡因子异常" << std::endl;
            return false;
        }
        return abs(right_height - left_height) < 2 && _IsAvlTree(root->_left) && _IsAvlTree(root->_right);
    }

    template <class T>
    size_t AvlTree<T>::_Height(Node *root)
    {
        if (root == nullptr)
        {
            return 0;
        }
        int leftsize = _Height(root->_left) + 1;
        int rightsize = _Height(root->_right) + 1;
        return leftsize > rightsize ? leftsize : rightsize;
    }

    template <class T>
    void AvlTree<T>::RotateL(Node *parent)
    {
        Node *cur = parent->_right, *curleft = cur->_left, *ppnode = parent->_parent;

        // 连接cur上需要修改的子树(比如左旋就是让parent当cur的左子树,那么cur左树就得和parent右边相连)
        if (curleft) // 防止左树为空
        {
            curleft->_parent = parent;
        }
        parent->_right = curleft;

        // 连接cur和parent
        cur->_left = parent;
        // 修改parent父结点的指向
        if (ppnode == nullptr) // 如果此时parent是根结点,那么它没有父结点,且需要更新根结点
        {
            _root = cur;
        }
        else
        {
            if (ppnode->_left == parent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
        }

        // 改父结点指向(千万别忘辣!!!)
        cur->_parent = ppnode;
        parent->_parent = cur;
        // 维护bf
        cur->_bf = parent->_bf = 0;
    }

    template <class T>
    void AvlTree<T>::RotateR(Node *parent)
    {
        Node *cur = parent->_left, *curright = cur->_right, *ppnode = parent->_parent;
        // 连接cur上需要修改的子树(右旋就是让parent当cur的右子树,那么cur右树就得和parent左边相连)
        if (curright) // 防止右树为空
        {
            curright->_parent = parent;
        }
        parent->_left = curright;

        // 连接cur和parent
        cur->_right = parent;
        // 修改parent父结点的指向
        if (ppnode == nullptr) // 如果此时parent是根结点,那么它没有父结点,且需要更新根结点
        {
            _root = cur;
        }
        else
        {
            if (ppnode->_left == parent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
        }

        // 改父结点指向
        cur->_parent = ppnode;
        parent->_parent = cur;
        // 维护bf
        cur->_bf = parent->_bf = 0;
    }

    template <class T>
    void AvlTree<T>::RotateLR(Node *parent)
    {
        Node *cur = parent->_left, *curright = cur->_right;
        int bf_comp = curright->_bf; // 用于判断插入结点的左右位置

        RotateL(parent->_left); // 让cur成为curright的左子树
        RotateR(parent);        // 让parent成为curright的右子树

        // curright是旋转后子树的根结点
        // 最终让curright的左子树给了cur的右子树,curright的右子树给了parent的左子树

        // if (_Height(curright) == 1)
        // {
        //     curright->_bf = 0;
        //     cur->_bf = 0;
        //     parent->_bf = 0;
        // }
        if (bf_comp == 0)
        {
            curright->_bf = 0;
            cur->_bf = 0;
            parent->_bf = 0;
        }
        else
        {
            if (bf_comp == -1) // 在curright的左边
            {
                // 说明cur的右子树多了1,使其与原先的左子树高度相等
                // 而parent的左子树是h-1,右子树是原先的h
                curright->_bf = 0;
                cur->_bf = 0;
                parent->_bf = 1;
            }
            else if (bf_comp == 1) // 在curright的右边
            {
                // 说明parent的左子树多了1,使其与原先的右子树高度相等
                // 而cur的右子树是h-1,左子树是原先的h
                curright->_bf = 0;
                cur->_bf = -1;
                parent->_bf = 0;
            }
            else
            {
                assert(false);
            }
        }
    }

    template <class T>
    void AvlTree<T>::RotateRL(Node *parent) // 插入结点在curleft的左右子树上其中一个位置
    {
        Node *cur = parent->_right, *curleft = cur->_left;

        int bf_comp = curleft->_bf; // 用于判断插入结点的左右位置
        RotateR(parent->_right);    // 让cur成为curleft的右子树
        RotateL(parent);            // 让parent成为curleft的左子树

        // curleft是旋转后子树的根结点
        // 最终让原curleft的右子树给了cur的左子树,curleft的左子树给了parent的右子树

        // if (_Height(curleft) == 1)
        // {
        //     curleft->_bf = 0;
        //     cur->_bf = 0;
        //     parent->_bf = 0;
        // }
        if (bf_comp == 0)
        {
            curleft->_bf = 0;
            cur->_bf = 0;
            parent->_bf = 0;
        }
        else
        {
            if (bf_comp == -1) // 在curleft的左边
            {
                // 说明parent右子树多了1,使其与原先的左子树高度相等
                // 而cur的左子树是h-1,右子树是原先的h
                curleft->_bf = 0;
                cur->_bf = 1;
                parent->_bf = 0;
            }
            if (bf_comp == 1) // 在curleft的右边
            {
                // 说明cur的左子树多了1,使其与原先的右子树高度相等
                // 而parent的右子树是h-1,左子树是原先的h
                curleft->_bf = 0;
                cur->_bf = 0;
                parent->_bf = -1;
            }
        }
    }
}

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

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

相关文章

如何像开发人员一样思考_成为一个问题解决者

程序员在处理大问题时通常会将其分解成多个小问题来解决。这个过程通常被称为“分解”或“分治”&#xff0c;它是一种将复杂问题分解成可管理的小问题的方法。 以下是程序员思考如何将大问题分解成小问题的一些步骤&#xff1a; 确定问题域&#xff1a;程序员需要了解和理解问…

Kettle REST Client获取token调用接口解析JSON入文件实战

Kettle REST Client通过GET获取token以POST方式请求接口解析JSON入文件完整实例 需求说明 通过kettle组件调用接口并解析JSON成结构化数据入文件。 完整实例 解决方法 利用生成记录组件定义URL参数通过REST ClENT组件请求得到TOKEN通过JSON INPUT组件解析接口请求的结果通过…

vue3新语法糖<script setup>

各种使用方法参考&#xff1a;(184条消息) 【vue3学习系列】组合式api中&#xff0c;替代setup()函数的&#xff1c;script setup&#xff1e;特性写法_庞囧的博客-CSDN博客https://blog.csdn.net/pagnzong/article/details/121733394 一、参考&#xff1a;Vue3 script setup …

Git版本控制:入门到精通

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

简单的自托管书签服务NeonLink

什么是 NeonLink &#xff1f; NeonLink 是一个简单且开源的自托管书签服务。它是轻量级的&#xff0c;使用最少的依赖项&#xff0c;并且易于通过 Docker 安装。由于系统要求较低&#xff0c;该应用程序非常适合部署在 RaspberryPI 上。 安装 在群晖上以 Docker 方式安装。 …

图片可变码怎么做?可编辑二维码制作教程

现在很多人会将拍摄的图片转二维码来展示&#xff0c;这种方法能够有效的让更多人更快的查看图片内容&#xff0c;那么图片生成二维码该怎么做呢&#xff1f;有些小伙伴知道现在二维码可以在图案不变情况下修改内容&#xff0c;如果我们需要不断填充照片&#xff0c;那么这种类…

4.基本IO口操作

CC2530端口资源&#xff1a;三个端口&#xff0c;表示为 P0、P1 和 P2。P0 和 P1 是完全的 8 位端口&#xff0c;而 P2 仅有 5 位可用 CC2530的IO口的一些功能&#xff1a; 通用IO口、外设IO口&#xff08;定时器、USART、ADC&#xff09; 输入引脚、输出引脚 当输入时&#…

奇安信天擎Linux客户端部署相关事项

奇安信天擎Linux客户端部署 一 Linux天擎客户端部署在线部署离线部署 二 Linux 单机部署需要开放的端口三 Linux天擎客户端停止和启动天擎的命令四 Linux天擎客户端卸载五 卸载后检查六 Linux天擎客户端病毒库更新操作步骤七 Linux客户端是否有补丁库&#xff1f; 一 Linux天擎…

设备巡检电力水利物业巡检小程序开源版开发

设备巡检电力水利物业巡检小程序开源版开发 以下是设备巡检电力水利物业巡检小程序开源版的可能功能列表&#xff1a; 用户登录/注册&#xff1a;用户可以通过手机号或其他方式进行登录和注册。 首页展示&#xff1a;展示设备巡检电力水利物业巡检小程序的基本信息和操作指南…

6.1 KMP算法搜索机器码

KMP算法是一种高效的字符串匹配算法&#xff0c;它的核心思想是利用已经匹配成功的子串前缀的信息&#xff0c;避免重复匹配&#xff0c;从而达到提高匹配效率的目的。KMP算法的核心是构建模式串的前缀数组Next&#xff0c;Next数组的意义是&#xff1a;当模式串中的某个字符与…

【力扣】83. 删除排序链表中的重复元素

题目描述 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 示例 2&#xff1a; 输入&#xff1a;head [1,1,2,3,3] 输…

笔试强训Day(一)

T1&#xff1a;组队竞赛 链接&#xff1a;组队竞赛__牛客网 牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个队伍,即每个队伍3人.牛牛发现队伍的水平值等于该队伍队员中第二高水平值。 例如: 一个队伍三个队员…

【DDPM论文解读】Denoising Diffusion Probabilistic Models

0 摘要 本文使用扩散概率模型合成了高质量的图像结果&#xff0c;扩散概率模型是一类受非平衡热力学启发的潜变量模型。本文最佳结果是通过根据扩散概率模型和朗之万动力学的去噪分数匹配之间的新颖联系设计的加权变分界进行训练来获得的&#xff0c;并且本文的模型自然地承认…

Jupyter Notebook中的魔法命令

关于魔术命令 Jupyter Notebook 使用的 Python 内核通常是 IPython 内核。IPython 是 Python 的增强交互式解释器&#xff0c;它提供了许多额外的功能&#xff0c;使得在 Jupyter Notebook 中编写和执行 Python 代码更加方便和强大。所以jupyter使用的是IPython的语法 IPytho…

彩色图像处理在数字图像处理中的应用(数字图像处理概念 P5)

文章目录 彩色模型伪彩色处理全彩色数字图像处理基础彩色变换平滑和锐化 彩色模型 伪彩色处理 全彩色数字图像处理基础 彩色变换 平滑和锐化

有名管道及其应用

创建FIFO文件 1.通过命令&#xff1a; mkfifo 文件名 2.通过函数: mkfifo #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数&#xff1a; -pathname&#xff1a;管道名称的路径 -mode&#xff1a;文件的权限&a…

MySQL学习笔记5

1、MySQL中的SQL语句&#xff1a; SQL 是 Structure Query Language(结构化查询语言)的缩写,它是使用关系模型的数据库应 用语言,由 IBM 在 20 世纪 70 年代开发出来,作为 IBM 关系数据库原型 System R 的原型关 系语言,实现了关系数据库中的信息检索。 20 世纪 80 年代初,美…

【ardunio】青少年机器人四级实操代码(2023年9月)

目录 一、题目 二、示意图 三、流程图 四、硬件连接 1、舵机 2、超声波 3、LED灯 五、程序 一、题目 实操考题(共1题&#xff0c;共100分) 1. 主题&#xff1a; 迎宾机器人 器件&#xff1a;Atmega328P主控板1块&#xff0c;舵机1个&#xff0c;超声波传感器1个&…

OLTP和OLAP有什么区别和不同?

OLTP概念 操作型处理&#xff0c;叫联机事务处理OLTP(On-LineTransactionProcessing)&#xff0c;主要目标是做数据处理&#xff0c;它是针对具体业务在数据库联机的日常操作&#xff0c;通常对少数记录进行查询、修改。 用户较为关心操作的响应时间、数据的安全性、完整性和…

分享78个Python源代码总有一个是你想要的

分享78个Python源代码总有一个是你想要的 源码下载链接&#xff1a;https://pan.baidu.com/s/1ZhXDsVuYsZpOUQIUjHU2ww?pwd8888 提取码&#xff1a;8888 下面是文件的名字。 12个python项目源码 Apache Superset数据探查与可视化平台v2.0.1 API Star工具箱v0.7.2 Archery…