福田欧曼官方网站/百姓网推广怎么收费标准
目录
1、AVL树的概念
2、AVL树结点的定义
3、AVL树的插入
4、AVL树的旋转
4.1 左单旋
4.2 右单旋
4.3 右左双旋
4.4 左右双旋
5、AVL树的验证
6、AVL树的性能
前面对map/multimap/set/multiset进行了简单的介绍,会大仙,这几个容器有个共同点是:其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。本章介绍的AVL树和下一章介绍的红黑树,就是平衡树
1、AVL树的概念
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
为什么不将树调整为左右子树的高度相等呢?因为有些结点数量下(如2和4个结点),做不到左右子树高度相等,所以最好的平衡二叉树就是高度不超过1
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
上面这棵树的平衡因子是用右子树高度-左子树高度,这不是一定的,也可以反过来,并没有严格规定,这篇文章中的平衡因子都是用右子树高度-左子树高度的
因为AVL树每个结点的左右子树高度差不超过1,所以平衡因子只有在-1、0、1是才是正常的
2、AVL树结点的定义
我们这里实现的是KV模型的AVL树,为了与map对应,两个值是以pair的形式存储。并且每个结点中还要增加一个平衡因子。当插入新结点时,为了检查路径上是否出现异常的平衡因子,还要增加一个指向父亲结点的指针,另外原来还有指向左右孩子的指针,称为三叉链结构
template<class K,class V>
struct AVLTreeNode
{pair<K, V> _kv;// 三叉链AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf; // 平衡因子AVLTreeNode(const pair<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};
根结点的_parent置为nullptr
3、AVL树的插入
向AVL树中插入一个新结点时,插入的操作是和二叉搜索树一致的,只是插入一个结点后,需要更新一下这个结点到这棵树的根节点路径上部分结点的平衡因子,若是平衡因子出现异常,则需要进行旋转操作。
插入结点,会影响部分祖先结点的平衡因子。因为这里的平衡因子 = 右子树高度 - 左子树高度
结点插入在左子树,其父亲的平衡因子--
结点插入在右子树,其父亲的平衡因子++
是否需要继续往上更新祖先,要看parent所在子树的高度是否发生了变化,此时有3种情况:
1. 更新后parent的平衡因子变成0
说明没插入时parent的平衡因子是-1/1,一边高一边低,插入后,变成两边一样高,parent所在子树的高度没有发生变化,所以不需要向上更新
2. 更新后parent的平衡因子变成-1/1
说明没插入时parent的平衡因子是0,两边一样高,插入后,变成一边高一边低,parent所在子树的高度变高了,所以需要继续向上更新
3. 更新后parent的平衡因子变成-2/2
说明没插入时parent的平衡因子是-1/1,插入结点插入在高的那一边,进一步加剧了parent所在子树的不平衡,已经违反规定,需要旋转处理
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->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// 更新平衡因子while (parent){if (cur == parent->_left)parent->_bf--;elseparent->_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){// 不平衡了,旋转处理}else{assert(false);}}return true;
}
4、AVL树的旋转
4.1 左单旋
当新结点插入到右子树的右侧,需要进行左单旋 ----- 右右:左单旋
这里面h>=0,表示a、b、c是高度为h的AVL子树
左单旋的标志就是出现异常的结点的平衡因子为2,并且其右子树的平衡因子为1,那么这个时候就进行左单旋。
左单旋步骤:
1. 将subRL变成parent的右子树
2. 将parent变成subR的左子树
3. 建立subR与parent的父亲结点parentParent的关系
4. 更新parent和subR的平衡因子
完成代码时,除了要完成每个结点的链接关系,还需要注意重新链接后结点父亲的变化和平衡因子的变化。其中平衡因子只有30和60这两个结点有变化,因为要改变平衡因子,需要改变左右子树的高度,并且这两个结点的平衡因子都变成了0。注意,subRL是有可能为nullptr的,当h等于0时,所以修改其父亲结点时需要判断一下。
void RotateL(Node* parent) // 左单旋
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;// 1. 将subRL变成parent的右子树parent->_right = subRL;if (subRL)subRL->_parent = parent;// 2. 将parent变成subR的左子树subR->_left = parent;parent->_parent = subR;// 3. 建立subR与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;subR->_parent = parentParent;}// 4. 更新parent和subR的平衡因子 parent->_bf = subR->_bf = 0;
}
4.2 右单旋
当新插入的结点在左子树的左侧,需要进行右单旋 ----- 左左:右单旋
右单旋的标志就是出现异常的结点的平衡因子为-2,并且其左子树的平衡因子为-1,那么这个时候就进行左单旋。
右单旋的步骤:
1. 将subLR变成parent的左子树
2. 将parent变成subL的右子树
3. 建立subL与parent的父亲结点parentParent的关系
4. 更新parent和subL的平衡因子
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;// 1. 将subLR变成parent的左子树parent->_left = subLR;if (subLR)subLR->_parent = parent;// 2. 将parent变成subL的右子树subL->_right = parent;parent->_parent = subL;// 3. 建立subL与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}// 4. 更新parent和subL的平衡因子 parent->_bf = subL->_bf = 0;
}
4.3 右左双旋
当新插入的结点在右子树的左侧,需要先进行右单旋,再进行左单旋 ----- 右左:右左双旋
右左双旋的标志是出现异常的结点的平衡因子是2,其右子树的平衡因子是-1
第一种情况:在右子树的左子树的右子树插入(即c子树插入)
第二种情况:在右子树的左子树的左子树插入(即b子树插入)
这两种情况都是先进行右单旋,然后进行左单旋,只是旋转完成后,个别结点的平衡因子不同。如何区分是在b插入还是c插入呢?
当h > 0时
1. 插入结点后,若右子树的左子树这个结点的平衡因子是1,则是在c插入的
2. 插入结点后,若右子树的左子树这个结点的平衡因子是-1,则是在b插入的
上面两幅图就是h > 0的两种情况的图
当h == 0 时
3. 插入之前连60这个结点都没有,60这个结点自己就是新增,此时60这个结点的平衡因子是0
右左单旋步骤:
1、计算subRL的平衡因子
2、对subR进行右单旋
3、对parent进行左单旋
4、更新parent、subR、subRL的平衡因子
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;// 1、计算subRL的平衡因子int bf = subRL->_bf;// 2、对subR进行右单旋RotateR(subR);// 3、对parent进行左单旋RotateL(parent);// 4、更新parent、subR、subRL的平衡因子if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}
}
4.4 左右双旋
当新插入的结点在左子树的右侧,需要先进行左单旋,再进行右单旋 ----- 左右:左右双旋
左右双旋的标志是出现异常的结点的平衡因子是-2,其左子树的平衡因子是1
第一种情况:在左子树的右子树的左子树插入(即b子树插入)
第二种情况:在左子树的右子树的右子树插入(即c子树插入)
第三种情况:当h == 0时
左右单旋的步骤:
1. 计算subLR的平衡因子
2. 对subL进行左单旋
3. 对parent进行右单旋
4. 更新parent、subR、subRL的平衡因子
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;// 1. 计算subLR的平衡因子int bf = subLR->_bf;// 2. 对subL进行左单旋RotateL(subL);// 3. 对parent进行右单旋RotateR(parent);// 4. 更新parent、subR、subRL的平衡因子if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}
}
会发现同号单旋,异号双旋
所以此时插入操作的代码为
bool Insert(const pair<K, V>& kv)
{// 若_root为空,直接让新插入的结点变成根if (_root == nullptr){_root = new Node(kv);return true;}// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点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->_right = cur;}else{parent->_left = cur;}// 因为现在是三叉链结果,还要链接一下父亲结点cur->_parent = parent;// 更新平衡因子while (parent){// 更新当前结点的父亲结点if (cur == parent->_left)parent->_bf--;elseparent->_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 && parent->_right->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && parent->_left->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && parent->_right->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}break; // 旋转完后,一定平衡了,所以可以跳出循环}else{assert(false);}}return true;
}
注意,旋转完成之后,这颗子树的父亲结点的平衡因子一定是0,所以可以直接跳出循环,不需要继续向上更新平衡因子
5、AVL树的验证
在前面,我们已经实现了插入操作,那么按照这个插入函数获得的真的是一颗AVL树吗?
所以,我们需要进行验证。验证只需要计算每个结点的左右树高,判断左右树高的差值小于2即可
int _Height(Node* root)
{if (root == nullptr) return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool _IsBalanceTree(Node* root)
{// 空树也是AVL树if (root == nullptr) return true;// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (abs(diff) >= 2 || root->_bf != diff)return false;// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root -> _right);
}
这两个函数需要写在AVL树内部,因为用到了_root
6、AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即log2 (N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
template<class K,class V>
struct AVLTreeNode
{pair<K, V> _kv;// 三叉链AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf; // 平衡因子AVLTreeNode(const pair<K,V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0){}
};
template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree() = default;~AVLTree(){Destory(_root);_root = nullptr;}AVLTree(const AVLTree<K,V>& kv){_root = Copy(kv._root);}AVLTree operator=(AVLTree<K, V> kv){swap(_root, kv._root);}bool Insert(const pair<K, V>& kv){// 若_root为空,直接让新插入的结点变成根if (_root == nullptr){_root = new Node(kv);return true;}// 若_root不为空,按二叉搜索树的规则找到插入的位置,同时也要记录父亲结点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->_right = cur;}else{parent->_left = cur;}// 因为现在是三叉链结果,还要链接一下父亲结点cur->_parent = parent;// 更新平衡因子while (parent){// 更新当前结点的父亲结点if (cur == parent->_left)parent->_bf--;elseparent->_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 && parent->_right->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && parent->_left->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && parent->_right->_bf == -1){RotateRL(parent);}else{RotateLR(parent);}break; // 旋转完后,一定平衡了,所以可以跳出循环}else{assert(false);}}return true;}void InOrder(){_InOrder(_root);}bool IsBalanceTree(){return _IsBalanceTree(_root);}int Height(){return _Height(_root);}int Size(){_Size(_root);}
private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr) return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool _IsBalanceTree(Node* root){// 空树也是AVL树if (root == nullptr) return true;// 计算pRoot节点的平衡因子:即pRoot左右子树的高度差int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);int diff = rightHeight - leftHeight;// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者// pRoot平衡因子的绝对值超过1,则一定不是AVL树if (abs(diff) >= 2 || root->_bf != diff)return false;// pRoot的左和右如果都是AVL树,则该树一定是AVL树return _IsBalanceTree(root->_left) && _IsBalanceTree(root -> _right);}void RotateL(Node* parent) // 左单旋{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;// 1. 将subRL变成parent的右子树parent->_right = subRL;if (subRL)subRL->_parent = parent;// 2. 将parent变成subR的左子树subR->_left = parent;parent->_parent = subR;// 3. 建立subR与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subR;subR->_parent = nullptr;}else{if (parent == parentParent->_left)parentParent->_left = subR;elseparentParent->_right = subR;subR->_parent = parentParent;}// 4. 更新parent和subR的平衡因子?parent->_bf = subR->_bf = 0;}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;// 1. 将subLR变成parent的左子树parent->_left = subLR;if (subLR)subLR->_parent = parent;// 2. 将parent变成subL的右子树subL->_right = parent;parent->_parent = subL;// 3.?建立subL与parent的父亲结点parentParent的关系if (parentParent == nullptr) // 说明parent就是根结点{_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent)parentParent->_left = subL;elseparentParent->_right = subL;subL->_parent = parentParent;}// 4.更新parent和subL的平衡因子parent->_bf = subL->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;// 1、计算subRL的平衡因子int bf = subRL->_bf;// 2、对subR进行右单旋RotateR(subR);// 3、对parent进行左单旋RotateL(parent);// 4、更新parent、subR、subRL的平衡因子if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;// 1. 计算subLR的平衡因子int bf = subLR->_bf;// 2. 对subL进行左单旋RotateL(subL);// 3. 对parent进行右单旋RotateR(parent);// 4.?更新parent、subR、subRL的平衡因子if (bf == 0){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}}void Destory(Node* root){if (root == nullptr) return;Destory(root->_left);Destory(root->_right);delete root;}Node* Copy(Node* root){if (root == nullptr) return nullptr;Node* newNode = new Node(root->_kv);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}void _InOrder(Node* _root){if (_root == nullptr) return;_InOrder(_root->_left);cout << _root->_kv.first << "--" << _root->_kv.second << "--" << _root->_bf << endl;_InOrder(_root->_right);}Node* _root;
};
相关文章:

【数据结构】AVL树(图文解析 + 代码实现)
目录 1、AVL树的概念 2、AVL树结点的定义 3、AVL树的插入 4、AVL树的旋转 4.1 左单旋 4.2 右单旋 4.3 右左双旋 4.4 左右双旋 5、AVL树的验证 6、AVL树的性能 前面对map/multimap/set/multiset进行了简单的介绍,会大仙,这几个容器有个共同点是…...

HTML(六)——HTML表单和框架
HTML 表单 HTML 表单用于收集用户的输入信息,是一个包含表单元素的区域 HTML 表单表示文档中的一个区域,此区域包含交互控件,将用户收集到的信息发送到 Web 服务器。 HTML 表单通常包含各种输入字段、复选框、单选按钮、下拉列表等元素。 …...

【Qt 】JSON 数据格式详解
文章目录 1. JSON 有什么作用?2. JSON 的特点3. JSON 的两种数据格式3.1 JSON 数组3.2 JSON 对象 4. Qt 中如何使用 JSON 呢?4.1 QJsonObject4.2 QJsonArray4.3 QJsonValue4.4 QJsonDocument 5. 构建 JSON 字符串6. 解析 JSON 字符串 1. JSON 有什么作用? &#x…...

路由表与IP数据报转发:基础小白指南
目录 1. 路由表的基本概念 2. 路由表中的默认路由 3. IP数据报的转发流程 4. 路由聚合 5. 最长前缀匹配 总结 在网络世界中,IP数据报的转发是如何进行的? 这篇文章将带你深入了解路由表的基本概念和IP数据报的转发流程。我们会用简洁明了的语言和实…...

python—selenium爬虫
文章目录 Selenium与Requests对比一、工作原理二、功能特点三、性能表现 下载对应驱动1.首先我们需要打开edge浏览器,打开设置,找到“关于Microsoft Edge”,点击进入查看浏览器版本。2.查找版本之后,搜索edge驱动下载,…...

Mysql - 索引
目录 一、存储引擎 二、索引 索引结构 索引分类 索引语法 联合索引 前缀索引 索引使用规则 最左前缀法则 范围查询使索引失效 字段做运算操作索引失效 字符串字段不加单引号索引失效 字段做前模糊查询索引失效 or连接条件索引失效 数据发布情况索引失效 指定使用…...

从课本上面开始学习的51单片机究竟有什么特点,在现在的市场上还有应用吗?
引言 51单片机,作为一种经典的微控制器,被广泛应用于各种嵌入式系统中。尽管如今ARM架构的高性能低成本单片机在市场上占据主导地位,但51单片机凭借其独特的优势依然在某些领域保持着应用价值。本文将深入探讨51单片机的特点、架构、应用以及…...

uniapp中出现Uncaught runtime errors
项目中运行出现上面的错误信息,使用uniapp发现,其实我只是跨域了,控制台报错,但是不想屏幕上显示; 解决办法是在vue.config.js增加如下配置即可 devServer: {client: {overlay: false,errors:true},}, 错误信息也不想…...

数字信号处理基础知识(二)
在介绍完“离散时间序列”基本概念和性质后,实际上就已经踏入了“数字信号处理”这门学科的学习征程,这篇文章里主要去说明“线性时不变系统”的定义概念和探讨“周期采样”的注意细节,相信更加理解这些概念定义和底层逻辑,对于大…...

人生低谷来撸C#--015 C# 属性(Property)
1、概念 在C#中,属性(Property)是一种特殊的成员,它提供了一种灵活的机制来访问和修改对象的状态(即类的字段)。属性结合了字段和方法的特性,使得数据的访问和修改更加安全和便捷。下面我用一个…...

面试题003:面向对象的特征——封装性
Java规定了4种权限修饰,分别是:private、缺省、protected、public。我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小。 封装性在程序中的体现: 场景1:私有化(private)类的属性,提供公共(pub…...

森林防火,森林防火智能储水罐_鼎跃安全
森林防火是保护森林的重要措施,每年发生的森林火灾都严重威胁着自然安全,对社会经济和生态造成严重的破坏。为了切实有效地预防并扑灭森林火灾,森林防火智能储水罐已成为现代森林防火体系中的重要装备。 储水罐内置传感器和控制系统ÿ…...

虚幻引擎,体积雾、体积光、镜头泛光
1、体积雾 这里介绍的是用于地面的体积雾效果,效果如图1-1: 图1-1 首先,需要场景中存在指数级高度雾并开启体积雾(如图1-2)。然后创建材质,材质域选择“体积”,混合模式选择“Additive”。材质节…...

Python 机器学习求解 PDE 学习项目——PINN 求解二维 Poisson 方程
本文使用 TensorFlow 1.15 环境搭建深度神经网络(PINN)求解二维 Poisson 方程: 模型问题 − Δ u f in Ω , u g on Γ : ∂ Ω . \begin{align} -\Delta u & f \quad & \text{in } \Omega,\\ u & g \quad & \text{on } \Gamma:\p…...

微信小程序删除滑块 SwiperCell 自动收起 Van weapp van-swipe-cell 滑块自动收起 点击页面也自动收起滑块
在当前页面整个 view 中 给页面绑定 点击事件bindtap"onSwipeCellPage"给 van-swipe-cell 组件设置 id (for循环可以添加 id"swip-cell-{{item.id}}" )van-swipe-cell 组件 添加属性 当用户打开滑块时触发 bind:open"swiperCel…...

【vluhub】log4j注入漏洞 CVE-2021-44228
LOG4介绍 是一个用Java编写的可靠,快速和灵活的日志框架(API),它在Apache软件许可下发布 log4j存在远程代码执行漏洞、受影响版本2.x 部署环境 攻击机环境:192.168.3.180 kail环境:192.168.203.12【NAT…...

Redis核心技术与实战学习笔记
Redis核心技术与实战学习笔记 最近想沉下心来看下redis,买了蒋德钧老师的《Redis 核心技术与实战》,这里记录一些学习笔记 希望能够坚持下去有想一起学习的童鞋,可以点击跳转到文章尾部获取学习资源,仅供学习不要用于任何商业用途!!! redis知识全景图 …...

力扣经典题目之->设计循环队列 的超详细讲解与实现
一:题目 二:思路讲解 前提: a:本文采取数组来实现队列去解决题目 b:开辟k1个空间,front指向队首,rear指向队尾的后一个,rear这样会更好的判空和判满 以下根据pop和push感受满和空…...

【数据结构】排序算法——Lesson2
Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…...

Ubuntu编译ffmpeg并添加cmake工程
文章目录 前言前提须知为什么要自己编译 FFmpeg前提软件包与工具的安装编译ffmpeg写CMakeList.txt包含ffmpeg到我们项目中 总结 前言 FFmpeg 是一个领先的多媒体框架,能够解码、编码、转码、复用、解复用、流化、过滤和播放几乎所有人类和机器创造的内容。FFmpeg 包…...

Vue.js[组件(Component)]
什么是: 拥有专属的HTML,CSS,数据的,可重用的页面独立区域 一个页面由多个组件聚合而成一个大型的页面 在代码层面上,一个组件就是一个可反复使用的自定义标签。 vs jq插件 vs boot组件 boot插件: 虽然可重用,但仍需…...

基于微信小程序+SpringBoot+Vue的校园自助打印系统(带1w+文档)
基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 基于微信小程序SpringBootVue的校园自助打印系统(带1w文档) 管理信息可以处理复杂的信息从而提高用户的工作效率,减少失误。所以本基于Vue和微信小程序的校园自助打印系统的开发非常有意义,本系…...

qt设置过滤器
1.创建事件过滤器类,在主窗口中安装事件过滤器 class PasteFilter : public QObject {Q_OBJECTpublic:PasteFilter(QObject *parent nullptr) : QObject(parent) {}protected:bool eventFilter(QObject *obj, QEvent *event) override {if (event->type() QEv…...

线上环境服务器CPU飙升排查
前因 收到线上服务器CPU使用率100%的告警信息。 环境 jdk1.8CentOS Linux ;CentOS Linux 排查 查看服务器CPU使用率 果然cpu已经达到了100%了 命令 top 使用arthas工具 使用方式 arthas 执行命令java -jar arthas-boot.jar 然后执行命令 thread 看到有两个…...

unity文字||图片模糊
一.文字模糊 1、增大字体大小后等比缩放 快捷键R 2、更改字体渲染模式 二.图片模糊 1、更改过滤模式 2、更改格式或者压缩 3、如果只是图片边缘看不清,可以增加canvas/图片的每单位参考像素...

香薰学习笔记
1 喷香水的方法 ChatGPT-4o 学习使用香水是提升个人形象的一个好方法。 喷香水的方法如下: 皮肤吸收:香水最好喷在皮肤上,因为皮肤的温度能帮助香水散发出更好的香味。喷在衣服上可能会影响香水的原始味道。脉搏点:将香水喷在脉搏…...

iOS ------ weak的基本原理
1.weak的基本概念 weak弱引用,所引用的对象的引用计数不会加一,引用对象被释放的时候会自动设置为nil多用于解决对象间的相互引用造成内存泄露的循环引用的问题 2.实现原理 Person *object [[Person alloc] init]; id __weak objc object;Runtime维…...

实时更新UI界面
1.处理实时通信,几种方案 1:当一个用户发送一条需要实时更新的信息,我可以直接查找在线用户,通过在线用户来进行判断条件,发送更新请求 2:用户在一个需要实时更新的界面时,就不断的向服务端发…...

为什么Spring不推荐@Autowired用于字段注入
背景 Spring是Java程序员常用的框架之一。官方从Spring 4.0开始不推荐使用Autowired进行字段注入。 Spring注入方式 基于构造器注入:在构造器上使用Autowired。 优点:可以声明字段为final,确保字段在构造时被初始化。 基于setter方法注入&…...

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十九章 Linux MISC驱动
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...