C++ -- 红黑树封装set和map

news2024/9/20 22:31:35

文章目录

  • 1. 红黑树概念和性质
    • 1.1 概念
    • 1.2 性质
    • 1.3 实例
    • 1.4 分析
  • 2. 节点定义
  • 3. 插入操作
  • 4. 检测
  • 5. 红黑树代码
  • 6. 红黑树实现set和map
    • 6.0 类设计图
    • 6.1 红黑树包装复用
    • 6.2 红黑树实现set
    • 6.3 红黑树实现map
    • 6.4 剖析代码
      • 6.4.1 如何复用
      • 6.4.2 如何区分set和map比较方式
      • 6.4.3 set和map仿函数作用
      • 6.4.4 怎么理解迭代器及其模板参数
      • 6.4.5 迭代器中operator++()和operator--()
      • 6.4.6 如何保证set的key值不能修改
      • 6.4.7 如何保证map的key值不被修改value值可修改
      • 6.4.8 怎么理解map中的operator[] ()

1. 红黑树概念和性质

1.1 概念

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

1.2 性质

  1. 每个节点要么是红色要么是黑色
  2. 整颗树的根节点是黑色
  3. 如果一个节点是红色,那么它的左右孩子节点是黑色
  4. 对每个节点,从该节点到其所有后代叶子节点的路径上均包含相同数目的黑色节点
  5. 每个叶子节点的左右空节点都是黑色

1.3 实例

1.4 分析

  1. 最短路径:路径上的节点全是黑色
  2. 最长路径:路径上的节点有黑色和红色

怎么保证最短路径的节点数目的两倍等于或者小于最长路径的节点数目?性质1-5来保证。

红黑树相比AVL树优势在哪里?AVL树需要旋转的树,红黑树不一定旋转(这样性能和AVL树差不多,但是红黑树更胜一筹),列举:

2. 节点定义

enum Color
{
	RED,
	BLACK
};

template <class KEY, class VALUE>
struct red_blackTreeNode
{
	red_blackTreeNode<KEY, VALUE>* _left; //左节点指向
	red_blackTreeNode<KEY, VALUE>* _right; //右节点指向
	red_blackTreeNode<KEY, VALUE>* _parent; //父节点指向
	pair<KEY, VALUE> _couple; //存储key/value
	Color _color; //颜色标签

	red_blackTreeNode(const pair<KEY, VALUE>& couple)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_couple(couple)
		,_color(RED) //默认插入红色节点(插入红色节点才能保证对每个节点,从该节点到其所有后代叶子节点的路径上均包含相同数目的黑色节点)
};

3. 插入操作

  1. 根节点为空
    1. new节点,改变根指向,返回true
  2. 根节点不为空
    1. 找到插入位置
      1. 右查找:当前节点key值 < 插入节点key值
      2. 左查找:当前节点key值 > 插入节点key值
      3. 当前节点key值 = 插入节点key值 :直接返回false
    2. 在对应待插入位置插入
      1. new节点,当前插入位置指向该节点
      2. 右插入:当前节点key值 < 插入节点key值
      3. 左插入: 当前节点key值 > 插入节点key值
    3. 当前被插入节点父指针指向指向被连接节点
    4. 调整(TODO)

如何调整?

  1. uncle存在且为红
    1. parent为红变黑
    2. grandpa为黑变红,uncle为红变黑
    3. 若grandpa_parent存在且为红,调整cur=grandpa,parent=grandpa_parent,grandpa=grandpa_parent->_parent,uncle=grandpa_parent -> _parent-> _left,调整后回到开始位置继续判断uncle是否存在且为红
  1. uncle不存在时
    1. parent为红变黑
    2. 左低右高需旋转,grandpa为黑变红

抽象图

  1. 情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红

解决方案:parent为红变黑,uncle为红变黑,grandpa为黑变红,如果grandpa的父节点是红色,则cur指向granda继续向上调整,如果grandpa的父节点是黑色,则停止调整。

  1. 情况二:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑

解决方案:左高右低采取右旋,左低右高采取左旋,parent为红变黑,grandpa为黑变红

  1. 情况三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑

解决方案:先双旋(先左后右旋或者先后后左旋),最终cur变黑,grandpa变红

bool insert(const pair<KEY, VALUE>& couple)
{
    if (_root == nullptr) //根为空:直接new并指向返回
    {
        _root = new node(couple);
        return true;
    }

    /*找插入位置*/
    node* parent = nullptr; //起初根节点的父节点为nullptr
    node* cur = _root; //被插入节点指向
    while (cur)
    {
        if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
        {
            parent = cur;
            cur = cur->_left;
        }
        else //当前节点key值 = 插入节点key值:直接退出
        {
            return false;
        }
    }

    /*在对应位置插入*/
    cur = new node(couple);
    if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
    {
        parent->_left = cur;
    }
    else //右插入:当前节点key值 < 插入节点key值
    {
        parent->_right = cur;
    }
    cur->_parent = parent; //反向链接

    /*调整*/
    while (parent && parent->_color == RED)
    {
        node* grandpa = parent->_parent;
        if (grandpa == nullptr)
        {
            break;
        }

        if (grandpa->_left == parent)
        {
            node* uncle = grandpa->_right;
            if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
            {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandpa->_color = RED;

                /*继续往上调整*/
                cur = grandpa;
                parent = cur->_parent;
            }
            else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
            {

                if (cur == parent->_left) //左高右低(右旋)
                {
                    //       grandpa
                    //       /     \ 
                    //   parent   uncle
                    //     /
                    //   cur

                    rightSingleRotate(grandpa); //grandpa为轴心右旋
                    parent->_color = BLACK;
                    grandpa->_color = RED;
                }
                else //
                {
                    //       grandpa
                    //       /     \ 
                    //   parent   uncle
                    //       \
                        //       cur

                    leftSingleRotate(parent); //parent为轴心左旋
                    rightSingleRotate(grandpa); //grandpa为轴心右旋
                    cur->_color = BLACK;
                    grandpa->_color = RED;
                }

                break;
            }
        }
        else //grandpa->_left == parent
        {
            node* uncle = grandpa->_left;
            if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
            {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandpa->_color = RED;

                /*继续往上调整*/
                cur = grandpa;
                parent = cur->_parent;
            }
            else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
            {
                if (cur == parent->_right) //左高右低(左单旋)
                {
                    //       grandpa
                    //       /     \ 
                    //   uncle   parent
                    //               \
                        //               cur

                    leftSingleRotate(grandpa); //grandpa为轴心左旋
                    parent->_color = BLACK;
                    grandpa->_color = RED;
                }
                else //cur == parent->_left
                {
                    //       grandpa
                    //       /     \ 
                    //   uncle   parent
                    //            /
                    //           cur

                    rightSingleRotate(parent); //parent为轴心右旋
                    leftSingleRotate(grandpa); //grandpa为轴心左旋
                    cur->_color = BLACK;
                    grandpa->_color = RED;
                }

                break;
            }
        }
    }
}
void leftSingleRotate(node* parent) //左单旋
{
    //记录指针
    node* parent_RightChild = parent->_right; //parent_RightChild = cur
    node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
    node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

    parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
    if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
    {
        parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
    }
    parent_RightChild->_left = parent; //让parent放在cur的左子树位置
    parent->_parent = parent_RightChild; //parent的父节点指向指向cur

    //cur(parent_RightChild)变为子树或者整颗树的根
    if (parent_parent == nullptr) //parent是整颗树的根
    {
        _root = parent_RightChild; //cur(parent_RightChild)就是根
        _root->_parent = nullptr;
    }
    else //parent是局部子树的根
    {
        if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
        {
            parent_parent->_left = parent_RightChild;
        }
        else //parent节点在父节点的右子树位置
        {
            parent_parent->_right = parent_RightChild;
        }
        parent_RightChild->_parent = parent_parent; //cur(parent_RightChild)指向局部根的父节点
    }
}

void rightSingleRotate(node* parent) //右单旋
{
    //记录指针
    node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
    node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
    node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

    parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
    if (parent_LeftChild_RightChild != nullptr)
    {
        parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
    }
    parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
    parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur

    //cur(parent_LeftChild)变为子树或者整颗树的根
    if (parent_parent == nullptr) //parent是整颗树的根
    {
        _root = parent_LeftChild; //cur(parent_LeftChild)就是根
        _root->_parent = nullptr;
    }
    else //parent是局部子树的根
    {
        if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
        {
            parent_parent->_left = parent_LeftChild;
        }
        else //parent节点在父节点的右子树位置
        {
            parent_parent->_right = parent_LeftChild;
        }
        parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
    }
}

4. 检测


void _inOrder(node* root) //中序遍历
{
    if (root == nullptr)
    {
        return;
    }
    _inOrder(root->_left);
    cout << root->_couple.first << " ";
    _inOrder(root->_right);
}

int getreferenceValue(node* root)
{
    node* cur = root;
    int referenceValue = 0;
    while (cur)
    {
        if (cur->_color == BLACK)
            ++referenceValue;
        cur = cur->_left;
    }
    return referenceValue;
}

bool _checkoutRedRootParent(node* root) //检测红色节点的父节点是否为红
{
    if (root == nullptr)
        return true;
    if (root->_color == RED
        && root->_parent
        && root->_parent->_color == RED) //红色节点一定有父节点
    {
        cout << "exist continuous RED root!" << endl;
        return false;
    }

    return _checkoutRedRootParent(root->_left)
        && _checkoutRedRootParent(root->_right);
}

void _eachPathBlackRootNumber(node* root, int blackRootNumber, int& referenceValue)
{
    if (root == nullptr)
    {
        if (blackRootNumber != referenceValue)
        {
            cout << "this path black root number is not equal!" << endl;
            return;
        }
        return;
    }

    if (root->_color == BLACK)
        ++blackRootNumber;

    _eachPathBlackRootNumber(root->_left, blackRootNumber, referenceValue);
    _eachPathBlackRootNumber(root->_right, blackRootNumber, referenceValue);
}

int _heightDiffer(node* root)
{
    if (root == nullptr)
        return 0;
    int leftHeight = _heightDiffer(root->_left);
    int rightHeight = _heightDiffer(root->_right);

    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

void inOrder()
{
    _inOrder(_root);
    cout << endl;
}

bool isBalance()
{
    if (_root && _root->_color == RED) //整颗的根必须为黑
    {
        cout << "color of _root is RED!" << endl;
        return false;
    }

    //如果一个节点是红色,那么它的左右孩子节点是黑色
    if (!_checkoutRedRootParent(_root)) //检测红色节点的父节点是否为红
    {
        return false;
    }

    int referenceValue = getreferenceValue(_root); //求最左边路径的黑色节点数量作为参考值

    //检测每条路径的黑色节点的数量
    _eachPathBlackRootNumber(_root, 0, referenceValue);

    return true;
}

int heightDiffer() //检测高度
{ 
    return _heightDiffer(_root);
}

5. 红黑树代码

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


enum Color
{
    RED,
    BLACK,
};

template <class KEY, class VALUE>
struct red_blackTreeNode
{
	red_blackTreeNode<KEY, VALUE>* _left; //左节点指向
	red_blackTreeNode<KEY, VALUE>* _right; //右节点指向
	red_blackTreeNode<KEY, VALUE>* _parent; //父节点指向
	pair<KEY, VALUE> _couple; //存储key/value
	Color _color; //颜色标签

	red_blackTreeNode(const pair<KEY, VALUE>& couple)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_couple(couple)
		,_color(RED) //默认插入红色节点(插入红色节点才能保证对每个节点,从该节点到其所有后代叶子节点的路径上均包含相同数目的黑色节点)
    {}
};

template <class KEY, class VALUE>
class red_blackTree
{
    typedef red_blackTreeNode<KEY, VALUE> node;
public:
    red_blackTree()
        :_root(nullptr)
    {}

    node* find(const pair<KEY, VALUE>& key)
    {
        node* cur = _root;
        while (cur)
        {
            if (cur->_couple.first < key)
                cur = cur->_right;
            else if (cur->_couple.first > key)
                cur = cur->_left;
            else
                return cur;
        }
        return nullptr;
    }

    red_blackTree(const red_blackTree<KEY, VALUE>& tree) //拷贝构造(递归复制)
    {
        _root = _copy(tree._root);
    }

    bool insert(const pair<KEY, VALUE>& couple)
    {
        if (_root == nullptr) //根为空:直接new并指向返回
        {
            _root = new node(couple);
            return true;
        }

        /*找插入位置*/
        node* parent = nullptr; //起初根节点的父节点为nullptr
        node* cur = _root; //被插入节点指向
        while (cur)
        {
            if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
            {
                parent = cur;
                cur = cur->_left;
            }
            else //当前节点key值 = 插入节点key值:直接退出
            {
                return false;
            }
        }

        /*在对应位置插入*/
        cur = new node(couple);
        if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
        {
            parent->_left = cur;
        }
        else //右插入:当前节点key值 < 插入节点key值
        {
            parent->_right = cur;
        }
        cur->_parent = parent; //反向链接

        /*调整*/
        while (parent && parent->_color == RED)
        {
            node* grandpa = parent->_parent;
            if (grandpa == nullptr)
            {
                break;
            }

            if (grandpa->_left == parent)
            {
                node* uncle = grandpa->_right;
                if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandpa->_color = RED;

                    /*继续往上调整*/
                    cur = grandpa;
                    parent = cur->_parent;
                }
                else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
                {

                    if (cur == parent->_left) //左高右低(右旋)
                    {
                        //       grandpa
                        //       /     \ 
                        //   parent   uncle
                        //     /
                        //   cur

                        rightSingleRotate(grandpa); //grandpa为轴心右旋
                        parent->_color = BLACK;
                        grandpa->_color = RED;
                    }
                    else //
                    {
                        //       grandpa
                        //       /     \ 
                        //   parent   uncle
                        //       \
                        //       cur

                        leftSingleRotate(parent); //parent为轴心左旋
                        rightSingleRotate(grandpa); //grandpa为轴心右旋
                        cur->_color = BLACK;
                        grandpa->_color = RED;
                    }

                    break;
                }
            }
            else //grandpa->_left == parent
            {
                node* uncle = grandpa->_left;
                if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandpa->_color = RED;

                    /*继续往上调整*/
                    cur = grandpa;
                    parent = cur->_parent;
                }
                else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
                {
                    if (cur == parent->_right) //左高右低(左单旋)
                    {
                        //       grandpa
                        //       /     \ 
                        //   uncle   parent
                        //               \
                        //               cur

                        leftSingleRotate(grandpa); //grandpa为轴心左旋
                        parent->_color = BLACK;
                        grandpa->_color = RED;
                    }
                    else //cur == parent->_left
                    {
                        //       grandpa
                        //       /     \ 
                        //   uncle   parent
                        //            /
                        //           cur

                        rightSingleRotate(parent); //parent为轴心右旋
                        leftSingleRotate(grandpa); //grandpa为轴心左旋
                        cur->_color = BLACK;
                        grandpa->_color = RED;
                    }

                    break;
                }
            }
        }

        _root->_color = BLACK; //整颗树的根为黑
        return true; //插入成功
    }

    void inOrder()
    {
        _inOrder(_root);
        cout << endl;
    }

    bool isBalance()
    {
        if (_root && _root->_color == RED) //整颗的根必须为黑
        {
            cout << "color of _root is RED!" << endl;
            return false;
        }

        //如果一个节点是红色,那么它的左右孩子节点是黑色
        if (!_checkoutRedRootParent(_root)) //检测红色节点的父节点是否为红
        {
            return false;
        }

        int referenceValue = getreferenceValue(_root); //求最左边路径的黑色节点数量作为参考值

        //检测每条路径的黑色节点的数量
        _eachPathBlackRootNumber(_root, 0, referenceValue);

        return true;
    }

    int heightDiffer()
    {
        return _heightDiffer(_root);
    }

    ~red_blackTree() 
    {
        _destory(_root);
        _root = nullptr;
    }


private:
    void leftSingleRotate(node* parent) //左单旋
    {
        //记录指针
        node* parent_RightChild = parent->_right; //parent_RightChild = cur
        node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

        parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
        if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
        {
            parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
        }
        parent_RightChild->_left = parent; //让parent放在cur的左子树位置
        parent->_parent = parent_RightChild; //parent的父节点指向指向cur

        //cur(parent_RightChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_RightChild; //cur(parent_RightChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_RightChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_RightChild;
            }
            parent_RightChild->_parent = parent_parent; //cur(parent_RightChild)指向局部根的父节点
        }
    }

    void rightSingleRotate(node* parent) //右单旋
    {
        //记录指针
        node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
        node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

        parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
        if (parent_LeftChild_RightChild != nullptr)
        {
            parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
        }
        parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
        parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur

        //cur(parent_LeftChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_LeftChild; //cur(parent_LeftChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_LeftChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_LeftChild;
            }
            parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
        }
    }

    node* _copy(node* root)
    {
        if (root == nullptr)
            return nullptr;
        node* copyTreeRoot = new node(root->_couple);
        copyTreeRoot->_left = _copy(root->_left);
        copyTreeRoot->_right = _copy(root->_right);

        return copyTreeRoot;
    }

    void _inOrder(node* root) //中序遍历
    {
        if (root == nullptr)
        {
            return;
        }
        _inOrder(root->_left);
        cout << root->_couple.first << " ";
        _inOrder(root->_right);
    }

    int getreferenceValue(node* root)
    {
        node* cur = root;
        int referenceValue = 0;
        while (cur)
        {
            if (cur->_color == BLACK)
                ++referenceValue;
            cur = cur->_left;
        }
        return referenceValue;
    }

    bool _checkoutRedRootParent(node* root) //检测红色节点的父节点是否为红
    {
        if (root == nullptr)
            return true;
        if (root->_color == RED
            && root->_parent
            && root->_parent->_color == RED) //红色节点一定有父节点
        {
            cout << "exist continuous RED root!" << endl;
            return false;
        }

        return _checkoutRedRootParent(root->_left)
            && _checkoutRedRootParent(root->_right);
    }

    void _eachPathBlackRootNumber(node* root, int blackRootNumber, int& referenceValue)
    {
        if (root == nullptr)
        {
            if (blackRootNumber != referenceValue)
            {
                cout << "this path black root number is not equal!" << endl;
                return;
            }
            return;
        }

        if (root->_color == BLACK)
            ++blackRootNumber;

        _eachPathBlackRootNumber(root->_left, blackRootNumber, referenceValue);
        _eachPathBlackRootNumber(root->_right, blackRootNumber, referenceValue);
    }

    int _heightDiffer(node* root)
    {
        if (root == nullptr)
            return 0;
        int leftHeight = _heightDiffer(root->_left);
        int rightHeight = _heightDiffer(root->_right);

        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    void _destory(node* root) //后序遍历销毁
    {
        if (root == nullptr)
            return;

        _destory(root->_left);
        _destory(root->_right);
        delete root;
    }

private:
	node* _root;
};


/*测试*/
void test_red_blackTree1()
{
    //int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
    red_blackTree<int, int> tree;
    for (auto x : arr)
    {
        tree.insert(make_pair(x, x));
    }

    tree.inOrder(); //中序遍历(证明搜索二叉树)

    red_blackTree<int, int> copyTree(tree);
    copyTree.inOrder();

    cout << tree.isBalance() << endl; //判断是否为平衡树

}

void test_perfermance() //测试性能
{
    srand(time(nullptr));
    const size_t N = 1000000;
    red_blackTree<int, int> tree;
    for (size_t i = 0; i < N; ++i)
    {
        size_t x = rand();
        tree.insert(make_pair(x, x));
    }

    cout << tree.isBalance() << endl;

    cout << tree.heightDiffer() << endl;
}

6. 红黑树实现set和map

6.0 类设计图

6.1 红黑树包装复用

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

enum Color
{
    RED,
    BLACK,
};

/*采取复用:set --> T=KEY;map --> pair*/
template <class T>
struct red_blackTreeNode /*红黑树节点*/
{
	red_blackTreeNode<T>* _left; //左节点指向
	red_blackTreeNode<T>* _right; //右节点指向
	red_blackTreeNode<T>* _parent; //父节点指向
	T _field; //存储key/value或者只存储key(字段)
	Color _color; //颜色标签

	red_blackTreeNode(const T& field)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		, _field(field)
		,_color(RED) //默认插入红色节点(插入红色节点才能保证对每个节点,从该节点到其所有后代叶子节点的路径上均包含相同数目的黑色节点)
    {}
};

template <class T, class Reference, class Pointer>
struct red_blackTreeIterator /*红黑树迭代器*/
{
    typedef red_blackTreeNode<T> node;
    typedef red_blackTreeIterator<T, Reference, Pointer> _self; //red_blackTreeIterator对象本身
    node* _node;

    red_blackTreeIterator(node* node)
        :_node(node)
    {}

    red_blackTreeIterator(const red_blackTreeIterator<T, T&, T*>& self) /*满足隐式转换:iterator --> const_iterator*/
        :_node(self._node)
    {}

    Reference operator*() //解引用获取key值
    {
        return _node->_field;
    }

    Pointer operator->() //取地址获取节点字段(field)地址
    {
        return &_node->_field;
    }

    bool operator!=(const _self& self) //节点不相等
    {
        return _node != self._node;
    }

    _self& operator++() //中序遍历(非递归)
    {
        if (_node->_right) //该节点右子树不为空,下一个节点就是该节点右子树最左节点
        {
            node* leftOfRightNode = _node->_right;
            while (leftOfRightNode->_left)
            {
                leftOfRightNode = leftOfRightNode->_left;
            }
            _node = leftOfRightNode; //当前访问指向调整
        }
        else //右子树为空
        {
            node* cur = _node;
            node* parent = cur->_parent;
            while (parent && cur == parent->_right)
            {
                cur = parent;
                parent = parent->_parent;
            }

            _node = parent; //当前访问指向调整
        }
        
        return *this;
    }

    _self& operator--() //反中序遍历(非递归)
    {
        if (_node->_left) //该节点左子树不为空,下一个节点就是该节点左子树最右节点
        {
            node* rightOfLeftNode = _node->_left;
            while (rightOfLeftNode->_right)
            {
                rightOfLeftNode = rightOfLeftNode->_right;
            }
            _node = rightOfLeftNode; //当前访问指向调整
        }
        else //左子树为空
        {
            node* cur = _node;
            node* parent = cur->_parent;
            while (parent && cur == parent->_left)
            {
                cur = parent;
                parent = parent->_parent;
            }

            _node = parent; //当前访问指向调整
        }

        return *this;
    }
};

template <class KEY, class T, class Compare> /*第二个模板参数T控制是KEY还是pair<const KEY, VALUE>*/
class red_blackTree /*红黑树*/
{
    typedef red_blackTreeNode<T> node;
public:
    typedef red_blackTreeIterator<T, T&, T*> iterator;
    typedef red_blackTreeIterator<T, const T&, const T*> const_iterator;
public:
    red_blackTree()
        :_root(nullptr)
    {}

    /*迭代器操作*/
    iterator begin()
    {
        node* cur = _root;
        while (cur && cur->_left)
        {
            cur = cur->_left;
        }
        return iterator(cur);
    }

    iterator end()
    {
        return iterator(nullptr);
    }

    const_iterator begin() const
    {
        node* cur = _root;
        while (cur && cur->_left)
        {
            cur = cur->_left;
        }
        return const_iterator(cur);
    }

    const_iterator end() const
    {
        return const_iterator(nullptr);
    }

    node* find(const T& key)
    {
        Compare compare;
        node* cur = _root;
        while (cur)
        {
            if (compare(cur->_field) < compare(key))
                cur = cur->_right;
            else if (compare(cur->_field) > compare(key))
                cur = cur->_left;
            else
                return cur;
        }
        return nullptr;
    }

    red_blackTree(const red_blackTree<KEY, T, Compare>& tree) //拷贝构造(递归复制)
    {
        _root = _copy(tree._root);
    }

    pair<iterator, bool> Insert(const T& field)
    {
        Compare compare;

        if (_root == nullptr) //根为空:直接new并指向返回
        {
            _root = new node(field);
            return make_pair(iterator(_root), true);
        }

        /*找插入位置*/
        node* parent = nullptr; //起初根节点的父节点为nullptr
        node* cur = _root; //被插入节点指向
        while (cur)
        {
            if (compare(cur->_field) < compare(field)) //右查找:当前节点key值 < 插入节点key值
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (compare(cur->_field) > compare(field)) //左查找: 当前节点key值 > 插入节点key值
            {
                parent = cur;
                cur = cur->_left;
            }
            else //当前节点key值 = 插入节点key值:直接退出
            {
                return make_pair(iterator(cur), false);
            }
        }

        /*在对应位置插入*/
        cur = new node(field);
        node* newnode = cur;
        if (compare(parent->_field) > compare(field)) //左插入: 当前节点key值 > 插入节点key值
        {
            parent->_left = cur;
        }
        else //右插入:当前节点key值 < 插入节点key值
        {
            parent->_right = cur;
        }
        cur->_parent = parent; //反向链接

        /*调整*/
        while (parent && parent->_color == RED)
        {
            node* grandpa = parent->_parent;
            if (grandpa == nullptr)
            {
                break;
            }

            if (grandpa->_left == parent)
            {
                node* uncle = grandpa->_right;
                if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandpa->_color = RED;

                    /*继续往上调整*/
                    cur = grandpa;
                    parent = cur->_parent;
                }
                else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
                {

                    if (cur == parent->_left) //左高右低(右旋)
                    {
                        //       grandpa
                        //       /     \ 
                        //   parent   uncle
                        //     /
                        //   cur

                        rightSingleRotate(grandpa); //grandpa为轴心右旋
                        parent->_color = BLACK;
                        grandpa->_color = RED;
                    }
                    else //
                    {
                        //       grandpa
                        //       /     \ 
                        //   parent   uncle
                        //       \
                        //       cur

                        leftSingleRotate(parent); //parent为轴心左旋
                        rightSingleRotate(grandpa); //grandpa为轴心右旋
                        cur->_color = BLACK;
                        grandpa->_color = RED;
                    }

                    break;
                }
            }
            else //grandpa->_left == parent
            {
                node* uncle = grandpa->_left;
                if (uncle && uncle->_color == RED) //情况一:cur为红,parent为红,grandpa为黑,uncle存在且为红
                {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandpa->_color = RED;

                    /*继续往上调整*/
                    cur = grandpa;
                    parent = cur->_parent;
                }
                else //情况二+三:cur为红,parent为红,grandpa为黑,uncle不存在/uncle存在且为黑
                {
                    if (cur == parent->_right) //左高右低(左单旋)
                    {
                        //       grandpa
                        //       /     \ 
                        //   uncle   parent
                        //               \
                        //               cur

                        leftSingleRotate(grandpa); //grandpa为轴心左旋
                        parent->_color = BLACK;
                        grandpa->_color = RED;
                    }
                    else //cur == parent->_left
                    {
                        //       grandpa
                        //       /     \ 
                        //   uncle   parent
                        //            /
                        //           cur

                        rightSingleRotate(parent); //parent为轴心右旋
                        leftSingleRotate(grandpa); //grandpa为轴心左旋
                        cur->_color = BLACK;
                        grandpa->_color = RED;
                    }

                    break;
                }
            }
        }

        _root->_color = BLACK; //整颗树的根为黑
        return make_pair(iterator(newnode), true); //插入成功
    }

    ~red_blackTree() 
    {
        _destory(_root);
        _root = nullptr;
    }

private:
    void leftSingleRotate(node* parent) //左单旋
    {
        //记录指针
        node* parent_RightChild = parent->_right; //parent_RightChild = cur
        node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

        parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
        if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
        {
            parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
        }
        parent_RightChild->_left = parent; //让parent放在cur的左子树位置
        parent->_parent = parent_RightChild; //parent的父节点指向指向cur

        //cur(parent_RightChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_RightChild; //cur(parent_RightChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_RightChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_RightChild;
            }
            parent_RightChild->_parent = parent_parent; //cur(parent_RightChild)指向局部根的父节点
        }
    }

    void rightSingleRotate(node* parent) //右单旋
    {
        //记录指针
        node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
        node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点

        parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
        if (parent_LeftChild_RightChild != nullptr)
        {
            parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
        }
        parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
        parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur

        //cur(parent_LeftChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_LeftChild; //cur(parent_LeftChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_LeftChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_LeftChild;
            }
            parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
        }
    }

    node* _copy(node* root)
    {
        if (root == nullptr)
            return nullptr;
        node* copyTreeRoot = new node(root->_couple);
        copyTreeRoot->_left = _copy(root->_left);
        copyTreeRoot->_right = _copy(root->_right);

        return copyTreeRoot;
    }

    void _destory(node* root) //后序遍历销毁
    {
        if (root == nullptr)
            return;

        _destory(root->_left);
        _destory(root->_right);
        delete root;
    }

private:
	node* _root;
};

6.2 红黑树实现set

#pragma once
#include "red_blackTree.h"

namespace my_set
{
	template<class KEY>
	class set
	{
		struct CompareOfSet
		{
			const KEY& operator()(const KEY& key) /*仿函数作用:取值做比较*/
			{
				return key;
			}
		};

	public:
		//typedef typename red_blackTree<KEY, const KEY, CompareOfSet>::iterator iterator; /*NO*/
		typedef typename red_blackTree<KEY, KEY, CompareOfSet>::const_iterator iterator;
		typedef typename red_blackTree<KEY, KEY, CompareOfSet>::const_iterator const_iterator;

		iterator begin()
		{
			return _tree.begin();
		}

		iterator end()
		{
			return _tree.end();
		}

		pair<iterator, bool> insert(const KEY& field)
		{
			return _tree.Insert(field);
		}

	private:
		red_blackTree<KEY, KEY, CompareOfSet> _tree;
	};

	void test_set()
	{
		int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		set<int> s;
		for (auto x : arr)
		{
			s.insert(x);
		}
		
		set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

6.3 红黑树实现map

#pragma once
#include "red_blackTree.h"

namespace my_map
{
	template<class KEY, class VALUE>
	class map
	{
		struct CompareOfMap
		{
			const KEY& operator()(const pair<const KEY, VALUE>& couple) /*仿函数作用:取值做比较*/
			{
				return couple.first;
			}
		};

	public:
		typedef typename red_blackTree<KEY, pair<const KEY, VALUE>, CompareOfMap>::iterator iterator;

		iterator begin()
		{
			return _tree.begin();
		}

		iterator end()
		{
			return _tree.end();
		}

		pair<iterator, bool> insert(const pair<const KEY, VALUE >& field)
		{
			return _tree.Insert(field);
		}

		VALUE& operator[](const KEY& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, VALUE()));
			return ret.first->second;
		}

	private:
		red_blackTree<KEY, pair<const KEY, VALUE>, CompareOfMap> _tree;

	};

	void test_map1()
	{
		map<std::string, std::string> dict;
		dict.insert(make_pair("melon", "西瓜"));
		dict.insert(make_pair("strawberry", "草莓"));
		dict.insert(make_pair("pineapple", "菠萝"));
		dict.insert(make_pair("lemon", "柠檬"));
		dict.insert(make_pair("orange", "橘子"));

		map<std::string, std::string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;

		//for (auto& x : dict)
		//{
		//	cout << x.first << ":" << x.second << endl;
		//}
		//cout << endl;
	}
	
	void test_map2()
	{
		std::string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "香蕉", "苹果", "香蕉", "梨子", "梨子"};
		map<std::string, int> countMap;
		for (auto& x : arr)
		{
			countMap[x]++;
		}
			
		for (auto& x : countMap)
		{
			cout << x.first << ":" << x.second << endl;
		}
		cout << endl;
	}
}

6.4 剖析代码

6.4.1 如何复用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xwf6gGsA-1684302146488)(https://typora130.oss-cn-nanjing.aliyuncs.com/QQ截图20230516215247.png)]

6.4.2 如何区分set和map比较方式

6.4.3 set和map仿函数作用

6.4.4 怎么理解迭代器及其模板参数

6.4.5 迭代器中operator++()和operator–()

//++操作:右子树为空,回到parent->_left = cur的parent的位置,右子树不为空,走到右子树的最左节点
//--操作:相反,左子树为空,回到parent->_right = cur的parent的位置,左子树不为空,走到左子树的最右节点
_self& operator++() //中序遍历(非递归)
{
    if (_node->_right) //该节点右子树不为空,下一个节点就是该节点右子树最左节点
    {
        node* leftOfRightNode = _node->_right;
        while (leftOfRightNode->_left)
        {
            leftOfRightNode = leftOfRightNode->_left;
        }
        _node = leftOfRightNode; //当前访问指向调整
    }
    else //右子树为空
    {
        node* cur = _node;
        node* parent = cur->_parent;
        while (parent && cur == parent->_right)
        {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent; //当前访问指向调整
    }
    return *this;
}    
_self& operator--() //反中序遍历(非递归)
{
    if (_node->_left) //该节点左子树不为空,下一个节点就是该节点左子树最右节点
    {
        node* rightOfLeftNode = _node->_left;
        while (rightOfLeftNode->_right)
        {
            rightOfLeftNode = rightOfLeftNode->_right;
        }
        _node = rightOfLeftNode; //当前访问指向调整
    }
    else //左子树为空
    {
        node* cur = _node;
        node* parent = cur->_parent;
        while (parent && cur == parent->_left)
        {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent; //当前访问指向调整
    }
    return *this;
}

6.4.6 如何保证set的key值不能修改

6.4.7 如何保证map的key值不被修改value值可修改

6.4.8 怎么理解map中的operator[] ()

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

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

相关文章

如何用appuploader上架

转载&#xff1a;下载和安装appuploader IOS开发工具官网地址 Appuploader home -- A tool improve ios develop efficiency such as submit ipa to appstore and manage ios certificate 最新版本已经优化了没支付688给apple的账号登录流程&#xff0c;无需再安装其他软件。…

SDK案例配置记录

目前的极简配置 注意事项 默认的属性配置中&#xff0c;大多采用环境变量的形式&#xff0c;方便不同设备通用 比如“常规”->“输出目录”为 $(SolutionDir)..\bin\win_msvc2017$(Platform)\$(Configuration)\案例运行前的配置&#xff08;除MwBatchSimPlugin&#xff0…

如何评估用户需求的价值?关键看4方面

一、需求本身是否有价值 评估一个需求是否有价值&#xff0c;主要是通过以下几个方面&#xff1a;需求的产生源、需求产生的时机&#xff08;情景价值&#xff09;、需求如何产生&#xff08;问题解决价值&#xff09;、需求如何解决&#xff08;解决需求&#xff09;。 在情景…

web安全学习笔记【网络安全】

前言 本文简单记录一下我学习 Web 安全方面的笔记。 本文不涉及 IIS、Windows 和 SqlServer 的安全管理与配置&#xff0c;尽量只谈编程相关的安全问题。 这个 Web 安全学习路线&#xff0c;整体大概半年左右&#xff0c;具体视每个人的情况而定。 &#xff08;上传一直很模…

支持AI算法定制的4G摄像头模组解决方案

随着科技的不断发展&#xff0c;现在人们对于监控摄像头的需求越来越高&#xff0c;但是市场上的大多数摄像头组件都是标准化的&#xff0c;很难满足不同应用场景的需求。 不过&#xff0c;现在有了这款支持AI算法定制的4G摄像头模组解决方案&#xff0c;这个问题就完美的解决了…

shell脚本教程

目录 一、shell历史 二、执行脚本 三、基本语法 3.1变量 3.1.1变量的分类 3.1.2删除变量 3.2文件名代换&#xff08;Globbing&#xff09; 3.3命令代换 3.4算术代换 3.5转义字符 3.6单引号 3.7双引号 四、Shell脚本语法 4.1条件测试 4.2分支 4.2.1if/then/elif…

重磅!用友荣登全球5强

近日&#xff0c;全球权威信息技术研究和顾问公司Gartner发布《Market Share: All Software Markets, Worldwide&#xff0c;2022》报告&#xff0c;用友在EAM&#xff08;资产管理&#xff09;市场再创新高&#xff0c;市场占有率位居全球第五位&#xff0c;亚太第一位&#x…

quic详解

目录 一 http简介 二 quic协议详解 1 重传机制 2 流量控制 3连接迁移 一 http简介 http0.9 1991年发布&#xff0c; 没有header&#xff0c;功能非常简单&#xff0c;只支持GET http1.0 1996年发布&#xff0c;明文传输安全性差&#xff0c;header特别大。它相对0.9有以…

postgres_exporter 部署

目录 - 下载地址- 配置环境变量- 启动vim ./start.shvim ./stop.shqueries.yaml - 配置prometheus - 下载地址 https://github.com/prometheus-community/postgres_exporter/releases - 配置环境变量 - 启动 因启动前需要配置环境变量后再进行启动 运行如下语句&#xff1a…

【自动化测试入门知识】自动化测试生命周期

如今&#xff0c;项目经理和开发人员面临着用最少的资源并在日渐缩减的时间表中构建可靠应用程序的挑战。因此&#xff0c;组织正在转向自动化测试以有效地实现此目标。 在自动化测试方面&#xff0c;我们许多人认为这只是SDLC&#xff08;软件开发生命周期&#xff09;的一部…

【竣达】浅谈电视台机房智能化动力环境监控系统管理

电视台作为面向全国甚至是世界的广播平台&#xff0c;在节目转播过程中不能有任何的闪失&#xff0c;为了最大限度保障电视节目的安全播出&#xff0c;一套完整的机房动力环境监控系统必不可少。 电视台机房及广播控制室建立包括机房动力、环境及安防的监控系统&#xff0c;主…

30岁女程序媛,对职业和未来的迷茫和焦虑

前言 2023快过去一半马上就要2024年了&#xff0c;92年的我就要步入30的行列了。 一转眼&#xff0c;也到了三十而立的年纪。 反观自己&#xff0c;到了这个时候&#xff0c;更多的是迷茫、彷徨、焦虑、无措 但也在不断地跌跌撞撞中&#xff0c;找到了自己的节奏和目标。 …

程序员一个月拿两万,得知卖猪肉可以赚五万,你是选择做程序员还是卖猪肉?

在知乎上看到这么个帖子&#xff0c;觉得挺有意思&#xff0c;大家一起瞧瞧&#xff1f; 对此&#xff0c;我也看到了许多犀利的回答哈 **A&#xff1a;**我反过来问你&#xff0c;如果一对夫妇卖猪肉一个月只能挣一万&#xff0c;听说一名程序员一个月拿五万&#xff0c;他们…

【轻量化网络系列(1)】MobileNetV1论文超详细解读(翻译 +学习笔记+代码实现)

前言 这几天学了一下轻量化网络&#xff0c;我们先从MobileNetV1讲起吧~ MobileNetV1网络是谷歌团队在2017年提出的&#xff0c;专注于移动端和嵌入设备的轻量级CNN网络&#xff0c;相比于传统的神经网络&#xff0c;在准确率小幅度降低的前提下大大减少模型的参数与运算量。…

机器学习 day13(正则化,线性回归的正则化,逻辑回归的正则化)

正则化的思想 如果特征的参数值更小&#xff0c;那么对模型有影响的特征就越少&#xff0c;模型就越简单&#xff0c;因此就不太容易过拟合 如上图所示&#xff0c;成本函数中有W₃和W₄&#xff0c;且他们的系数很大&#xff0c;要想让该成本函数达到最小值&#xff0c;就得使…

【软件测试】未来软件测试必备的八大技能!你缺少哪个?

软件测试工程师是个神奇的职业&#xff0c;他是开发人员与老板之间的传话筒&#xff08;三夹板&#xff09;&#xff0c;也是开发人员与老板的好帮手&#xff1b; 他不仅需要有销售的沟通能力&#xff0c;也需要具备编辑人员的文档撰写技巧。如此一个面面俱到的岗位&#xff0…

软件设计师--易错题集结

计算机组成与结构 海明校验码是在n个数据位之外增选择题设k个校验位&#xff0c;从而形成一个kn位的新的码字&#xff0c;使新的码字的码距比较均匀地拉大。n与k的关系是&#xff08;A&#xff09;。 A、2k-1≥nk B、2n-1≤nk C、nk D、n-1≤k 知识&#xff1a; 确定要传输的信…

小白眼中的卷积神经网络(CNN)

相信和笔者一样爱技术对AI兴趣浓厚的小伙伴们&#xff0c;一定对卷积神经网络并不陌生&#xff0c;也一定曾经对如此“高级”的名字困惑良久。今天让我们一起回顾/学习这玩意到底是什么和为什么呢。 引言 说起CNN&#xff0c;必然会首先想到的是CV吧&#xff0c;而边缘检测可…

电动车防盗器语音芯片方案——NV020C 直推0.5w喇叭

电动车已经成为了越来越多个人的出行交通便利公交&#xff0c;近几年来各式各样的电动车也在不断更新中&#xff0c;功能也逐渐变多。电动车防盗装置也在逐渐更新换代中&#xff0c;其中电动车电池盒成为有些人窥测的重点对象。部分电动车的电池盒锁结构简单&#xff0c;很容易…

FiftyOne 系列教程(2)使用FiftyOne读取数据集

1. 支持的数据集 1.1. 支持各种常见的数据集格式 docs.voxel51.com/user guide/dataset creation/datasets.html#supported import formats此外&#xff0c;zoo上面有什么数据集&#xff0c;这里就可以加载到对应的数据集Available Zoo Datasets — FiftyOne 0.20.1 document…