【数据结构高阶】AVL树

上期博客我们讲解了set/multiset/map/multimap的使用,下面我们来深入到底层,讲解其内部结构:

目录

一、AVL树的概念

二、AVL树的实现

2.1 节点的定义

2.2 数据的插入

2.2.1 平衡因子的调整

2.2.1.1 调整平衡因子的规律

2.2.2 子树的旋转调整

2.2.2.1 左单旋

2.2.2.2 右单旋

2.2.2.3 左右双旋

2.2.2.4 右左双旋

2.3 AVL树的检查验证

2.4 测试代码

三、AVL树实现的完整代码


一、AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年,发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

● 它的左右子树都是AVL树

● 左右子树高度之差(简称平衡因子,不是每种AVL树都有平衡因子,平衡因子只是AVL树实现的一种方式)的绝对值不超过1(下图的平衡因子计算公式为:节点的右子树高度-左子树高度)

下图是一个AVL树:

二、AVL树的实现

2.1 节点的定义

这次我们直接实现K-V模型的二叉树:

template<class Key,class Val>
class AVLTreeNode
{
public:AVLTreeNode<Key, Val>* _left;AVLTreeNode<Key, Val>* _right;AVLTreeNode<Key, Val>* _parent;//多一个指针指向其父节点,方便我们的后续操作pair<Key, Val> _kv;int _bf;//balance factor(平衡因子)AVLTreeNode(pair<Key, Val> kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

2.2 数据的插入

AVL树的数据插入需要遵循二叉搜索树的规律:

template<class Key, class Val>
class AVLTree
{
public:typedef AVLTreeNode<Key, Val> Node;bool Insert(const pair<Key,Val>& kv){Node* cur = _root, * parent = nullptr;while (cur)//找到合适的位置{if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{cout << "插入的值重复" << endl;return false;}}cur = new Node(kv);cur->_parent = parent;//将插入的节点连接上二叉树if (parent == nullptr){_root = cur;}else if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}return true;}private:AVLTreeNode<Key, Val>* _root = nullptr;
};

但是成功插入数据后就结束了吗?在AVL树中可没有这么简单,我们需要更新其插入节点的父节点的平衡因子,来保证这课树的整体结构还是一个AVL树。

2.2.1 平衡因子的调整

下面我们来讨论一下更新父节点平衡因子的操作:

我们看到上面的二叉树,现在向其中插入一个键值为8的数据:

插入后我们发现其插入节点的父节点平衡因子需要修改,那就要修改一下:

修改其父节点后,我们发现其父节点的父节点的平衡因子也需要调整,那再调整一下吧:

但是本次调整过后,平衡因子出现了2这个数值,但是平衡因子只能是1/0/-1,说明这时该节点下的子树已经不满足AVL树的结构了,这时要对其子树进行旋转来调整该树的结构(至于怎么旋转我们在下面会详细讲解),经过旋转过后子树肯定是满足AVL树的结构的,所以就并不再检查进行旋转的节点父节点平衡因子了:

那我们再看看另一种情况,我们向下面的二叉树中插入键值为6的数据:

插入后,我们发现需要修改其父节点的平衡因子: 

修改后,我们发现其父节点的平衡因子为0,为0时就意味着子树的高度没有发生变化,我们就没有必要再向上更新其父节点的平衡因子了: 

2.2.1.1 调整平衡因子的规律

所以总结一下上述规律,我们插入节点后肯定是要调整其父节点的平衡因子的,那调整父节点的平衡因子后,什么时候要向上调整其父节点的父节点(爷爷节点)的平衡因子呢?

这里有三种情况:

当父节点的平衡因子调整过后为1或-1时,此时说明其节点所在的子树的高度发生了变化(变为1或-1前,平衡因子只可能是0,说明插入之前子树两边的高度是相同的,这时在插入一个节点会导致其中一颗子树的高度发生变化),这时需要再继续向上调整其父节点的平衡因子

当父节点的平衡因子调整过后为2或-2时,该节点的子树不平衡,需要处理这颗旋转处理子树,旋转实质是是将该节点所在的子树的高度降1,所以旋转过后不影响子树的高度变化,此时就不需要再向上更新其父节点的平衡因子了

当父节点的平衡因子调整过后为0时(变为0前,平衡因子只可能是1或-1,说明插入之前一边高,一边低,但是插入节点是在矮的那边,其子树的高度不变),说明所在的子树高度不变,不用继续向上更新

我们用代码来实现一下平衡因子的调整:

template<class Key, class Val>
class AVLTree
{
public:typedef AVLTreeNode<Key, Val> Node;bool Insert(const pair<Key,Val>& kv){Node* cur = _root, * parent = nullptr;while (cur)//找到合适的位置{if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{cout << "插入的值重复" << endl;return false;}}cur = new Node(kv);cur->_parent = parent;//将插入的节点连接上二叉树if (parent == nullptr){_root = cur;}else if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}//向上调整各节点的平衡因子while (parent){if (parent->_left == cur){--parent->_bf;//更新平衡因子}else{++parent->_bf;//更新平衡因子}//根据父节点的平衡因子的情况决定师傅继续向上调整各节点的平衡因子if (parent->_bf == 1 || parent->_bf == -1)//平衡因子为1或1继续向上更新{parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//平衡因子为2或-2,树的结构不平衡,旋转parent节点所在子树{//旋转parent所在的子树}else if (parent->_bf == 0)//平衡因子为0,插入结束{break;}else//出现其他的情况说明树的根据有问题,报错退出{cout << "树的结构出错了" << endl;assert(0);exit(-1);}}return true;}private:AVLTreeNode<Key, Val>* _root = nullptr;
};

2.2.2 子树的旋转调整

当某个节点的平衡因子变为2或-2时,我们需要对其所在的子树进行旋转调整

下面我们分情况来讨论:

2.2.2.1 左单旋

下面的图代表的是一棵AVL树,其中52和60表示的两个节点,A、B、C分别为高度为h的AVL子树:

下面向C子树中插入数据使其高度发生变化:

现在键值为52的节点平衡因子变为2,下面我们要将这棵子树旋转:

我们可以看到旋转的过程是这样的:

B子树变成52的右子树->52变成60的左子树->60变成整颗树的根

上面这种需要旋转的树的根节点平衡因子为2,并且其根节点右孩子节点的平衡因子为1;这种情况下的旋转我们将其称为左单旋

下面我们就用代码实现一下左单旋:

void RotateL(Node* parent)//左单旋
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;//更新parent的右节点if (subRL)//防止该节点为空{subRL->_parent = parent;//更新subRL的父节点}parent->_parent = subR;//更新parent的父节点subR->_left = parent;//subR的左子树置为parentsubR->_parent = pparent;//更新subR的父节点if (pparent == nullptr)//旋转的是整棵树{_root = subR;//更新根节点}else//将旋转后的子树链接上整个二叉树{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}}subR->_bf = parent->_bf = 0;//更新平衡因子
}

下图是画出了代码所表示的节点: 

2.2.2.2 右单旋

再来看到另一种情况:

 下面向子树A中插入数据使其高度发生变化:

现在键值为60的节点平衡因子变为-2,下面我们要将这棵子树旋转: 

我们可以看到旋转的过程是这样的:

B子树变成60的左子树->60变成52的右子树->52变成整颗树的根

上面这种需要旋转的树的根节点平衡因子为-2,并且其根节点左孩子节点的平衡因子为-1;这种情况下的旋转我们将其称为右单旋

下面我们用代码实现一下右单旋:

void RotateR(Node* parent)//右单旋
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;parent->_left = subLR;//更新parent的左节点if (subLR)//防止该节点为空{subLR->_parent = parent;//更新subLR的父节点}parent->_parent = subL;//更新parent的父节点subL->_right = parent;//subL的右子树置为parentsubL->_parent = pparent;//更新subL的父节点if (pparent == nullptr)//旋转的是整棵树{_root = subL;//更新根节点}else//将旋转后的子树链接上整个二叉树{if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}}subL->_bf = parent->_bf = 0;//更新平衡因子
}

下图画出了代码所表示的节点: 

2.2.2.3 左右双旋

接着来看到稍微复杂一点的情况,下面的图代表的是一棵AVL树,其中99、66和88表示的是三个节点,A、D分别为高度为h的AVL子树,B、C分别为高度为h-1的AVL子树:

现在我们向B子树中插入数据使其高度发生变化:

现在键值为99的节点平衡因子变为-2,下面我们要将这棵子树旋转: 

下面先将66所在子树进行左单旋:

再让99所在子树右单旋:

上面这种需要旋转的树的根节点平衡因子为-2,并且其根节点左孩子节点的平衡因子为1;这种情况下的两次旋转我们将其称为左右双旋

但是我们要注意的是,当新增节点在C子树上时,通过左右双旋调整后的结果又不一样:

 

我们可以看到因为新增节点的位置发生了变化,最终导致了各节点的平衡因子出现了差异

除了这两种情况,还有一种当h为0的极端情况:

所以我们在用代码实现的时要注意

void RotateLR(Node* parent)//左右双旋
{Node* subL = parent->_left;Node* subLR = subL->_right;int subLR_bf = subLR->_bf;//记录该节点的平衡因子来做旋转结束后的修改平衡因子的依据RotateL(subL);//调用左单旋RotateR(parent);//调用右单旋if (subLR_bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (subLR_bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (subLR_bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else{assert(false);}
}

2.2.2.4 右左双旋

最后我们只剩一种情况了:需要旋转的树的根节点平衡因子为-2,并且其根节点右孩子节点的平衡因子为-1

下面我们来分析:

我们向上面这棵树中的C子树插入数据使其高度发生变化:

现在键值为66的节点平衡因子变为2,下面我们要将这棵子树旋转:

下面先将99所在子树进行右单旋:

再将66所在子树进行左单旋:

这样先右旋再左旋的情况我们将其称做:右左双旋

和左右双旋一样新增节点的位置发生变化,会导致各节点的平衡因子出现差异,当新增节点在B子树上时,通过左右双旋调整后的结果又不一样:

另外还有h为0的特殊情况:

好了,分析到这里,使用代码实现也就不是问题了:

void RotateRL(Node* parent)//右左双旋
{Node* subR = parent->_right;Node* subRL = subR->_left;int subRL_bf = subRL->_bf;//记录该节点的平衡因子来做旋转结束后的修改平衡因子的依据RotateR(subR);//调用右单旋RotateL(parent);//调用左单旋if (subRL_bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (subRL_bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (subRL_bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}
}

2.3 AVL树的检查验证

下面我们来实现一个函数来检查自己所创建的AVL树是否符合规则:

int _CountHeight(Node* root)//计算树的高度
{if (root == nullptr)return 0;int leftnum = _CountHeight(root->_left);int rightnum = _CountHeight(root->_right);return leftnum > rightnum ? leftnum + 1 : rightnum + 1;
}
bool _IsBalance(Node* root)
{if (root == nullptr)return true;int leftH = _CountHeight(root->_left);int rightH = _CountHeight(root->_right);if (rightH - leftH != root->_bf)//检查当前节点平衡因子是否正确{cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}return abs(leftH - rightH) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);//检查左右子树高度之差的绝对值是否小于2
}
bool IsBalance()//检查是否为AVL树
{return _IsBalance(_root);
}

2.4 测试代码

void Test_AVLTree()
{const size_t N = 5000;AVLTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand() + i;t.Insert(make_pair(x, x));}t.InOrder();cout << t.IsBalance() << endl;cout << t.CountHeight() << endl;
}

运行效果:

三、AVL树实现的完整代码

#pragma once
#include<iostream>
#include<cassert>using namespace std;template<class Key,class Val>
class AVLTreeNode
{
public:AVLTreeNode<Key, Val>* _left;AVLTreeNode<Key, Val>* _right;AVLTreeNode<Key, Val>* _parent;//多一个指针指向其父节点,方便我们的后续操作pair<Key, Val> _kv;int _bf;//balance factor(平衡因子)AVLTreeNode(const pair<Key, Val>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};template<class Key, class Val>
class AVLTree
{typedef AVLTreeNode<Key, Val> Node;public:bool Insert(const pair<Key,Val>& kv){Node* cur = _root, * parent = nullptr;while (cur)//找到合适的位置{if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{cout << "插入的值重复" << endl;return false;}}cur = new Node(kv);cur->_parent = parent;//将插入的节点连接上二叉树if (parent == nullptr){_root = cur;}else if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}//向上调整各节点的平衡因子while (parent){if (parent->_left == cur){--parent->_bf;//更新平衡因子}else{++parent->_bf;//更新平衡因子}//根据父节点的平衡因子的情况决定师傅继续向上调整各节点的平衡因子if (parent->_bf == 1 || parent->_bf == -1)//平衡因子为1或1继续向上更新{parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//平衡因子为2或-2,树的结构不平衡,旋转parent节点所在子树{//旋转parent所在的子树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 if (parent->_bf == 0)//平衡因子为0,插入结束{break;}else//出现其他的情况说明树的根据有问题,报错退出{cout << "树的结构出错了" << endl;assert(0);exit(-1);}}return true;}void InOrder()//中序遍历{_InOrder(_root);cout << endl;}int CountHeight()//计算树的高度{return _CountHeight(_root);}bool IsBalance()//检查是否为AVL树{return _IsBalance(_root);}private:void RotateL(Node* parent)//左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* pparent = parent->_parent;parent->_right = subRL;//更新parent的右节点if (subRL)//防止该节点为空{subRL->_parent = parent;//更新subRL的父节点}parent->_parent = subR;//更新parent的父节点subR->_left = parent;//subR的左子树置为parentsubR->_parent = pparent;//更新subR的父节点if (pparent == nullptr)//旋转的是整棵树{_root = subR;//更新根节点}else//将旋转后的子树链接上整个二叉树{if (pparent->_left == parent){pparent->_left = subR;}else{pparent->_right = subR;}}subR->_bf = parent->_bf = 0;//更新平衡因子}void RotateR(Node* parent)//右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;Node* pparent = parent->_parent;parent->_left = subLR;//更新parent的左节点if (subLR)//防止该节点为空{subLR->_parent = parent;//更新subLR的父节点}parent->_parent = subL;//更新parent的父节点subL->_right = parent;//subL的右子树置为parentsubL->_parent = pparent;//更新subL的父节点if (pparent == nullptr)//旋转的是整棵树{_root = subL;//更新根节点}else//将旋转后的子树链接上整个二叉树{if (pparent->_left == parent){pparent->_left = subL;}else{pparent->_right = subL;}}subL->_bf = parent->_bf = 0;//更新平衡因子}void RotateLR(Node* parent)//左右双旋{Node* subL = parent->_left;Node* subLR = subL->_right;int subLR_bf = subLR->_bf;//记录该节点的平衡因子来做旋转结束后的修改平衡因子的依据RotateL(subL);//调用左单旋RotateR(parent);//调用右单旋if (subLR_bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if(subLR_bf == 0){parent->_bf = 0;subL->_bf = 0;subLR->_bf = 0;}else if (subLR_bf == 1){parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else{assert(false);}}void RotateRL(Node* parent)//右左双旋{Node* subR = parent->_right;Node* subRL = subR->_left;int subRL_bf = subRL->_bf;//记录该节点的平衡因子来做旋转结束后的修改平衡因子的依据RotateR(subR);//调用右单旋RotateL(parent);//调用左单旋if (subRL_bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (subRL_bf == 0){parent->_bf = 0;subR->_bf = 0;subRL->_bf = 0;}else if (subRL_bf == 1){parent->_bf = -1;subR->_bf = 0;subRL->_bf = 0;}else{assert(false);}}void _InOrder(Node* root){if (root == NULL)//如果是空树就直接结束{return;}_InOrder(root->_left);//先递归遍历其左子树cout << root->_kv.first << " ";//再遍历其根节点_InOrder(root->_right);//最后递归遍历其右子树}int _CountHeight(Node* root)//计算树的高度{if (root == nullptr)return 0;int leftnum = _CountHeight(root->_left);int rightnum = _CountHeight(root->_right);return leftnum > rightnum ? leftnum + 1 : rightnum + 1;}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftH = _CountHeight(root->_left);int rightH = _CountHeight(root->_right);if (rightH - leftH != root->_bf)//检查当前节点平衡因子是否正确{cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}return abs(leftH - rightH) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);//检查左右子树高度之差的绝对值是否小于2}
private:AVLTreeNode<Key, Val>* _root = nullptr;
};

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

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

相关文章

JavaEE 多线程

JavaEE 多线程 文章目录 JavaEE 多线程引子多线程1. 特性2. Thread类2.1 概念2.2 Thread的常见构造方法2.3 Thread的几个常见属性2.4 启动一个线程2.5 中断一个线程2.6 等待一个线程2.7 获取当前线程引用2.8 休眠当前线程 3. 线程状态 引子 当进入多线程这一块内容时&#xff…

2023-12-03 LeetCode每日一题(可获得的最大点数)

2023-12-03每日一题 一、题目编号 1423. 可获得的最大点数二、题目链接 点击跳转到题目位置 三、题目描述 几张卡牌 排成一行&#xff0c;每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动&#xff0c;你可以从行的开头或者末尾拿一张卡牌&#x…

使用OpenMVS重建模型

1、数据格式转换 首先将生成的稠密点云以及图片信息转换成openmvs支持的.mvs文件。在openmvs_sample中的bin文件内打开终端 作者&#xff1a;舞曲的小水瓶 https://www.bilibili.com/read/cv25019877/ 出处&#xff1a;bilibili interfaceCOLMAP.exe -i D:\desktop\test\toy\…

【Linux服务器Java环境搭建】05 Node JS安装及环境变量配置

【Linux服务器Java环境搭建】01购买云服务器以及在服务器中安装Linux系统 【Linux服务器Java环境搭建】02 通过xftp和xshell远程连接云服务器 【Linux服务器Java环境搭建】03 Git工具安装 【Linux服务器Java环境搭建】04 JDK安装&#xff08;JAVA环境安装&#xff09; 【Linux服…

flink源码分析 - 命令行参数解析-CommandLineParser

flink版本: flink-1.11.2 调用位置: org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint#main 代码位置: flink核心命令行解析器: org.apache.flink.runtime.entrypoint.parser.CommandLineParser /** Licensed to the Apache Software Foundati…

基于OpenAPI工具包以及LSTM的CDN网络流量预测

基于LSTM的CDN网络流量预测 本案例是基于英特尔CDN以及英特尔 OpenAPI Intel Extension for TensorFlow* Intel oneAPIDPC Library 的网络流量预测&#xff0c;CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;目的是将源站内容分发至最接近用户的节点&#xff0c;使用…

unity学习笔记17

一、动画组件 Animation Animation组件是一种更传统的动画系统&#xff0c;它使用关键帧动画。你可以通过手动录制物体在时间轴上的变换来创建动画。 一些重要的属性&#xff1a; 1. 动画&#xff08;Animation&#xff09;&#xff1a; 类型&#xff1a; Animation组件允许…

使用Prometheus监控Padavan路由器

Prometheus监控Padavan路由器 1、背景 近期在Synology&#xff08;群辉&#xff09;中安装一套Prometheus监控程序&#xff0c;目前已经监控Synology&#xff0c;然后家中有有路由器&#xff08;Padavan&#xff09;型号&#xff0c;也准备使用PrometheusGrafan进行监控。 ‍…

采集工具-免费采集器下载

在当今信息时代&#xff0c;互联网已成为人们获取信息的主要渠道之一。对于研究者和开发者来说&#xff0c;如何快速准确地采集整个网站数据是至关重要的一环。以下将从九个方面详细探讨这一问题。 确定采集目标 在着手采集之前&#xff0c;明确目标至关重要。这有助于确定采集…

冲突域和广播域

文章目录 冲突域广播域 冲突域 在网络内部两个数据帧同时进行传输时&#xff0c;产生与发生冲突的区域&#xff0c;所有共享介质都是一个冲突域。冲突域时基于第一层&#xff0c;物理层的。 集线器和中继器因为都在物理层&#xff0c;没有MAC地址表&#xff0c;所以不能隔离冲…

数据结构之堆排序以及Top-k问题详细解析

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力 目录 1.前言 2.堆排序 2.1降序排序 2.2时间复杂…

Prime 1.0

信息收集 存活主机探测 arp-scan -l 或者利用nmap nmap -sT --min-rate 10000 192.168.217.133 -oA ./hosts 可以看到存活主机IP地址为&#xff1a;192.168.217.134 端口探测 nmap -sT -p- 192.168.217.134 -oA ./ports UDP端口探测 详细服务等信息探测 开放端口22&#x…

【Vulnhub 靶场】【HackathonCTF: 2】【简单】【20210620】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hackathonctf-2,714/ 靶场下载&#xff1a;https://download.vulnhub.com/hackathonctf/Hackathon2.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月20日 文件大小&#xff1a;2.6 GB 靶场作者&…

54.多级缓存

目录 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。 1&#xff09;进程缓存和缓存。 2&#xff09;导入商品案例。 1.安装MySQL 2.导入SQL 3.导入Demo工程 4.导入商品查询页面 3&#xff09;初识Caffeine&#xff08;就是在springboot学过的注解方式的cache&…

NAND Flash和NOR Flash的异同

NAND Flash和NOR Flash是两种常见的闪存类型。 NOR Flash是Intel于1988年首先开发出来的存储技术&#xff0c;改变了原先由EPROM和EEPROM一统天下的局面。 NAND Flash是东芝公司于1989年发布的存储结构&#xff0c;强调降低每比特的成本&#xff0c;更高的性能&#xff0c;并…

栈和队列OJ题——15.循环队列

15.循环队列 622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; * 解题思路&#xff1a; 通过一个定长数组实现循环队列 入队&#xff1a;首先要判断队列是否已满&#xff0c;再进行入队的操作&#xff0c;入队操作需要考虑索引循环的问题&#xff0c;当索引越界&…

网络接口规范

1、基本物理层: a) RJ45接口作为最基本的网络接口之一有两种形式&#xff1a;对于百兆网口有4条线&#xff0c;2对差分线&#xff1b;对于千兆网口有4对差分线。RJ45水晶头是有8个凹槽和8个触点&#xff08;8p8c&#xff09;的接头&#xff0c;分为集成网络变压器和非集成网络变…

2022年9月8日 Go生态洞察:Go Developer Survey 2022 Q2 结果分析

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

物奇平台电容触摸功能调试

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 物奇平台电容触摸功能调试 1 修改按键驱动宏 2 编译生成wpk 文件,import 导入烧录文件。…

水果编曲软件fl studio手机版下载

fl studio mobile手机版中文名水果编曲软件&#xff0c;它是一款非常不错的音乐编曲软件&#xff0c;凭借简单易上手的操作方式&#xff0c;强悍且实用的功能&#xff0c;深受到了音乐创作者的喜爱&#xff0c;不仅仅提供了广阔的音乐创作空间&#xff0c;可以让用户对舞曲、轻…