AVL树(二叉搜索树)

news2024/9/21 20:54:34

AVL树

  • 1.1 AVL树的概念
  • 1.2 AVL树节点的定义
  • 1.3 AVL树的旋转
    • 1.3.1 右旋(右单旋)
    • 1.3.2 左旋(左单旋)
    • 1.3.3 左右双旋(先左单旋再右单旋)
    • 1.3.4 右左双旋(先右单旋再左单旋)
  • 1.4 AVL树的插入
  • 1.5 AVL树的遍历和树的高度

1.1 AVL树的概念

1.AVL树是一种自平衡的二叉搜索树,它的特点是任何一个节点的左右子树的高度差不超过1。AVL树的平衡性保证了它的查找、插入和删除操作的时间复杂度都是O(logn)。AVL树的平衡调整是通过旋转操作来实现的,有四种基本的旋转方式:左旋、右旋、左右旋和右左旋。AVL树的命名来自于它的发明者Adelson-Velsky和Landis。
2.一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  1. 它的左右子树都是AVL树;
  2. 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。

因为AVL树的特点是任何一个节点的左右子树的高度差不超过1,所以当插入一个节点,需要判断平衡因子是否满足,不满足则需要调整,这就导致插入时效率低下,实际中用的并不多,但是AVL树的旋转 是需要了解的。

1.2 AVL树节点的定义

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或-1的节点被认为是平衡的。带有平衡因子-2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

template<class K, class V>
struct AVLTreeNode
{
    AVLTreeNode<K, V>* _left;   // 该节点的左孩子
    AVLTreeNode<K, V>* _right;  // 该节点的右孩子
    AVLTreeNode<K, V>* _parent; // 该节点的双亲
    pair<K, V> _kv;             // 该节点所保存的数据
    int _bf; // balance factor  // 该节点的平衡因子

    AVLTreeNode(const pair<K, V>& kv)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _kv(kv)
        , _bf(0)
    {}
};

二叉搜索树的头文件AVLTree.h:

template<class K, class V>
class AVLTree
{
    typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv); //插入
	void InOrder();  //遍历
	int Height(); //树的高度
private:
	void RotateR(Node* parent); //右旋
	void RotateL(Node* parent); //左旋
	void RotateLR(Node* parent); //左右双旋
	void RotateRL(Node* parent); //右左双旋
	void _InOrder(Node* root); //遍历
	int _Height(Node* root); //树的高度
private:
    Node* _root = nullptr;
};

1.3 AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,
使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:左旋、右旋、左右旋和右左旋。
至于哪四种,只需要画图看看就很会清楚:
在这里插入图片描述
如果按顺序插入,可以看到每个节点的左右子树的高度差不超过1,所以不需要旋转。

1.3.1 右旋(右单旋)

而如果如下图,那么就需要旋转,其中,parent为平衡因子大于1的结点,subL为parent的左节点,subLR为parent的右节点;下图的树,可能为某棵树的子树,subLR可能为空,也可能不为空

如下图,subLR不为空,parent为平衡因子大于1的结点:
在这里插入图片描述

如何旋转?让parent变为subL的右子树,subL的右子树即subLR变为parent的左子树,这样可以降低高度并且旋转后仍为AVL树,如下图:
在这里插入图片描述
代码于解析如下:

    void RotateR(Node* parent) //右单旋
    {
        //找到旋转需要的结点
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        //parent可能为某一棵树的子树,所以parent->_parent有两种情况,所以先更改别的指针
        parent->_left = subLR;
        //subLR可能为空也可能不为空,所以这里需要判断一下,防止指针非法访问
        if (subLR)
            subLR->_parent = parent;
        //接下来处理parent->_parent的节点,先找到parent->_parent节点的位置,然后可以修改parent->_parent的指针
        Node* ppnode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        //判断parent->_parent的两种情况
        if (parent == _root) //判断条件也可以为 ppnode == nullptr
        {
            //如果parent为根节点,则需要变根节点
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            //否则,判断parent为左子树还是右子树
            if (ppnode->_left == parent)
            {
                ppnode->_left = subL;
            }
            else
            {
                ppnode->_right = subL;
            }
            //修改指针
            subL->_parent = ppnode;
        }
        //更新平衡因子,高度降低,且parent、subL左右子树高度差为0
        parent->_bf = subL->_bf = 0;
    }

1.3.2 左旋(左单旋)

左单旋和右单旋思路基本相同。
在这里插入图片描述
代码如下:

    void RotateL(Node* parent)  //左单旋
    {
        //找到旋转需要的结点
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        //parent可能为某一棵树的子树,所以parent->_parent有两种情况,所以先更改别的指针
        parent->_right = subRL;
        //subRL可能为空也可能不为空,所以这里需要判断一下,防止指针非法访问
        if (subRL)
            subRL->_parent = parent;
        //接下来处理parent->_parent的节点,先找到parent->_parent节点的位置,然后可以修改parent->_parent的指针
        Node* ppnode = parent->_parent;

        subR->_left = parent;
        parent->_parent = subR;
        //判断parent->_parent的两种情况
        if (ppnode == nullptr)
        {
            //如果parent为根节点,则需要变根节点
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            //否则,判断parent为左子树还是右子树
            if (ppnode->_left == parent)
            {
                ppnode->_left = subR;
            }
            else
            {
                ppnode->_right = subR;
            }

            subR->_parent = ppnode;
        }
        //更新平衡因子,高度降低,且parent、subR左右子树高度差为0
        parent->_bf = subR->_bf = 0;
    }

1.3.3 左右双旋(先左单旋再右单旋)

如果按如下图插入,该如何旋转?
在这里插入图片描述

如果右单旋上图会是什么样子?
在这里插入图片描述

可以看到右旋后的树高度未降低,所以右旋后的不是AVL树,那么左旋?
在这里插入图片描述
可以看到树的高度虽然降低,但是旋转后的树违反了AVL树的规定,即左节点大于右结点,所以左旋后的不是AVL树。那么该如何旋转?所以这里必须旋转两次,即先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。如下图所示:
在这里插入图片描述

    void RotateLR(Node* parent) //左右双旋
    {
        //找到旋转需要更改的结点
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        //旋转之前,保存subLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
        int bf = subLR->_bf;
        //先左旋再右旋
        RotateL(parent->_left);
        RotateR(parent);
        //对subLR结点的平衡因子进行讨论
        //旋转之前,subLR的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
        if (bf == 1)
        {
            parent->_bf = 0;
            subLR->_bf = 0;
            subL->_bf = -1;
        }
        else if (bf == -1)
        {
            parent->_bf = 1;
            subLR->_bf = 0;
            subL->_bf = 0;
        }
        else if (bf == 0)
        {
            parent->_bf = 0;
            subLR->_bf = 0;
            subL->_bf = 0;
        }
        else
        {
            //否则直接报错
            assert(false);
        }
    }

1.3.4 右左双旋(先右单旋再左单旋)

右左双旋代码如下:

    void RotateRL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        int bf = subRL->_bf;

        RotateR(parent->_right);
        RotateL(parent);

        if (bf == 1)
        {
            subR->_bf = 0;
            parent->_bf = -1;
            subRL->_bf = 0;
        }
        else if (bf == -1)
        {
            subR->_bf = 1;
            parent->_bf = 0;
            subRL->_bf = 0;
        }
        else if (bf == 0)
        {
            subR->_bf = 0;
            parent->_bf = 0;
            subRL->_bf = 0;
        }
        else
        {
            assert(false);
        }
    }

1.4 AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

1.先按照二叉搜索树的规则将节点插入到AVL树中
2.新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否
破坏了AVL树的平衡性
插入后,pParent的平衡因子一定需要调整,在插入之前,pParent的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:

  1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
  2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可

此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
1.如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整
成0,此时满足AVL树的性质,插入成功
2.如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更
新成正负1,此 时以pParent为根的树的高度增加,需要继续向上更新
3.如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进
行旋转处理

代码如下:

bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            return true;
        }

        Node* parent = nullptr;
        Node* cur = _root;
        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);
        if (parent->_kv.first > kv.first)
        {
            parent->_left = cur;
        }
        else
        {
            parent->_right = cur;
        }
        cur->_parent = parent;

        // 更新平衡因子
        while (parent)
        {
            if (cur == parent->_right)
            {
                parent->_bf++;
            }
            else
            {
                parent->_bf--;
            }

            if (parent->_bf == 1 || parent->_bf == -1)
            {
                // 继续更新
                parent = parent->_parent;
                cur = cur->_parent;
            }
            else if (parent->_bf == 0)
            {
                break;
            }
            else if (parent->_bf == 2 || parent->_bf == -2)
            {
                // 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
                if (parent->_bf == 2 && cur->_bf == 1)
                {
                    RotateL(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == -1)
                {
                    RotateR(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == 1)
                {
                    RotateLR(parent);
                }
                else if (parent->_bf == 2 && cur->_bf == -1)
                {
                    RotateRL(parent);
                }
                else
                {
                    assert(false);
                }

                break;
            }
            else
            {
                assert(false);
            }
        }
        return true;
    }

1.5 AVL树的遍历和树的高度

在类中调用AVL树的遍历和树的高度函数时,在类中套一层,因为类外不能访问私有成员,代码如下:

    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    int Height()
    {
        return _Height(_root);
    }
    int _Height(Node* root)
    {
        if (root == NULL)
            return 0;

        int leftH = _Height(root->_left);
        int rightH = _Height(root->_right);

        return leftH > rightH ? leftH + 1 : rightH + 1;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
        {
            return;
        }
        _InOrder(root->_left);
        cout << root->_kv.first << " ";
        _InOrder(root->_right);
    }

完整代码如下:

#pragma once

template<class K, class V>
struct AVLTreeNode
{
    AVLTreeNode<K, V>* _left;   // 该节点的左孩子
    AVLTreeNode<K, V>* _right;  // 该节点的右孩子
    AVLTreeNode<K, V>* _parent; // 该节点的双亲
    pair<K, V> _kv;             // 该节点所保存的数据
    int _bf; // balance factor  // 该节点的平衡因子

    AVLTreeNode(const pair<K, V>& kv)
        :_left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _kv(kv)
        , _bf(0)
    {}
};

template<class K, class V>
class AVLTree
{
    typedef AVLTreeNode<K, V> Node;
public:
    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            return true;
        }

        Node* parent = nullptr;
        Node* cur = _root;
        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);
        if (parent->_kv.first > kv.first)
        {
            parent->_left = cur;
        }
        else
        {
            parent->_right = cur;
        }
        cur->_parent = parent;

        // 更新平衡因子
        while (parent)
        {
            if (cur == parent->_right)
            {
                parent->_bf++;
            }
            else
            {
                parent->_bf--;
            }

            if (parent->_bf == 1 || parent->_bf == -1)
            {
                // 继续更新
                parent = parent->_parent;
                cur = cur->_parent;
            }
            else if (parent->_bf == 0)
            {
                break;
            }
            else if (parent->_bf == 2 || parent->_bf == -2)
            {
                // 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
                if (parent->_bf == 2 && cur->_bf == 1)
                {
                    RotateL(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == -1)
                {
                    RotateR(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == 1)
                {
                    RotateLR(parent);
                }
                else if (parent->_bf == 2 && cur->_bf == -1)
                {
                    RotateRL(parent);
                }
                else
                {
                    assert(false);
                }

                break;
            }
            else
            {
                assert(false);
            }
        }
        return true;
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }

    int Height()
    {
        return _Height(_root);
    }
private:
    int _Height(Node* root)
    {
        if (root == NULL)
            return 0;

        int leftH = _Height(root->_left);
        int rightH = _Height(root->_right);

        return leftH > rightH ? leftH + 1 : rightH + 1;
    }
    void _InOrder(Node* root)
    {
        if (root == nullptr)
        {
            return;
        }
        _InOrder(root->_left);
        cout << root->_kv.first << " ";
        _InOrder(root->_right);
    }
    void RotateR(Node* parent) //右单旋
    {
        //找到旋转需要的结点
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        //parent可能为某一棵树的子树,所以parent->_parent有两种情况,所以先更改别的指针
        parent->_left = subLR;
        //subLR可能为空也可能不为空,所以这里需要判断一下,防止指针非法访问
        if (subLR)
            subLR->_parent = parent;
        //接下来处理parent->_parent的节点,先找到parent->_parent节点的位置,然后可以修改parent->_parent的指针
        Node* ppnode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        //判断parent->_parent的两种情况
        if (parent == _root) //判断条件也可以为 ppnode == nullptr
        {
            //如果parent为根节点,则需要变根节点
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            //否则,判断parent为左子树还是右子树
            if (ppnode->_left == parent)
            {
                ppnode->_left = subL;
            }
            else
            {
                ppnode->_right = subL;
            }
            //修改指针
            subL->_parent = ppnode;
        }
        //更新平衡因子,高度降低,且parent、subL左右子树高度差为0
        parent->_bf = subL->_bf = 0;
    }
    void RotateL(Node* parent)  //左单旋
    {
        //找到旋转需要的结点
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        //parent可能为某一棵树的子树,所以parent->_parent有两种情况,所以先更改别的指针
        parent->_right = subRL;
        //subRL可能为空也可能不为空,所以这里需要判断一下,防止指针非法访问
        if (subRL)
            subRL->_parent = parent;
        //接下来处理parent->_parent的节点,先找到parent->_parent节点的位置,然后可以修改parent->_parent的指针
        Node* ppnode = parent->_parent;

        subR->_left = parent;
        parent->_parent = subR;
        //判断parent->_parent的两种情况
        if (ppnode == nullptr)
        {
            //如果parent为根节点,则需要变根节点
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            //否则,判断parent为左子树还是右子树
            if (ppnode->_left == parent)
            {
                ppnode->_left = subR;
            }
            else
            {
                ppnode->_right = subR;
            }

            subR->_parent = ppnode;
        }
        //更新平衡因子,高度降低,且parent、subR左右子树高度差为0
        parent->_bf = subR->_bf = 0;
    }
    void RotateLR(Node* parent) //左右双旋
    {
        //找到旋转需要更改的结点
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        //旋转之前,保存subLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子
        int bf = subLR->_bf;
        //先左旋再右旋
        RotateL(parent->_left);
        RotateR(parent);
        //对subLR结点的平衡因子进行讨论
        //旋转之前,subLR的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
        if (bf == 1)
        {
            parent->_bf = 0;
            subLR->_bf = 0;
            subL->_bf = -1;
        }
        else if (bf == -1)
        {
            parent->_bf = 1;
            subLR->_bf = 0;
            subL->_bf = 0;
        }
        else if (bf == 0)
        {
            parent->_bf = 0;
            subLR->_bf = 0;
            subL->_bf = 0;
        }
        else
        {
            //否则直接报错
            assert(false);
        }
    }
    void RotateRL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        int bf = subRL->_bf;

        RotateR(parent->_right);
        RotateL(parent);

        if (bf == 1)
        {
            subR->_bf = 0;
            parent->_bf = -1;
            subRL->_bf = 0;
        }
        else if (bf == -1)
        {
            subR->_bf = 1;
            parent->_bf = 0;
            subRL->_bf = 0;
        }
        else if (bf == 0)
        {
            subR->_bf = 0;
            parent->_bf = 0;
            subRL->_bf = 0;
        }
        else
        {
            assert(false);
        }
    }
private:
    Node* _root = nullptr;
};

测试代码如下:

void Test_AVLTree()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		t1.Insert(make_pair(e, e));
	}
	t1.InOrder();
	cout << "Height: " << t1.Height() << endl;
}
int main()
{
	Test_AVLTree();
	return 0;
}

运行结果如下:
在这里插入图片描述

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

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

相关文章

进程 的初识

程序和进程有什么区别 程序是静态的概念&#xff0c;gcc xxx.c -o pro 磁盘中生成的文件&#xff0c;叫做程序。进程是程序的一次运行活动&#xff0c;通俗点的意思就是程序跑起来了&#xff0c;系统中就多了一个进程。 如何查看系统中有哪些进程 使用 ps 指令&#xff08;完整…

❤ vue组件的生命周期

❤ vue组件的生命周期 介绍 在vue组件中&#xff0c;生命周期指的是从组件创建开始&#xff0c;到组件销毁&#xff0c;所经历的整个过程&#xff1b;在这个过程中的一些不同的阶段&#xff0c;vue会调用指定的一些组件方法。基本生命周期函数有下面几个阶段&#xff1a;创建…

Python中的dataclass:简化数据类的创建

Python中的dataclass是一个装饰器&#xff0c;用于自动添加一些常见的方法&#xff0c;如构造函数、__repr__、__eq__等。它简化了创建数据类的过程&#xff0c;减少了样板代码&#xff0c;提高了代码的可读性和可维护性。有点类似java里面的Java Bean。 让我们看一个简单的例子…

将.doc文档的默认打开方式从WPS修改为word office打开方式的具体方法(以win 10 操作系统为例)

将.doc文档的默认打开方式从WPS修改为word office打开方式的具体方法&#xff08;以win 10 操作系统为例&#xff09; 随着近几年WPS软件的不断完善和丰富&#xff0c;在某些方面取得了具有特色的优势。在平时编辑.doc文档时候也常常用到wps软件&#xff0c;不过WPS文献也存在…

【分布式技术专题】RocketMQ延迟消息实现原理和源码分析

痛点背景 业务场景 假设有这么一个需求&#xff0c;用户下单后如果30分钟未支付&#xff0c;则该订单需要被关闭。你会怎么做&#xff1f; 之前方案 最简单的做法&#xff0c;可以服务端启动个定时器&#xff0c;隔个几秒扫描数据库中待支付的订单&#xff0c;如果(当前时间-订…

EasyPoi导出 导入(带校验)简单示例 EasyExcel

官方文档 : http://doc.wupaas.com/docs/easypoi pom的引入: <!-- easyPoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version></dep…

分布式协调组件Zookeeper

Zookeeper介绍 什么是Zookeeper ZooKeeper 是⼀种分布式协调组件&#xff0c;用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper 通过其简单的架构和 API 解决了这个问题。ZooKeeper 允许开发人员专注于核心应用程序逻辑&#xff0c;而不必担心应用…

【Linux】多线程——线程引入 | 线程控制

文章目录 一、Linux多线程1. 线程概念2. 线程创建3. 线程和进程4. 线程的优缺点 二、线程控制1. 线程创建2. 线程终止3. 线程等待4. 线程分离5. 线程局部存储 三、线程封装 一、Linux多线程 一级页表和二级页表都是key/val模型&#xff0c;一级页表的key是第一份的10个比特位&a…

(统计学习方法|李航)第一章统计学习方法概论——四五六节模型评估与模型选择,正则化与交叉验证,泛化能力

一&#xff0c;模型评估与模型选择 1.训练误差与测试误差 假如我们有100个数据。80条记录给训练集&#xff0c;10条记录给测试集&#xff0c;10条记录给验证集 先在训练集中训练模型&#xff0c; 再在验证集上测试看哪种模型更拟合 最后用测试集算出成绩 表示决策函数 模型…

数据清理在数据科学中的重要性

什么是数据清理&#xff1f; 推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 在数据科学中&#xff0c;数据清理是识别不正确数据并修复错误的过程&#xff0c;以便最终数据集可供使用。错误可能包括重复字段、格式不正确、字段不完整、数据不相关或不准…

基于kettle实现pg数据定时转存mongodb

mogodb 待创建 基于kettle实现pg数据定时转存mongodb_kettle 实时迁移 mongodb_呆呆的私房菜的博客-CSDN博客

链表和哈希Set

1 LinkedList集合类 LinkedList集合类底层是使用双向链表实现的&#xff0c;相较于ArrayList&#xff0c;更方便进行增删操作。 在增删查改方面&#xff0c;新增了头尾操作&#xff0c;比如从头部插入、尾部插入、头部删除、尾部删除、头部查询和尾部查询等操作。由于有头尾的…

SpringCloud实用篇3----Docker

1.初识Docker 1.1 什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署&#xff0c;环境不一定一致…

gitblit windows部署

1.官网下载 往死慢&#xff0c;我是从百度找的1.9.1&#xff0c;几乎就是最新版 http://www.gitblit.com/ 2.解压 下载下来是一个zip压缩包&#xff0c;直接解压即可 3.配置 3.1.配置资源库路径 找到data文件下的gitblit.properties文件&#xff0c;用Notepad打开 **注意路…

云原生可观测框架 OpenTelemetry 基础知识(架构/分布式追踪/指标/日志/采样/收集器)...

什么是 OpenTelemetry&#xff1f; OpenTelemetry 是一个开源的可观测性框架&#xff0c;由云原生基金会(CNCF)托管。它是 OpenCensus 和 OpenTracing 项目的合并。旨在为所有类型的可观测信号(如跟踪、指标和日志)提供单一标准。 https://opentelemetry.iohttps://www.cncf.io…

微服务Eureka注册中心

目录 一、Eureka的结构和作用 二、搭建eureka-server 三、服务注册 四、服务发现 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 存在的问题&#xff1a; order-service在发起远程调用的时候&#xff0c;该如何得知user-service实例的ip地址…

SpringCloud 尚硅谷 微服务简介以及Eureka使用

写在前面 该系列博客仅用于本人学习尚硅谷课程SpringCloud笔记&#xff0c;其中的错误在所难免&#xff0c;如有错误恳请指正。 官方源码地址&#xff1a;https://github.com/zzyybs/atguigu_spirngcloud2020 什么是SpringCloud Spring Cloud是微服务一站式服务解决方案&…

芒果 TV 基于 Flink 的实时数仓建设实践

公司简介&#xff1a;芒果 TV 作为湖南广电旗下互联网视频平台&#xff0c;在“一云多屏&#xff0c;多元一体”的战略指导下&#xff0c;通过内容自制&#xff0c;培植核心竞争力&#xff0c;从独播、独特走向独创&#xff0c;并通过市场化运作完成 A 轮、B 轮融资&#xff0c…

数据库活动监控(DAM)

在当今数据驱动的世界中&#xff0c;组织在保护存储在数据库中的机密数据并确保其完整性方面面临着越来越多的挑战。数据库审计通过提供全面的数据库活动监控方法&#xff0c;在应对这些挑战方面发挥着至关重要的作用。 数据库活动监控&#xff08;Database Activity Monitori…

【Redis】初学Redis

目录 使用Redisyum安装redis启动redis操作redis设置远程连接 Redis路线Redis 使用Redis yum安装redis 使用命令&#xff0c;直接将Redis安装到linux服务器&#xff1a; yum -y install redis启动redis redis-server /etc/redis.conf &操作redis redis-cli设置远程连接…