C++之AVL树

news2024/9/24 1:16:22

目录

  • 一.介绍
  • 二.简单实现AVL树
    • 1. 基本框架
    • 2. 插入结点(Insert)
      • a. 更新平衡因子
      • b. 左单旋
      • c. 右单旋
      • d. 左右双旋
      • e. 右左双旋
    • 3. 删除节点(Erase)
      • a. 更新平衡因子
      • b. 旋转
      • c. 代码
    • 4. 测试

一.介绍

作为对二叉搜索树的优化版本。AVL树是由俄罗斯的两位数学家G.M.Adelson-Velskii和E.M.Landis发明的,并以名字的首字母命名。

AVL树(可以为空树),在二叉搜索树的基础上具有以下性质:

  • 它的左右子树都是AVL树

  • 左右子树高度差(简称平衡因子)的绝对值不超过1(-1/0/1)

    平衡因子 = 右子树高度 - 左子树高度
    在这里插入图片描述

示例三图,都符合AVL树的规则:在二叉搜索树的继承上,每个节点的平衡因子都是-1、0、1

平衡因子 = 右子树高度 - 左子树高度

有N个结点的AVL树,,其高度 H ≈ l o g 2 N H \approx log_2N Hlog2N,则其查找的时间复杂度可以控制在 O ( l o g 2 N ) O(log_2N) O(log2N)

二.简单实现AVL树

1. 基本框架

template<class K>
struct AVLTreeNode
{
	AVLTreeNode<K>* _left;
	AVLTreeNode<K>* _right;
	AVLTreeNode<K>* _parent;

	K _key;
	int _bf;  // balance factor

	AVLTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _key(key)
		, _bf(0) //新节点平衡因子为0
	{}
};

template<class K>
struct AVLTree
{
	typedef AVLTreeNode<K> Node;
private:
	Node* _root = nullptr;
};

在这里插入图片描述

_root:指向AVL树的根节点

使用三叉链的结构(_parent),是为了后续的操作。

2. 插入结点(Insert)

由于AVL树的节点插入,可能会引起子树高度的变化,从而改变平衡因子,导致其不再符合AVL树的规则。

步骤:

  1. 按照搜索二叉树进行插入新节点
  2. 更新AVL树节点的平衡因子,当不再平衡时,进行旋转调整平衡

a. 更新平衡因子

平衡因子更新规则:

子树的高度改变,则需要更新其父节点的平衡因子;

直到更新到根节点或左右子树平衡(_bf=0)或者违反规则( _bf=2/-2)停止。

  • 新节点在父节点的左边,父节点的平衡因子–

  • 新节点在父节点的右边,父节点的平衡因子++
    在这里插入图片描述

节点 _bf(平衡因子)更改后的操作:

平衡因子_bf操作解析
1或-1向其父节点更新_bf子树的高度改变
0停止更新左右子树高度相等(平衡)
2或-2停止更新,继续旋转违反规则,需要进行调整
bool Insert(const K& key)
{
    // 插入第一个节点
    if (nullptr == _root)
    {
        _root = new Node(key);
        return true;
    }

    // 插入新节点
    Node* cur = _root;
    Node* parent = nullptr;
    //寻找新节点位置(即cur到空时)
    while (cur)
    {
        parent = cur;
        if (cur->_key > key) // 在左子树
        {				
            cur = cur->_left;
        }
        else if (cur->_key < key) // 在右子树
        {				
            cur = cur->_right;
        }
        else // 已存在,退出
        {
            return false;
        }
    }


    cur = new Node(key);

    if (parent->_key < key)//新节点的值大于parent
        parent->_right = cur;//放在parent的右边
    else 
        parent->_left = cur;//左边

    cur->_parent = parent;

    //更新平衡因子
    while (cur != _root) // 最坏情况下更新到根节点
    {
        if (parent->_left == cur) //cur在parent左边,平衡因子--
            parent->_bf--;
        else                      //cur在parent右边,平衡因子++
            parent->_bf++;

        if (parent->_bf == 0)  // 左右平衡,停止更新
        {
            break;
        }
        else if (parent->_bf == 1 || parent->_bf == -1) // 继续往上更新
        {
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -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)
            {
                RotateRL(parent); //右左双旋
            }
            else if (parent->_bf == -2 && cur->_bf == 1)
            {
                RotateLR(parent); //左右双旋
            }
            break;
        }
        else
        {
            assert(false);//出现问题,终止程序
        }
    }
    return true;
}

旋转的情况详情在下面


对于节点的 _bf=2或-2 时,旋转的情况大致分为以下4种

b. 左单旋

在这里插入图片描述

构成左单旋的条件是:parent=2 && subR=1

左旋:即节点11与节点22的结构 逆时针(向左)旋转了一下

步骤:

  1. 让subRL成为parent的右子树
  2. 将parent作为subR的左子树
  3. subR成为根(子树/树)
  4. 更新parent与subR的平衡因子
void RotateL(Node* parent)
{
    Node* pParent = parent->_parent;//parent的父节点
    Node* subR = parent->_right;//parent的右孩子
    Node* subRL = subR->_left;//subR的左孩子

    //让subRL成为parent的右子树
    parent->_right = subRL;
    if (subRL)//如果subRL不为空,更改其父指针
    {
        subRL->_parent = parent;
    }

    //将parent作为subR的左子树
    subR->_left = parent;
    parent->_parent = subR;

    //subR成为根(树/子树)
    if (_root == parent)//parent为根节点
    {
        _root = subR;
        _root->_parent = nullptr;
    }
    else //parent为一棵子树
    {
        //将subR链接到pParent上
        if (pParent->_right == parent)
            pParent->_right = subR;
        else
            pParent->_left = subR;

        subR->_parent = pParent;
    }

    // 更新parent与subR的平衡因子
    parent->_bf = subR->_bf = 0;
}

c. 右单旋

在这里插入图片描述

产生左单旋的条件是:parent=-2 && subL=-1

右旋:即节点23与节点20的结构 顺时针(向右)旋转了一下

步骤:

  1. 让subLR成为parent的左子树
  2. 将parent作为subL的右子树
  3. subL成为根(子树/树)
  4. 更新parent与subL的平衡因子
void RotateR(Node* parent)
{
    Node* pParent = parent->_parent;//parent的父节点
    Node* subL = parent->_left;//parent的左孩子
    Node* subLR = subL->_right;//subL的右孩子

    //让subLR成为parent的左子树
    parent->_left = subLR;
    if (subLR)//如果subLR不为空,更改其父指针
    {
        subLR->_parent = parent;
    }

    // 将parent作为subL的右子树
    subL->_right = parent;
    parent->_parent = subL;

    // subL成为根(子树/树)
    if (parent == _root)//parent为根节点
    {
        _root = subL;
        _root->_parent = nullptr;
    }
    else  //parent为一棵子树
    {
        //将subR链接到pParent上
        if (pParent->_right == parent)
            pParent->_right = subL;
        else
            pParent->_left = subL;

        subL->_parent = pParent;
    }

    // 更新parent与subL的平衡因子
    subL->_bf = parent->_bf = 0;
}

d. 左右双旋

在这里插入图片描述

由图左右双旋分为三种情况

在这里插入图片描述

可以发现插入位置不同,subLR->_bf=-1/1/0不同,导致右左旋转后平衡因子不同。

步骤:

  1. 将subL为根的子树左旋
  2. 将parent为根的子树右旋
  3. 更新parent、subL和subLR的平衡因子
void RotateLR(Node* parent)
{
    Node* subL = parent->_left;//parent的左孩子
    Node* subLR = subL->_right;//subL的右孩子

    int bf = subLR->_bf; // 旋转前subLR的平衡因子

    RotateL(subL);//左旋
    RotateR(parent);//右旋

    // 更新平衡因子
    if (bf == -1)
    {
        parent->_bf = 1;			
        subL->_bf = 0;
        subLR->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = 0;			
        subL->_bf = -1;
        subLR->_bf = 0;
    }
    else if (bf == 0)
    {
        parent->_bf = 0;
        subL->_bf = 0;
        subLR->_bf = 0;
    }
    else
        assert(false); //错误
}

e. 右左双旋

在这里插入图片描述

由图右左双旋分为三种情况,触发条件为parent=2 && subR=-1

在这里插入图片描述

可以发现插入位置不同,subRL->_bf=-1/1/0不同,导致右左旋转后平衡因子不同。

步骤:

  1. 将subL为根的子树左旋
  2. 将parent为根的子树右旋
  3. 更新parent、subR和subRL的平衡因子
void RotateRL(Node* parent)
{
    Node* subR = parent->_right;//parent的右孩子
    Node* subRL = subR->_left;//subR的左孩子

    int bf = subRL->_bf; //旋转前subRL的平衡因子

    RotateR(subR);//右旋
    RotateL(parent);//左旋

    // 更新三种情况的平衡因子
    if (bf == -1)
    {
        parent->_bf = 0;
        subR->_bf = 1;
        subRL->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = -1;
        subR->_bf = 0;			
        subRL->_bf = 0;
    }
    else if (bf == 0)
    {
        parent->_bf = 0;
        subR->_bf = 0;			
        subRL->_bf = 0;
    }
    else
        assert(false);//错误
}

3. 删除节点(Erase)

和插入类似,删除节也可能会引起子树高度的变化,从而改变平衡因子,导致其不再符合AVL树的规则。

步骤:

  1. 按照搜索二叉的操作删除节点
  2. 更新AVL树节点的平衡因子,当不再平衡时,进行旋转调整平衡

a. 更新平衡因子

平衡因子更新规则:

子树的高度改变,则需要更新其父节点的平衡因子;

直到更新到根节点或左右子树平衡(_bf=0)或者违反规则( _bf=2/-2)停止。

  • 删除节点在父节点的左边,父节点的平衡因子++
  • 删除节点在父节点的右边,父节点的平衡因子–
    在这里插入图片描述

节点 _bf(平衡因子)更改后的操作:

平衡因子_bf操作解析
1或-1停止更新删除前_bf=0,其左右子树高度H相等。删除后,该节点为根的子树高度为H+1不变
0向其父节点更新_bf删除前_bf=-1/1,左子树高或右子树高。删除后左右子树高度一样,即该节点为根的子树高度变小(减1)
2或-2停止更新,继续旋转违反规则,需要进行调整

b. 旋转

对于节点的 _bf=2或-2 时,旋转的情况大致分为以下6种

在这里插入图片描述

1)parent的_bf为 2 , subR的 _bf为 1----》左旋

2)parent的_bf为 -2 , subL的 _bf为 -1----》右旋

3)parent的_bf为 2 , subR的 _bf为 0----》左旋

4)parent的_bf为 -2 , subL的 _bf为 0----》右旋

5)parent的_bf为 -2 , subL的 _bf为 -1----》左右双旋

6)parent的_bf为 2 , subR的 _bf为 -1----》右左双旋

在这里插入图片描述

3)左单旋后,需更新平衡因子parent=1、subR=-1

4)右单旋后,需更新平衡因子parent=-1、subL=1

除3)和4)外,其他4种旋转和插入的旋转相同。3)和4)旋转后新子树根的_bf=-1/1,停止更新;但是其他4种情况中,旋转后新子树根的 _bf=0,因此旋转后任需向上更新。

c. 代码

bool Erase(const K& key)
{
    if (_root == nullptr)// 空树,退出
        return false;

    Node* cur = _root;//当前节点
    Node* parent = nullptr;//当前节点的父节点

    Node* del = nullptr;    // 要删除的节点
    Node* pDel = nullptr; // 要删除节点的父节点

    //查找要删除的点
    while (cur)
    {
        if (cur->_key < key)
        {
            parent = cur;
            cur = cur->_right;
        }
        else if (cur->_key > key)
        {
            parent = cur;
            cur = cur->_left;
        }
        else // 找到
        {

            if (cur->_left == nullptr) //所删除节点的左子树为空
            {
                if (cur == _root) //如果是删除根节点
                {
                    _root = cur->_right;

                    if (_root)//如果cur->_right不为空
                        _root->_parent = nullptr;

                    delete cur;
                    return true;
                }
                else
                {
                    // 记录要删除的节点和其父节点
                    del = cur;
                    pDel = parent;
                }
            }
            else if (cur->_right == nullptr) // 待删除节点的右子树为空
            {
                if (cur == _root) //如果是删除根节点
                {
                    _root = cur->_left;

                    if (_root)//如果cur->_left不为空
                        _root->_parent = nullptr;

                    delete cur;
                    return true;
                }
                else
                {
                    // 记录要删除的节点和其父节点
                    del = cur;
                    pDel = parent;
                }
            }
            else //左右都不为空
            {
                // 找到待删除节点右子树中的最左节点进行替换删除
                Node* minRight = cur->_right;//右子树的最小值节点
                Node* minParent = cur;//minRight的父节点

                while (minRight->_left)//即右子树的最左节点
                {
                    minParent = minRight;
                    minRight = minRight->_left;
                }

                // 交换值
                std::swap(cur->_key, minRight->_key);

                // 记录要删除的节点和其父节点
                del = minRight;
                pDel = minParent;
            }
            break;
        }
    }

    // 遍历结束,没有找到待删除节点
    if (cur == nullptr)
        return false;

    //更新平衡因子
    cur = del;
    parent = pDel;

    while (cur != _root)// 最坏情况下更新到根节点
    {
        if (cur == parent->_left)//cur在parent左边,平衡因子++
        {
            parent->_bf++;
        }
        else if (cur == parent->_right)//cur在parent右边,平衡因子--
        {
            parent->_bf--;
        }

        if (parent->_bf == 0)//继续向上更新
        {
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 1 || parent->_bf == -1)
        {
            break;//停止更新
        }
        else if (parent->_bf == 2 || parent->_bf == -2)//旋转
        {
            if (parent->_bf == 2 && parent->_right->_bf == 1)
            {
                Node* tmp = parent->_right; //记录左单旋转后的根节点
                RotateL(parent);//左单旋
                parent = tmp; // 更新parent
            }
            else if (parent->_bf == -2 && parent->_left->_bf == -1)
            {
                Node* tmp = parent->_left;//记录右单旋转后的根节点
                RotateR(parent);//右单旋
                parent = tmp;
            }
            else if (parent->_bf == 2 && parent->_right->_bf == 0)
            {
                Node* tmp = parent->_right; //记录左单旋转后的根节点
                RotateL(parent);//左单旋
                parent = tmp;

                //更新平衡因子
                parent->_bf = -1;
                parent->_left->_bf = 1;

                break;//停止更新
            }
            else if (parent->_bf == -2 && parent->_left->_bf == 0)
            {
                Node* tmp = parent->_left;//记录右单旋转后的根节点
                RotateR(parent);//右单旋
                parent = tmp;

                //更新平衡因子
                parent->_bf = 1;
                parent->_right->_bf = -1;

                break;//停止更新
            }
            else if (parent->_bf == -2 && parent->_left->_bf == 1)
            {
                Node* tmp = parent->_left->_right;//记录左右双旋转后的根节点
                RotateLR(parent);//左右双旋
                parent = tmp;
            }
            else if (parent->_bf == 2 && parent->_right->_bf == -1)
            {
                Node* tmp = parent->_right->_left;//记录右左双旋转后的根节点
                RotateRL(parent);//右左双旋转
                parent = tmp;
            }
            else
            {					
                assert(false);// 出现错误
            }
            //继续向上更新
            cur = parent;
            parent = parent->_parent;
        }
    }

    if (del->_left == nullptr)//所删除节点的左子树为空
    {
        //将cur的右子树链接到父节点
        if (del == pDel->_left)
        {
            pDel->_left = del->_right;
            if (del->_right)
                del->_right->_parent = pDel;
        }
        else
        {
            pDel->_right = del->_right;
            if (del->_right)
                del->_right->_parent = pDel;
        }
    }
    else// 待删除节点的右子树为空
    {
        //将cur的左子树链接到父节点
        if (del == pDel->_left)
        {
            pDel->_left = del->_left;
            if (del->_left)
                del->_left->_parent = pDel;
        }
        else
        {
            pDel->_right = del->_left;
            if (del->_left)
                del->_left->_parent = pDel;
        }
    }

    delete del;// 删除节点
    return true;
}

4. 测试

为了验证上述的插入(Insert)、删除(Erase)函数编写的正确性,可以检查操作后的AVL树是否还符合规则。

//是否平衡yes:1,no:0
bool IsBalance()
{
    return _IsBalance(_root);
}
bool _IsBalance(Node* root)
{
    if (root == nullptr)
    {
        return true;//空树是AVL树
    }

    int leftHT = Height(root->_left);
    int rightHT = Height(root->_right);

    //平衡因子=右子树高度-左子树高度
    int bf = rightHT - leftHT;

    if (bf != root->_bf)//如果不同,则报错
    {
        cout << root->_key << "平衡因子异常" << endl;
        return false;
    }

    return abs(bf) < 2		//平衡因子的绝对值小于2
        && _IsBalance(root->_left)	//左右子树也为AVL树
        && _IsBalance(root->_right);
}

int Height(Node* root)//求树高
{
    if (root == nullptr)
        return 0;

    return max(Height(root->_left), Height(root->_right)) + 1;
}

示例:

void TestAVLTree3()
{
	size_t N = 10000;
	srand(time(0));
	AVLTree<int> t1;

	//插入N次随机数
	int count = 0;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		if(t1.Insert(x)) ++count;
	}
	cout << "插入成功次数:" << count << endl;

	//删除N次随机数
	count = 0;
	for (int i = 0; i < N; ++i)
	{
		int x = rand();
		if (t1.Erase(x)) ++count;
	}
	cout << "删除成功次数:" << count << endl;

	//是否仍是AVL树
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

int main()
{
	TestAVLTree();
	return 0;
}

在这里插入图片描述


🦀🦀观看~

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

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

相关文章

Python 3 基本语法与基本数据类型 | 菜鸟教程(二)

目录 一、Python3 基础语法 &#xff08;一&#xff09;编码 &#xff08;二&#xff09;标识符 &#xff08;三&#xff09;python保留字 &#xff08;四&#xff09;注释 ​&#xff08;五&#xff09;行与缩进 &#xff08;六&#xff09;多行语句 &#xff08;七&am…

操作系统复习笔记3

1、条件变量和互斥锁 条件变量一般和互斥锁一起使用&#xff0c;来弥补互斥锁的不足。总得来说&#xff0c;互斥锁用来规范线程对共享数据的竞争使用&#xff0c;条件变量用来协调各个线程合作完成任务。 2、enum枚举类型 enum typeName { valueName1, valueName2, valueName3…

Workerman在线客服系统源码 附搭建文档

Workerman在线客服系统源码 模块化开发 强大的一键生成功能极速简化你的开发流程&#xff0c;加快你的项目开发 响应式布局 自动适配&#xff0c;无需要担心兼容性问题 完善的权限管理 自由分配子级权限、一个管理员司同时属于多个组别 通用的会员和API模块 共用同一账…

【MySQL多表查询】:让你的数据检索更高效

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL中多表查询相关知识的讲解 目录 前言一、多表关系二、多表查询1、交叉连接2、内连接3、外连接 三、集合运算四、七种JOINS实现五、多表查询练习六、总结 一、多表关系 ✨项目开发中&#xff0c;在进行数据库表结…

6月人工智能论文推荐

Prompt Space Optimizing Few-shot Reasoning Success with Large Language Models https://arxiv.org/abs/2306.03799 Prompt engineering 是通过提供明确和具体的指令来增强大型语言模型(llm)能力的基本技术。它使LLM能够在各种任务中脱颖而出&#xff0c;例如算术推理、问…

列表、表格、表单

day02&#xff1a;列表、表格、表单 目标&#xff1a;掌握嵌套关系标签的写法&#xff0c;使用列表标签布局网页 01-列表 作用&#xff1a;布局内容排列整齐的区域。 列表分类&#xff1a;无序列表、有序列表、定义列表。 无序列表 作用&#xff1a;布局排列整齐的不需要规…

Bean 的生命周期

观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8) 电脑使用的操作系统版本为 Windows 10 目录 Spring 的执行流程 1. 启动容器 2. 根据配置完成 Bean 的初始化 3. 注册 Bean 对象到容器中 4. 装配 Bean 的属性 Bean 的生命周期 Bean 的…

掌握Python的X篇_2_Python的安装

掌握Python的X篇_2_Python的安装 1. 软件安装包下载1.1 Python版本的区别1.2 同一版本不同安装包的区别 2. 安装过程3. 验证是否安装成功 1. 软件安装包下载 下载地址&#xff1a;https://www.python.org/downloads/ 可以看到最新版本和历史版本 1.1 Python版本的区别 就像…

express的使用(五) 简单的使用mongodb

原文链接 express的使用(五) 简单的使用mongodb 不要脸的求关注&#xff0c;希望能让大家批评我的不足点&#xff0c;一键三连最好了 看前提示 本篇主要描述关于express中使用mongodb以及mongodb的简单介绍还有初步使用&#xff0c;不涉及任何常用的语法&#xff0c;不建议有…

算法刷题-双指针-反转链表

反转链表的写法很简单&#xff0c;一些同学甚至可以背下来但过一阵就忘了该咋写&#xff0c;主要是因为没有理解真正的反转过程。 206.反转链表 力扣题目链接 题意&#xff1a;反转一个单链表。 示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2-…

AMD CPU微架构分析

一、SoC架构 1.1 整体架构 Zeppelin 参考链接&#xff1a;wikichip: Zeppelin 通过infinity fabric总线将单die分成多die的SoC架构&#xff0c;每个Die包含两个CPU核&#xff08;CCX&#xff09;、2各DDR通道、USB、低功耗IO以及多个IFOP和IFIS serdes接口。 如下所述中&…

干翻Mybatis源码系列之第十篇:Mybatis Plugins基本概念与拦截概述

给自己的每日一句 不从恶人的计谋&#xff0c;不站罪人的道路&#xff0c;不坐亵慢人的座位&#xff0c;惟喜爱耶和华的律法&#xff0c;昼夜思想&#xff0c;这人便为有福&#xff01;他要像一棵树栽在溪水旁&#xff0c;按时候结果子&#xff0c;叶子也不枯干。凡他所做的尽…

vue jointjs 拓扑图 自定义shape 按需引入

只展示部分核心代码&#xff0c;完整代码见文章尾部连接 import jointjs/dist/joint.core.css;// 类库 包含: Paper Graph Cell CellView Element Link 等等 import { dia } from jointjs/src/core.mjs; // 样式库 包含多个分组(basic standard custom ...) import * as standa…

基于Hexo和Butterfly创建个人技术博客,(11) 使用插件增强博客站点能力

Butterfly官方网站&#xff0c;请 点击进入 本章目标&#xff1a; 掌握常用的plugin插件的用法&#xff0c;本文中是butterfly主题内置集成的第三方插件(部分插件需要再次安装)&#xff1b; 一、建议开启的三方插件 KaTeX-数学公式 katex:enable: true# true 表示每一页都加载…

C# NX二次开发:通过UFUN函数获取刀具描述,目录号,库号等信息

今天要将的是&#xff0c;在NX中对CAM模块进行二次开发的时候&#xff0c;往往需要获取一些关于刀具使用的信息&#xff0c;这些信息用NXOPEN的的方法录制也可以录制出来&#xff0c;但是录制出来的代码&#xff0c;往往都是一种刀具类型会出现一个Builder。这样在你不知道有多…

淘宝买家订单API

目录 下载安装与运行 支持的订单读取方式 请求数据格式一 请求头示例 数据格式说明 数据格式示例 返回数据格式一 返回头示例 数据格式说明 数据格式示例 请求数据格式二&#xff08;根据订单编号&#xff09; 请求头示例 返回数据格式二&#xff08;根据订单编号…

【随笔记】如何获得铁粉(仅供参考)

文章目录 一、前言二、秘籍2.1 良好的个人简介2.2 统一的文章格式2.3 详细的专栏划分2.4 有序的博客排版2.4.1 目录部分2.4.2 正文部分2.4.2.1 标题分级2.4.2.2 正文分段2.4.2.3 善用多级列表2.4.2.4 章节分割 三、总结 一、前言 在这篇随笔记的开始&#xff0c;我想声明一下&a…

ChatGPT将改变教育,而不是摧毁它

01 学校和大学的反应迅速而果断 就在 OpenAI 于 2022 年 11月下旬发布ChatGPT 的几天后&#xff0c;该聊天机器人被广泛谴责为一种免费的论文写作、应试工具&#xff0c;它很容易在作业中作弊。 美国第二大学区洛杉矶联合大学立即阻止了OpenAI网站从其学校网络访问。其他人很…

【Java】Java核心要点总结 67

文章目录 1. 浮点数运运算会有精度损失2. 构造方法特点 & 不能被重写3. 接口和抽象类的异同4. Object 类的常见方法5. hashCode() 有什么用 为什么要有 hashCode() 1. 浮点数运运算会有精度损失 这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的&#x…

面试时一定要确认该岗位的直属领导是否在场,如果不在,千万不要接offer,有坑!...

对于有心人来说&#xff0c;面试时可以看出许多隐形信息&#xff0c;比如下面这位网友的提醒&#xff1a; 面试时一定要确认这个岗位的直接汇报领导是否参与了面试&#xff0c;如果没有参与&#xff0c;千万不要接offer&#xff01; 该职位的直接领导不参与面试&#xff0c;只能…