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

算法实战:亲自写红黑树之五 删除erase的平衡

        本文承接自:

        算法实战:亲自写红黑树之一-CSDN博客

        算法实战:亲自写红黑树之二 完整代码-CSDN博客

        算法实战:亲自写红黑树之三 算法详解-CSDN博客

        算法实战:亲自写红黑树之四 插入insert的平衡-CSDN博客

目录

一、入口

二、删除

三、转换中间节点(左右子树都存在)

四、简单情形

五、复杂情形


一、入口

        有几个不同参数的删除入口:

	bool erase(const_iterator it){T_COMP comp;return erase(it, comp);}//删除并指向下一个iterator& DeleteAndMoveNext(iterator& it){if (end() == it)return it;iterator& ret = it;iterator tmp = ret;++ret;erase(tmp);return ret;}bool erase(const_iterator it, T_COMP& comp){if (it.handle < 0)return true;//DEBUG_LOG<<"要删除节点"<<show(it.handle)<<endi;bool ret = _erase(it.handle);return ret;}bool erase(T_DATA const& data){T_COMP comp;return erase(data, comp);}bool erase(T_DATA const& data, T_COMP& comp){const_iterator it = find(data, comp);if (it.handle < 0)return true;return erase(it, comp);}

        最终都进入_erase(h),直接删除一个节点。

        在到达这一步之前要先找到要删除的节点,这个属于二叉树的标准算法,很简单。

二、删除

        删除的控制代码:

	//删除bool _erase(T_SHM_SIZE position){string  report;//DEBUG_LOG<<"开始删除...."<<Report(report) <<endi;bool vLeft;//删除的节点是左子节点bool vRed;//删除的节点是红色T_SHM_SIZE p = TREE_NODE::at(position).hParent;//父节点T_SHM_SIZE u;//替换节点bool uRed;//替换节点是红色(-1为黑色)if (TREE_NODE::at(position).hLeft != -1 && TREE_NODE::at(position).hRight != -1){debug_log << "左右都存在,替换为左子树最大值" << endi;if (!__erase_change_middle(position, vLeft, vRed, u, uRed, p))return false;debug();return _erase(position);}else if (-1 == TREE_NODE::at(position).hLeft && -1 == TREE_NODE::at(position).hRight){vLeft = TREE_NODE::at(position).isLeft();vRed = TREE_NODE::at(position).bColorRed;u = -1;uRed = false;if (-1 == p){debug_log << "最后一个节点" << endi;tree_head->hHead = -1;vRed = true;//阻止后面继续处理,删除红色无需额外处理}else{debug_log << "叶子节点" << endi;if (TREE_NODE::at(position).isLeft())TREE_NODE::at(p).hLeft = -1;else TREE_NODE::at(p).hRight = -1;}}else{vLeft = TREE_NODE::at(position).isLeft();vRed = TREE_NODE::at(position).bColorRed;if (-1 != TREE_NODE::at(position).hLeft){debug_log << "左子树存在" << endi;u = TREE_NODE::at(position).hLeft;}else{debug_log << "右子树存在" << endi;u = TREE_NODE::at(position).hRight;}if (-1 == p){debug_log << "根节点" << endi;tree_head->hHead = u;}else{if (vLeft)TREE_NODE::at(p).hLeft = u;else TREE_NODE::at(p).hRight = u;}TREE_NODE::at(u).hParent = p;uRed = TREE_NODE::at(u).bColorRed;p = TREE_NODE::at(u).hParent;}bool ret = true;if (vRed){debug_log << "删除红色节点,不用调整" << endi;}else if (!vRed && uRed){debug_log << "删除黑色节点,替换节点红色改为黑色" << endi;TREE_NODE::at(u).bColorRed = false;}else{debug_log << "删除双黑节点================================================" << endi;ret = _RB_erase_Balance(p, u);}debug();if (ret)__erase_node(position);if (-1 != tree_head->hHead)TREE_NODE::at(tree_head->hHead).bColorRed = false;return ret;}

        这个函数的逻辑分为两部分:

  1. 分析要删除的节点的分支情况,将左右节点都存在的情形改为删除叶子数据节点(不是红黑树意义的叶子节点,指一般意义的叶子节点),然后确定删除节点和替换节点。
  2. 根据删除节点和替换节点的颜色进行处理,黑节点减少则需要平衡。

三、转换中间节点(左右子树都存在)

        很容易把中间节点替换掉,跟左子树最大值或者右子树最小值交换即可。

        本代码用__erase_change_middle(h)函数做这件事,将颜色也做了交换(逻辑上相当于不改变结构数据,只交换用户数据,但是我们的用户数据一般都很大,所以反过来操作,改变了除用户数据以外的所有结构数据),执行完毕后红黑树结构仍然正确,删除节点变成了最多只有一个子树的节点。

	//删除中间节点,将中间节点替换为左子树最大值(数据不动,改变树结构)bool __erase_change_middle(T_SHM_SIZE const position, bool& vLeft, bool& vRed, T_SHM_SIZE& u, bool& uRed, T_SHM_SIZE& p){string tmp;T_SHM_SIZE new_position = TREE_NODE::at(position).hLeft;if (-1 != new_position){while (-1 != TREE_NODE::at(new_position).hRight){new_position = TREE_NODE::at(new_position).hRight;}}debug_log << "position " << position << " new_position " << new_position << endi;debug_log << "position " << position << TREE_NODE::at(position).toString(tmp) << endi;debug_log << "new_position " << new_position << TREE_NODE::at(new_position).toString(tmp) << endi;vLeft = false;vRed = TREE_NODE::at(new_position).bColorRed;p = TREE_NODE::at(new_position).hParent;debug_log << "p " << TREE_NODE::at(p).toString(tmp) << endi;if (p != position){debug_log << "非直接对调 p " << TREE_NODE::at(p).toString(tmp) << ende;TREE_NODE t;t._CopyWithoutData(TREE_NODE::at(new_position));TREE_NODE::at(new_position)._CopyWithoutData(TREE_NODE::at(position));TREE_NODE::at(position)._CopyWithoutData(t);debug_log << "position " << TREE_NODE::at(position).toString(tmp) << endi;debug_log << "new_position " << TREE_NODE::at(new_position).toString(tmp) << endi;//注意,这里无法用isLeft来判断,因为父节点的hLeft和hRight是旧数据if (TREE_NODE::at(new_position).hParent != -1){if (TREE_NODE::at(TREE_NODE::at(new_position).hParent).hLeft == position)TREE_NODE::at(TREE_NODE::at(new_position).hParent).hLeft = new_position;else TREE_NODE::at(TREE_NODE::at(new_position).hParent).hRight = new_position;}if (TREE_NODE::at(position).hParent != -1){if (TREE_NODE::at(TREE_NODE::at(position).hParent).hLeft==new_position)TREE_NODE::at(TREE_NODE::at(position).hParent).hLeft = position;else TREE_NODE::at(TREE_NODE::at(position).hParent).hRight = position;}if (TREE_NODE::at(new_position).hLeft != -1)TREE_NODE::at(TREE_NODE::at(new_position).hLeft).hParent = new_position;if (TREE_NODE::at(position).hLeft != -1)TREE_NODE::at(TREE_NODE::at(position).hLeft).hParent = position;if (TREE_NODE::at(new_position).hRight != -1)TREE_NODE::at(TREE_NODE::at(new_position).hRight).hParent = new_position;if (TREE_NODE::at(position).hRight != -1)TREE_NODE::at(TREE_NODE::at(position).hRight).hParent = position;}else{debug_log << "直接对调 p " << TREE_NODE::at(p).toString(tmp) << endi;TREE_NODE t;t._CopyWithoutData(TREE_NODE::at(new_position));TREE_NODE::at(new_position)._CopyWithoutData(TREE_NODE::at(position));TREE_NODE::at(position)._CopyWithoutData(t);debug_log << "position " << TREE_NODE::at(position).toString(tmp) << endi;debug_log << "new_position " << TREE_NODE::at(new_position).toString(tmp) << endi;//注意,这里无法用isLeft来判断,因为父节点的hLeft和hRight是旧数据if (TREE_NODE::at(new_position).hParent != -1){if (TREE_NODE::at(TREE_NODE::at(new_position).hParent).hLeft == position)TREE_NODE::at(TREE_NODE::at(new_position).hParent).hLeft = new_position;else TREE_NODE::at(TREE_NODE::at(new_position).hParent).hRight = new_position;}TREE_NODE::at(position).hParent = new_position;TREE_NODE::at(new_position).hLeft=position;if (TREE_NODE::at(position).hLeft != -1)TREE_NODE::at(TREE_NODE::at(position).hLeft).hParent = position;if (TREE_NODE::at(new_position).hRight != -1)TREE_NODE::at(TREE_NODE::at(new_position).hRight).hParent = new_position;if (TREE_NODE::at(position).hRight != -1)TREE_NODE::at(TREE_NODE::at(position).hRight).hParent = position;}//如果是根节点if (-1 == TREE_NODE::at(new_position).hParent){TREE_NODE::at(new_position).bColorRed = false;tree_head->hHead = new_position;}debug_log << "position " << TREE_NODE::at(position).toString(tmp) << endi;debug_log << "new_position " << TREE_NODE::at(new_position).toString(tmp) << endi;debug();return true;}

        这个代码不复杂,就是累。

四、简单情形

        现在要删除的节点要么没有子树,要么只有一个子树(说起来是子树,其实最多就是一个红节点,否则必定违反红黑树规则),只需要把子树接上去就可以了,然后把要删除的节点扔掉。

        这里我们还是用一般二叉树的概念来理解,以下说的节点都是数据节点,不包括空节点。

  1.         如果删除节点是红节点,不管下面有没有子树,不用平衡,因为不影响规则。
  2.         如果删除节点是黑节点而下面有一个红节点(前面已经说了,没有更多可能性),把这个红节点变成黑节点就行了。
  3.         如果删除节点是黑节点而下面没有可供变色的红节点(也就是没有子节点,红黑树规则不允许下面有黑色数据节点,因为只有一个子树,另外一边深度是0),这就导致红黑树规则被破坏,很多文章称这种情况为“双黑”,其实就是黑节点少了。

        第三种情形就被称为“复杂情形”,需要调用_RB_erase_Balance()来平衡。

五、复杂情形

        前面已经说了,所谓复杂情形就是少了一个黑节点,现在涉及到这么几个节点:

        u 替换节点,也就是出问题的节点,这个节点的高度比兄弟少一。这个节点可能是是空节点。

        p 替换节点的父节点

        s 替换节点的兄弟节点

        由于红黑树的规则限制,最初进入这种情形只可能是被删除节点下没有子节点,所以一定是p的一边为空另一边黑高为1。递归之后黑高少一的节点就有了各种可能,可能是红色或黑色,可能有或没有子节点。

        重新平衡有三种策略:

  1. 如果能把p改成黑同时让s黑高减一,这样就恢复平衡了,这要求p为红,p为红则s必为黑,如果s的子节点均为黑(此时s的子节点必定都是空节点,否则违反红黑树规则),只需要将p和s颜色交换就可以了。
  2. 另一边如果有红色节点,能挪过来改成黑色,平衡就完成了。按照红色节点的位置不同,需要有不同的挪法,虽然繁琐但并不困难。
  3. 另一边如果有红色节点,挪过来也不能平衡,想办法把问题往上移,递归处理。
psss处理
1黑黑/不存在p、s交换颜色,问题上移,递归
2红红挪一个红改成黑
3黑黑-[红红红红]旋转p,p、s交换颜色,p还是少1,但是变色了,重新处理p,递归
4红红挪一个红改成黑
5黑黑/不存在p、s交换颜色

        上表是所有情形,“不存在”指的是空节点,也是黑色。ss指的是s的子节点。“红红”也可能是只有一个红,因为处理时只要有一个红就不用管另一个。第三种情形的ss是双黑,后面还可能有红色子节点,不过与算法无关。

        为什么只有五种组合而不是八种?因为不允许连续红,另外三种不可能存在。

        上面第二种和第四种处理方式相同,因为并不关心p的颜色,通过挪一个改成黑,下面就平衡了。

        代码的实际处理逻辑是从ss开始判断的,先分成ss至少有一个红和没有红,然后没有红的分三种,有红的分四种(所谓四种不过是四种结构的挪法而已)。

        代码如下:

	//删除时做平衡,u可能是空节点bool _RB_erase_Balance(T_SHM_SIZE p, T_SHM_SIZE u){if (-1 == p){debug_log << "已经到顶,结束" << endi;return true;}string tmp;debug_log << "p " << TREE_NODE::at(p).toString(tmp) << endi;if (u != -1)debug_log << "u " << TREE_NODE::at(u).toString(tmp) << endi;else debug_log << "u -1" << endi;bool isL = (u == TREE_NODE::at(p).hRight);//兄弟节点是否是左节点T_SHM_SIZE s = (isL ? TREE_NODE::at(p).hLeft : TREE_NODE::at(p).hRight);T_SHM_SIZE r = -1;//s的红色子节点bool is_rL;//s的红色子节点是否是左节点debug_log << "平衡:p " << p << " u " << u << " s " << s << " TREE_NODE::at(s).hRight " << TREE_NODE::at(s).hRight << endi;if (TREE_NODE::at(s).hRight != -1 && TREE_NODE::at(TREE_NODE::at(s).hRight).bColorRed){r = TREE_NODE::at(s).hRight;is_rL = false;}else if (TREE_NODE::at(s).hLeft != -1 && TREE_NODE::at(TREE_NODE::at(s).hLeft).bColorRed){r = TREE_NODE::at(s).hLeft;is_rL = true;}else{r = -1;is_rL = false;//无意义}debug_log << "p " << p << " u " << u << " s " << s << " r(-1表示双黑) " << r << endi;debug();if (-1 == r){debug_log << "s子节点均为黑" << endi;if (TREE_NODE::at(p).bColorRed){debug_log << "p为红,s必为黑 s改为红p改为黑 结束" << endi;//s子节点均为黑,p改为黑,所以s改为红是安全的,不会造成双红TREE_NODE::at(s).bColorRed = true;TREE_NODE::at(p).bColorRed = false;debug();}else{debug_log << "p为黑" << endi;if (!TREE_NODE::at(s).bColorRed){debug_log << "s为黑 s改为红平衡下层并向上递归" << endi;//p的左右分支均少1,所以p成为新的双黑节点//置s为红,p为新的u,递归TREE_NODE::at(s).bColorRed = true;debug();return _RB_erase_Balance(TREE_NODE::at(p).hParent, p);}else{debug_log << "s为红 " << TREE_NODE::at(s).toString(tmp) << endi;debug_log << TREE_NODE::at(TREE_NODE::at(s).hParent).toString(tmp) << endi;if (TREE_NODE::at(s).isLeft()){debug_log << "s为左 " << s << endi;//右旋p_RRotate(p);}else{debug_log << "s为右" << endi;_LRotate(p);}//p和s变色TREE_NODE::at(s).bColorRed = false;TREE_NODE::at(p).bColorRed = true;debug();//此时深度情况不变,但p变成了红,重新对p和u做平衡处理return _RB_erase_Balance(p, u);}}}else{if (isL && is_rL){debug_log << "LL" << ende;TREE_NODE::at(r).bColorRed = TREE_NODE::at(s).bColorRed;TREE_NODE::at(s).bColorRed = TREE_NODE::at(p).bColorRed;_RRotate(p);TREE_NODE::at(p).bColorRed = false;}else if (isL && !is_rL){debug_log << "LR" << endi;debug();TREE_NODE::at(r).bColorRed = TREE_NODE::at(p).bColorRed;debug();_LRotate(s);debug();_RRotate(p);debug();TREE_NODE::at(p).bColorRed = false;debug();}else if (!isL && is_rL){debug_log << "RL------------------------" << endi;debug();TREE_NODE::at(r).bColorRed = TREE_NODE::at(p).bColorRed;debug();_RRotate(s);debug();string tmp;debug_log << "p " << TREE_NODE::at(p).toString(tmp) << endi;debug_log << "s " << TREE_NODE::at(p).toString(tmp) << endi;_LRotate(p);debug();TREE_NODE::at(p).bColorRed = false;debug();}else if (!isL && !is_rL){debug_log << "RR" << endi;TREE_NODE::at(r).bColorRed = TREE_NODE::at(s).bColorRed;TREE_NODE::at(s).bColorRed = TREE_NODE::at(p).bColorRed;_LRotate(p);TREE_NODE::at(p).bColorRed = false;}else{thelog << "非预期的状态" << ende;return false;}}return true;}

        我感觉吧,我应该刚好解决了一部分人的困惑。

(这里是结束,而且是整个系列的结束)

相关文章:

算法实战:亲自写红黑树之五 删除erase的平衡

本文承接自&#xff1a; 算法实战&#xff1a;亲自写红黑树之一-CSDN博客 算法实战&#xff1a;亲自写红黑树之二 完整代码-CSDN博客 算法实战&#xff1a;亲自写红黑树之三 算法详解-CSDN博客 算法实战&#xff1a;亲自写红黑树之四 插入insert的平衡-CSDN博客 目录 一、入口…...

春秋云境靶场CVE-2021-41402漏洞复现(任意代码执行漏洞)

文章目录 前言一、CVE-2021-41402描述二、CVE-2021-41402漏洞复现1、信息收集1、方法一弱口令bp爆破2、方法二7kb扫路径&#xff0c;后弱口令爆破 2、找可能可以进行任意php代码执行的地方3、漏洞利用找flag 总结 前言 此文章只用于学习和反思巩固渗透测试知识&#xff0c;禁止…...

12 Go的接口

概述 在上一节的内容中&#xff0c;我们介绍了Go的作用域&#xff0c;包括&#xff1a;局部作用域、全局作用域、命名空间作用域等。在本节中&#xff0c;我们将介绍Go的接口。Go语言中的接口是一种类型&#xff0c;它定义了一组函数的集合。接口是一种抽象的描述&#xff0c;它…...

Python编程-----并行处理应用程序

目录 一.进程 二.线程 三.Python标准库中并行处理的相关模块 Threading模块 &#xff08;1&#xff09;使用Thread对象创建线程 &#xff08;2&#xff09;自定义派生于Thread的对象 &#xff08;3&#xff09;线程加入join() (4)用户线程和daemon线程 (5)Timer线程 线…...

kubernetes集群编排——istio

官网&#xff1a;https://istio.io/latest/zh/about/service-mesh/ 部署 [rootk8s2 ~]# tar zxf istio-1.19.3-linux-amd64.tar.gz [rootk8s2 ~]# cd istio-1.19.3/[rootk8s2 istio-1.19.3]# export PATH$PWD/bin:$PATH demo专为测试准备的功能集合 [rootk8s2 istio-1.19.3]# i…...

mfc140u.dll丢失的解决方法,以及mfc140u.dll解决方法的优缺点

在使用电脑过程中&#xff0c;有时会遇到一些与动态链接库文件&#xff08;DLL&#xff09;相关的错误。其中&#xff0c;mfc140u.dll丢失的错误是较为常见的一种。当这个关键的mfc140u.dll文件丢失或损坏时&#xff0c;可能会导致某些应用程序无法正常运行。在本文中&#xff…...

2源码安装网络协议

2.2源码安装/网络协议 一、源码包应用场景 有时我们所用的内核版本太旧&#xff0c;系统自带的库&#xff08;如libstdc.so.6&#xff09;版本低或者依赖的其他软件版 本较低&#xff0c;导致无法安装目标软件。 软件/库其实是对机器汇编指令集的封装&#xff0c;在X86体系下…...

未来服务器操作系统的趋势与展望

摘要&#xff1a; 随着云计算、大数据和人工智能不断的发展&#xff0c;服务器操作系统也需要随之进行新一轮的升级。本文通过分析当前服务器操作系统的现状&#xff0c;探讨了未来服务器操作系统的趋势和展望&#xff0c;并针对一些关键问题提出了解决方案。 一、引言 服务器…...

VB.net WebBrowser网页元素抓取分析方法

在用WebBrowser编程实现网页操作自动化时&#xff0c;常要分析网页Html&#xff0c;例如网页在加载数据时&#xff0c;常会显示“系统处理中&#xff0c;请稍候..”&#xff0c;我们需要在数据加载完成后才能继续下一步操作&#xff0c;如何抓取这个信息的网页html元素变化&…...

自建ES6.2.4切阿里云商业版ES(7.10)整体方案

一、切换目的&阿里云商业版ES版本选择 1.1 升级切换阿里云商业版7.10目的 自建的Elasticsearch服务运维难度高,操作复杂,需要手动调整资源,遇到性能瓶颈时优化难度相对云上Elasticsearch较大。使用阿里云提供的ES服务,提高系统稳定性使用云服务es,易于备份,数据恢复…...

Vue实现封装自定义指令

目录 一、什么是自定义指令&#xff1f; 二、自定义指令的使用 Vue中的自定义指令使用Vue.directive函数进行定义。该函数接受两个参数&#xff0c;第一个是指令名称&#xff0c;第二个是指令选项对象。 上述代码中&#xff0c;我们定义了一个名为my-directive的自定义指令…...

<MySQL> 查询数据进阶操作 -- 聚合查询

目录 一、聚合查询概述 二、聚合函数查询 2.1 常用函数 2.2 使用函数演示 2.3 聚合函数参数为*或列名的查询区别 2.4 字符串不能参与数学运算 2.5 具有误导性的结果集 三、分组查询 group by 四、分组后条件表达式查询 五、MySQL 中各个关键字的执行顺序 一、聚合查询…...

arm开发板

一个简单的hello world程序 minicom用来和开发板之间交互并且可以向开发板传输文件。打印hello world字符串。在linux虚拟机上编译我的代码&#xff0c;使用的交叉编译工具是arm-linux-gnueabihf-gcc (hard float) 可以使用 readelf -h libc.so.6 查看开发板是不是&#xff08…...

nodejs+vue教室管理系统的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

用户 用户管理&#xff1a;查看&#xff0c;修改自己的个人信息 教室预约&#xff1a;可以预约今天明天的教室&#xff0c;按着时间段预约&#xff08;可多选&#xff09;&#xff0c;如果当前时间超过预约时间段不能预约该时间段的教室 预约教室的时候要有个预约用途&#xff…...

rabbitMQ的Topic模式的生产者与消费者使用案例

topic模式 RoutingKey 按照英文单词点号多拼接规则填充。其中消费者匹配规则时候 * 代表一个单词&#xff0c;#表示多个单词 消费者C1的RoutingKey 规则按照*.orange.* 匹配 绑定队列Q1 package com.esint.rabbitmq.work05;import com.esint.rabbitmq.RabbitMQUtils; import …...

【软考篇】中级软件设计师 第五部分

中级软件设计师 第五部分 三十六. 下午题变动题型参考答案例题一 如何保持数据流图平衡例题二 结构化语言例题三 关系模式例题四 用例关系内涵例题五 观察者模式 三十七&#xff1a;下午题第四题往年算法部分参考答案 读前须知&#xff1a; 【软考篇】中级软件设计师 学前须知 …...

论文阅读——RetNet

transformer的问题&#xff1a;计算量大&#xff0c;占用内存大&#xff0c;不好部署。 所以大家在找能解决办法&#xff0c;既能和transformer表现一样好&#xff0c;又能在推理阶段计算复杂度很低。 这些方法大概分类三类&#xff1a;一是代替transformer非线性注意力机制的…...

【Proteus仿真】【51单片机】锂电池管理系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用LCD1602显示模块、DS18B20温度传感器、PCF8691 ADC模块、按键、LED蜂鸣器模块等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示温度…...

【工具使用-VScode】设置 VSCode 的自动保存功能

要设置 VSCode 的自动保存功能&#xff0c;请按照以下步骤进行操作&#xff1a; 打开 VSCode 编辑器。在顶部菜单中选择 “文件&#xff08;File&#xff09;”。选择 “首选项&#xff08;Preferences&#xff09;”。在下拉菜单中选择 “设置&#xff08;Settings&#xff0…...

常用Git命令记录

持续补充… git add&#xff1a;提交到暂存区git remote add <remote_name> <remote_url> : 添加一个新的远程仓库。指定一个远程仓库的名称和 URL&#xff0c;将其添加到当前仓库中。git commit&#xff1a;暂存区提交到本地仓库&#xff1b;-m&#xff1a;添加日…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...