当前位置: 首页 > news >正文

AVL树的模拟实现

我们上期提到了二叉搜索树,只是简单的讲了一下原理,那么今天我们就讲一下AVL树。

目录

  • AVL树的概念
  • AVL树的实现
    • AVL树的架构
    • insert插入
      • 引用pair对象
      • 引进parent指针
      • 仅插入数据
      • 调节平衡因子
        • 情况1:插入在父亲的右边,父亲的平衡因子++后为0
        • 情况2:插入在父亲的左边,父亲的平衡因子--后为0
        • 情况3:插入左或者右,恰巧父亲不是0,是-1/1
        • 情况4:当父亲的平衡因子==-2/2,不需要在更新了,证明不平衡了,需要旋转。
        • 左边高,右旋
        • 右边高,左旋
        • 双旋转
          • 右左旋转:先右旋然后左旋。
          • 左右双旋:先左旋,在右旋。
    • 中序遍历
    • 判断是否平衡
    • AVL树整体代码

AVL树的概念

其实大家应该很奇怪,难道二叉搜索树不能存储数据吗?为什么要有AVL树呢?二叉搜索树有可能会有畸形的情况。像下图,数据比较分散的话,这棵树很正常,如果我们插入的数相对有序就会变成右边那样畸形的树。
在这里插入图片描述
这个时候就需要人工干预,这里的AVL数就可以更好的控制这个情况,它有自己的平衡规则:左右子树高度之差(平衡因子)绝对不超过1(-1,0,1)。如果不满足这个规则,那么我们就旋转。

AVL树的实现

因为AVL树也是二叉搜索树的一种,所以他也要满足二叉搜索树的条件,然后在满足他自己的平衡规则

AVL树的架构

其实数的节点架构和整体架构并没有变,只是多了一个bf(平衡因子),用来调节平衡。

struct AVLTreeNode
{AVLTreeNode(const pair<K, V> kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;pair<K, V> _kv;int _bf;
};
template<class K,class V>
class AVLTree
{public:typedef AVLTreeNode<K,V> Node;private:Node* _root=nullptr;
}

insert插入

引用pair对象

这里与原来不同,这里我们引入了一个pair对象,那么pair是什么?我们用pair来实现KV结构,在库中的map也是用pair也完成KV结构的,所以这里我们就用这pair。pair对象的first是K值,second是V值。
在这里插入图片描述

引进parent指针

这里引用父指针是因为我们将来要旋转,要不断向上调节平衡因子,因为当我们插入某个值有可能引起平衡因子失衡。

仅插入数据

其实插入部分和我们之前写的一样,只不过要注意的是,我们的值存的是pair对象,要像拿到K值需要 _kv.first拿到K值。一定要遵循二叉搜索树的规律,左子树比根小,右子树比根大

	if (_root == nullptr){//插入第一个值Node* newNode = new Node(kv);_root = newNode;return true;}Node* newNode = new Node(kv);Node* cur = _root;Node* parent = cur;while (cur){if (cur->_kv.first < newNode->_kv.first){//大于在右边parent = cur;cur = cur->_right;}else if (cur->_kv.first > newNode->_kv.first){//小于在左边parent = cur;cur = cur->_left;}else{//等于,二叉搜索树不允许冗余,所以直接返回false。return false;}}if (parent->_kv.first > newNode->_kv.first){//在左边parent->_left = newNode;}else{//在右边parent->_right = newNode;}newNode->_parent = parent;

调节平衡因子

情况1:插入在父亲的右边,父亲的平衡因子++后为0

红色是我插入的数,插入后,它的parent:12从-1加加后变成了0,我们还需要向上更新吗?答案是不需要向上更新,为什么?0表示左右子树的高度差为0,也就说高度没有变,所以我不需要再向上更新
解释:平衡因子原来是1/-1都表示这个树缺了一个节点,当我们插入之后正好填上了这个节点,但是高度并不变。看图!我把15插入,右子树这个高度没有变。
在这里插入图片描述

情况2:插入在父亲的左边,父亲的平衡因子–后为0

当我插入在左边,那我们就需要给父亲的因子bf–。
在这里插入图片描述

情况3:插入左或者右,恰巧父亲不是0,是-1/1

父亲的平衡因子==1/-1,父亲所在子树高度变了。高度变了继续向上更新。
在这里插入图片描述

情况4:当父亲的平衡因子==-2/2,不需要在更新了,证明不平衡了,需要旋转。

这个时候就不满足AVL树的规则了,就需要旋转。
在这里插入图片描述

左边高,右旋

这个树就是左边高的情况,你会发现parent为-2,cur为-1,这个情况就是左边比右边要高,那么我们需要右旋。
在这里插入图片描述
右旋的口诀:把cur的右边给parent的左边,cur的右边链接parent,在让parent的parent链接cur。会发现我们把原来的parent变成了cur的右边。并且现在是平衡的。

其实为什么要把cur的右边给parent呢?是因为cur的右边时当前树最大的值,cur给了parent链接到左边,依然不会破坏二叉搜索树的规则。
在这里插入图片描述

void RotateR(Node* parent )
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}parent->_bf = subL->_bf = 0;
}
右边高,左旋

当我们插入红色节点的时候就会导致右边过高,那么我们就需要左旋。
在这里插入图片描述

左旋的口诀:让cur的左边给parent的右边链接,然后让父亲变成cur的左边
解释:为什么要把cur左边的值给parent的右边?cur当前位置是parent的右边,cur的左边也是比parent大的值,给parent的右边依然不影响二叉搜索树的规则。
在这里插入图片描述

void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if(subRL)subRL->_parent = parent;subR->_left = parent;Node* pphead = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pphead->_parent == parent){pphead->_right = subR;}else{pphead->_left = subR;}subR->_parent = pphead;}subR->_bf = parent->_bf = 0;
}
双旋转

还有一种情况,它并不是单纯的一边高,用单旋并不能解决问题。当我们仅仅只是单旋会发现他有变成了形态不同,但是问题一样的情况,这个时候就需要双旋
在这里插入图片描述

右左旋转:先右旋然后左旋。

既然我们单旋解决不了问题,我们可以把他变成一边高,然后进行单旋。
如这个图,我们可以对subR进行右旋后,在对parent左旋就可以了。
在这里插入图片描述
右左双旋后:
在这里插入图片描述
但是有个问题,平衡因子我们应该如何更新?你可能会说这种情况不是正好满足左旋/右旋后平衡因子变成0吗?但是如果其他位置都有节点呢?接下来我们抽象图,给大家演示一下。
当我们插入节点的位置不同,你会发现每一个平衡因子也不一样。如图:我插在了右边。这个时候我通过右左双旋就会得到下图。如最右图的红色平衡因子,应该变成这个情况。所以说插入的位置不同,平衡因子也会不同
在这里插入图片描述
如图:假如我插在了左边。会发现平衡因子又不一样了。所以我们并不能用一个例子来概括所有。
在这里插入图片描述
那么我们应该怎么做呢?其实从我们画图,你会发现我们改变的只不过是parent 、subR、sunRL这三个节点的因子,其他的并没有改变。并且,跟subRL的因子有密切关系,所以我们只需要判断subRL的因子是0/1/-1就能修改他们三个因子

		void RotateRL(Node* parent){//右左双旋//平衡因子需要自己调节Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);if (bf == 0){//新插入的parent->_bf =0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){//右边插入的subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if(bf==-1){//在左边插入的parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else{assert(false);}}
左右双旋:先左旋,在右旋。

图下是:左右双旋。
在这里插入图片描述

当然了,我们依然需要分析平衡因子,所以我们依然要分析插入在哪个位置。
如图:当插入在右边。
在这里插入图片描述

如图:当我们插在左边。
在这里插入图片描述

		void RotateLR(Node* parent){//左右双旋//平衡因子需要单独调节Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;//先左旋在右旋RotateL(parent->_left);RotateR(parent);//重新计算调节因子if (bf == 0){//当且节点就是新插的subL->_bf = 0;parent->_bf = 0;subLR->_bf = 0;}else if(bf==1){//在当前节点的右边插入的parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if(bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else{assert(false);}}

中序遍历

因为二叉搜索树我们都是中序遍历,因为中序遍历更接近有序。

void _Inorder(Node* root)
{if (root == nullptr){return;}_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second<<endl;_Inorder(root->_right);
}

判断是否平衡

什么时候不平衡?是不是因子大于等于2的时候,所以我们需要算左右树的高度相减,如果超过2,那就是不平衡。

		bool _isBalance(Node* root){if (root == nullptr){return true;}int left = _Height(root->_left);int right = _Height(root->_right);if (abs(left - right) >= 2) return false;//因子大于等于2的时候不平衡return _isBalance(root->_left) && _isBalance(root->_right);}int _Height(Node* root){if (root == nullptr)return 0;int left = _Height(root->_left);int right = _Height(root->_right);return max(left, right) + 1;}

AVL树整体代码

以下是整个AVL树所有的代码?

问题:为什么没有像库一样写删除呢?答:我们模拟实现其实是为了了解底层,并不是要超过底层,因为现有的库已经很好了,我们没必要写一个。二叉搜索树很少用删除接口。所以这里没有实现

#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<assert.h>using namespace std;
namespace KV
{template<class K,class V>struct AVLTreeNode{AVLTreeNode(const pair<K, V> kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;pair<K, V> _kv;int _bf;};template<class K,class V>class AVLTree{public:typedef AVLTreeNode<K,V> Node;bool insert(const pair<K, V> kv){if (_root == nullptr){//插入第一个值Node* newNode = new Node(kv);_root = newNode;return true;}Node* newNode = new Node(kv);Node* cur = _root;Node* parent = cur;while (cur){if (cur->_kv.first < newNode->_kv.first){//大于在右边parent = cur;cur = cur->_right;}else if (cur->_kv.first > newNode->_kv.first){//小于在左边parent = cur;cur = cur->_left;}else{//等于,二叉搜索树不允许冗余,所以直接返回false。return false;}}if (parent->_kv.first > newNode->_kv.first){//在左边parent->_left = newNode;}else{//在右边parent->_right = newNode;}newNode->_parent = parent;cur = newNode;while (parent){if (parent->_left == cur){cur->_parent->_bf--;}else if (parent->_right == cur){cur->_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){RotateR(parent);}//右边高,左旋转else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋RotateRL(parent);}else{assert(false);}}else{//按道理说不可能有这种情况,但是保不准会有bugassert(false);break;}}return true;}void Inorder(){_Inorder(_root);}int Height(){return _Height(_root);}bool isBalance(){return _isBalance(_root);}private:int _Height(Node* root){if (root == nullptr)return 0;int left = _Height(root->_left);int right = _Height(root->_right);return max(left, right) + 1;}bool _isBalance(Node* root){if (root == nullptr){return true;}int left = _Height(root->_left);int right = _Height(root->_right);if (abs(left - right) >= 2) return false;return _isBalance(root->_left) && _isBalance(root->_right);}void RotateRL(Node* parent){//右左双旋//平衡因子需要自己调节Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);if (bf == 0){//新插入的parent->_bf =0;subR->_bf = 0;subRL->_bf = 0;}else if (bf == 1){//右边插入的subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if(bf==-1){//在左边插入的parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){//左右双旋//平衡因子需要单独调节Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;//先左旋在右旋RotateL(parent->_left);RotateR(parent);//重新计算调节因子if (bf == 0){//当且节点就是新插的subL->_bf = 0;parent->_bf = 0;subLR->_bf = 0;}else if(bf==1){//在当前节点的右边插入的parent->_bf = 0;subL->_bf = -1;subLR->_bf = 0;}else if(bf == -1){subL->_bf = 0;parent->_bf = 1;subLR->_bf = 0;}else{assert(false);}}void RotateR(Node* parent ){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}parent->_bf = subL->_bf = 0;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if(subRL)subRL->_parent = parent;subR->_left = parent;Node* pphead = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (pphead->_parent == parent){pphead->_right = subR;}else{pphead->_left = subR;}subR->_parent = pphead;}subR->_bf = parent->_bf = 0;}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=nullptr;};
}

相关文章:

AVL树的模拟实现

我们上期提到了二叉搜索树&#xff0c;只是简单的讲了一下原理&#xff0c;那么今天我们就讲一下AVL树。 目录 AVL树的概念AVL树的实现AVL树的架构insert插入引用pair对象引进parent指针仅插入数据调节平衡因子情况1&#xff1a;插入在父亲的右边&#xff0c;父亲的平衡因子后…...

php 一个数组中的元素是否在一个字符串中包含

php 一个数组中的元素是否在一个字符串中包含 要检查一个数组中的元素是否在一个字符串中出现&#xff0c;你可以使用strpos()函数。这个函数返回子字符串首次出现的位置索引&#xff0c;如果没有找到&#xff0c;它会返回false。 $array [apple, banana, cherry]; $string …...

conda修改环境名称后,无法安装包,显示no such file

1问题描述 原本创建环境时设置的名字不太合适&#xff0c;但是因为重新创建环境很麻烦&#xff0c;安装很多包。。所以想直接对包名进行修改&#xff0c;本人采用的方式是直接找到conda环境的文件目录&#xff0c;然后修改文件名&#xff0c;简单粗暴。确实修改成功了&#xf…...

linux安装mysql【linux】

linux安装mysql【linux】 前言版权推荐CentOS7.9安装mysql8.0【linux】yum安装rpm安装 最后 前言 2024-5-13 15:52:22 以下内容源自《【linux】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://jss…...

C 语言实例 - 表格形式输出数据

将 1~100 的数据以 10x10 矩阵格式输出。 #include <stdio.h>int main() {int i, j, count;for(i 1; i < 10; i) {for(j i; j <100; j 10 )printf(" %3d", j);printf("\n");}return 0; }运行结果&#xff1a; 1 11 21 31 41 51 61 …...

markdown语法保存

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

数据结构(八)二叉树、哈希查找

文章目录 一、树&#xff08;一&#xff09;概念1. 前序遍历&#xff1a;根左右2. 中序遍历&#xff1a;左根右3. 后序遍历&#xff1a;左右根4. 层序遍历&#xff1a;需要借助队列实现 &#xff08;二&#xff09;代码实现&#xff1a;二叉树1. 结构体定义2. 创建二叉树1. 注意…...

uniApp 创建Android.keystore证书IOS的证书

Android 证书 1、安装JRE环境 可从Oracle官方下载jre安装包&#xff1a;https://www.oracle.com/technetwork/java/javase/downloads/index.html 打开命令行&#xff08;cmd&#xff09;&#xff0c;输入以下命令&#xff1a; //切换工作目录到f:路径 D: //将jre命令添加到…...

怎么藏族翻译中文在线翻译?更好地了解藏族文化

怎么藏族翻译中文在线翻译&#xff1f;着全球化的发展&#xff0c;语言交流的重要性日益凸显。藏族&#xff0c;作为中国的一个古老而神秘的民族&#xff0c;其语言对于很多人来说充满了神秘感。然而&#xff0c;在今天的数字化时代&#xff0c;我们有了更多的工具来打破语言壁…...

模拟集成电路(5)----单级放大器(共栅级)

模拟集成电路(5)----单级放大器&#xff08;共栅级&#xff09; 有一些场合需要一些小的输入电阻&#xff08;电流放大器&#xff09; 大信号分析 − W h e n V i n ≥ V B − V T H ∙ M 1 i s o f f , V o u t V D D − F o r L o w e r V i n I d 1 2 μ n C o x W L ( V…...

学习笔记——数据通信基础——数据通信网络(网络工程师)

网络工程师 网络工程&#xff0c;就是围绕着网络进行的一系列的活动&#xff0c;包括∶网络规划、设计、实施、调试、排错等。网络工程设计的知识领域很宽广&#xff0c;其中路由和交换是计算机网络的基本。 网络工程师∶是在网络工程领域&#xff0c;掌握专业的网络技术&…...

将本地项目上传到 gitee 仓库

1、创建 gitee 仓库 到 gitee 官网&#xff0c;新建仓库 配置新建仓库 完成仓库的创建 项目上传到仓库 上传项目需要安装git git官方下载地址&#xff1a;git下载地址 安装完成&#xff0c;前往本地项目所在文件夹&#xff0c;右击选择 Git Bash Here 刚下载完成需要配置G…...

Django学习

1.pycharm社区版创建django PyCharm社区版如何创建Django项目并运行_pycharm社区版打开django-CSDN博客 2.Django TemplateDoesNotExist: rest_framework 当我们使用djangorestframework框架时&#xff0c;首先下载pip install djangorestframework 参考博文Django Templat…...

说唱程序员

Yo yo yo&#xff0c;这里是代码的战场&#xff0c;程序员的秀场&#xff0c; 键盘敲击声&#xff0c;是我们的节奏响亮。 夜深人静时&#xff0c;我们与Bug正面刚&#xff0c; 调试、优化&#xff0c;每一行代码都得刚强。 我们不懂数理化&#xff0c;只是喜欢瞎搞哈&#xf…...

058.最后一个单词的长度

题意 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 难度 简单 示例 1&#xff1a; 输入&#xff1a;s "Hello World" 输…...

决策树模型-预测用户是否购买某母婴产品

1&#xff0c;场景描述 假设我们是京东的数据分析师&#xff0c;负责分析母婴产品的购买行为。我们想预测用户是否会购买一款新上线的母婴产品。为了进行预测&#xff0c;我们将利用用户的历史购买数据、浏览行为和其他特征&#xff0c;通过决策树模型进行分析&#xff0c;并提…...

工具使用-网络性能测试工具(iperf)-TCP 和 UDP 的吞吐量-包转发率参数的理解

时间戳&#xff1a;2024年5月26日15:18:39 iperf 和 netperf 都是最常用的网络性能测试工具&#xff0c;测试 TCP 和 UDP 的吞吐量。它们都以客户端和服务器通信的方式&#xff0c;测试一段时间内的平均吞吐量。 接下来&#xff0c;我们就以 iperf 为例&#xff0c;看一下 TC…...

什么是JS引擎

JS引擎&#xff08;JavaScript引擎&#xff09;是负责在浏览器或Node.js等环境中解析和执行JavaScript代码的软件组件。它是JavaScript运行时的核心&#xff0c;将JavaScript代码转换为机器语言&#xff0c;使其能够在计算机上执行。 不同的浏览器和运行环境使用不同的JS引擎。…...

前端手写文件上传;使用input实现文件拖动上传

使用input实现文件拖动上传 vue2代码&#xff1a; <template><div><div class"drop-area" dragenter"highlight" dragover"highlight" dragleave"unhighlight" drop"handleDrop"click"handleClick&quo…...

Flutter 中的 PhysicalModel 小部件:全面指南

Flutter 中的 PhysicalModel 小部件&#xff1a;全面指南 Flutter 的 PhysicalModel 小部件提供了一种简单而高效的方式来给应用添加物理效果&#xff0c;如阴影和层次感。它本质上是一个矩形的 Container&#xff0c;带有圆角边框和可选的阴影&#xff0c;能够模仿真实世界中…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...