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

数据结构进阶:二叉搜索树_C++

目录

前言:

一、二叉搜索树

1.1二叉搜索树概念

2.2 二叉搜索树操作

1. 二叉搜索树的插入

1.1、插入过程

1.2、代码实现

2、二叉树的删除

2.1、结点删除情况

2.2、替换删除法

1、替换思路

2、代码实现:

3、二叉搜索树的查找

3.1、查找规则

3.2、代码实现

 二、二叉搜索树的应用

1. K模型

2、KV模型

三、二叉搜索树的性能分析


前言:

1. map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构

2. 二叉搜索树的特性了解,有助于更好的理解map和set的特性

3. 二叉树中部分面试题稍微有点难度,在前面讲解大家不容易接受,且时间长容易忘

4. 有些OJ题使用C语言方式实现比较麻烦,比如有些地方要返回动态开辟的二维数组,非常麻 烦。

一、二叉搜索树

1.1二叉搜索树概念

  • 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

2.2 二叉搜索树操作

树的框架:

//结点
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};
//二叉搜索树的操作
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
private:Node* _root = nullptr;
};

1. 二叉搜索树的插入

1.1、插入过程

插入的具体过程如下:

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

解释:假设我们要插入16、0,那我们就要根据二叉搜索树的特点来进行判断,想要插入16,从根节点开始,如果比根节点大,那么就走右子树,继续比较。如果比根节点小,那么就走左子树继续比较

所以我们的插入功能应该如何写呢?

1.2、代码实现
bool Insert(const K& key)
{
//判断二叉树是否为空if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;
//进行插入while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}
//找到插入位置,并新开辟一个结点cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

细节解释:

1、我们最开始需要判断二叉树是否为空,如果不判断,cur就为空,就不会进入循环判断的同时,之后parent指向结点为野指针,不安全

2、在我们循环判断时,需要记录cur的父节点,cur最终找到的是插入位置,如果我们想成功插入,那么就需要由该位置的父节点进行链接

2、二叉树的删除

2.1、结点删除情况

  首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情 况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

情况a:直接删除

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点 中,再来处理该结点的删除问题--替换法删除

情况a、b、c都好理解,情况a直接删除即可,情况bc是让被删除结点的孩子去顶替它的位置即可

难的是情况d,当它有两个孩子时应该怎么去选择,我们所用到的替换法删除又是什么呢?

2.2、替换删除法
1、替换思路

  我们所找的去替换被删除结点的值最重要的是能在该位置站得住脚---就是要比左孩子大,又要比右孩子小

  那哪些结点能站得住脚呢?

  是被删除结点的左子树的最大结点(右结点)或者右子树的最小结点(左结点)

  怎么理解?我们知道,父结点的左子树上的值,都要比父结点小,父结点的右子树上的值,都要比父结点大。我们再拿父结点的左孩子来说,同理比它小的值也同样会走到它的左子树上,比它大的值也同样会走到它的右子树上。

  那么我们就可以明白:被删除结点的左子树的最大右节或者右子树的最小左结点一定会比被删除结点的左孩子大,又比其右孩子小

  这也是搜索二叉数的特征:一棵树的左子树的最右节点是左子树的最大结点,一棵树的右子树的最左结点是右子树的最小结点

  另外需要注意的是,我们被删除结点的左子树的最大结点(右结点)或者右子树的最小结点(左结点是可以直接删除的,我们在上面已经分析过删除情况。

2、代码实现:

情况b、c再删除时又会遇到的情况:

1、被删除结点的左子树为空

  假如我们要删除结点3,且被删除结点的左子树为空,我们被删除结点的父结点就要链接被删除结点的右子树

2、被删除结点的右子树为空

 假如我们要删除结点3,且被删除结点的右子树为空,我们被删除结点的父结点就要链接被删除结点的左子树

情况d:

我们知道:

1、找被删除结点的左子树的最大右结点或者右子树的最左小结点(这个在后面的理解很重要!

2、一棵树的左子树的最右节点是左子树的最大结点,一棵树的右子树的最左结点是右子树的最小结点

  所以我们需要先找到一个能站得住脚的结点,我们就拿右子树的最小结点举例,将它命名为rightMin

  我们找到rightMin再去将被删除结点与rightMin结点的值key交换,并且在找rightMin时再用一个中间变量rightMinParent去记录rightMin的父结点,最终在交换完key后我们需要删除交换后的rightMin就需要由父结点来链接。

  此时又会遇到两种情况:

1、rightMin如果是父结点rightMinParent的左结点,我们就需要让rightMinParent去链接rightMin的右节点(如果存在即链接,不存在即为空)

2、1、rightMin如果是父结点rightMinParent的右结点,我们就需要让rightMinParent去链接rightMin的右节点(如果存在即链接,不存在即为空)

删除函数代码:

bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;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(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;}

3、二叉搜索树的查找

3.1、查找规则

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。

b、最多查找高度次,走到空,还没找到,这个值不存在

3.2、代码实现
bool Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;
}

 二、二叉搜索树的应用

1. K模型

  K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到 的值。

  比如:给一个单词word,判断该单词是否拼写正确。

  具体方式如下:

  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

这个我们需要一个词库,所以我们在这里先不做这个实现,我们用KV模型来实现

2、KV模型

每一个关键码key,都有与之对应的值Value,即的键值对。该种方 式在现实生活中非常常见:

  • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文就构成一种键值对;
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出 现次数就是就构成一种键值对。

我们先将KV模型代码写出来:

namespace key_value
{template<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;// pair<K, V> _kv;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}};template<class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:// logNbool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return cur;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;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(parent == nullptr)if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//if(parent == nullptr)if (cur == _root){_root = cur->_left;}else{// 右为空,父亲指向我的左if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 左右都不为空,替换法删除// // 查找右子树的最左节点替代删除Node* rightMinParent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}swap(cur->_key, rightMin->_key);if (rightMinParent->_left == rightMin)rightMinParent->_left = rightMin->_right;elserightMinParent->_right = rightMin->_right;delete rightMin;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};

这也只是在我们原先实现的基础上做改动。 

示例一:翻译单词

void TestBSTree2()
{BSTree<string, string> dict;dict.Insert("string", "字符串");dict.Insert("left", "左边");dict.Insert("insert", "插入");//...string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词,请重新输入" << endl;}}
}

示例二:计数

void TestBSTree3(){// 统计次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓"};BSTree<string, int> countTree;for (const auto& str : arr){auto ret = countTree.Find(str);if (ret == nullptr){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();}
}

三、二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:$log_2 N$

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:$\frac{N}{2}$ 

相关文章:

数据结构进阶:二叉搜索树_C++

目录 前言&#xff1a; 一、二叉搜索树 1.1二叉搜索树概念 2.2 二叉搜索树操作 1. 二叉搜索树的插入 1.1、插入过程 1.2、代码实现 2、二叉树的删除 2.1、结点删除情况 2.2、替换删除法 1、替换思路 2、代码实现&#xff1a; 3、二叉搜索树的查找 3.1、查找规则 …...

uni-app之旅-day04-商品列表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言6. 商品列表6.0 创建 goodslist 分支6.1 定义请求参数对象6.2 获取商品列表数据6.3 渲染商品列表结构6.4 把商品 item 项封装为自定义组件在 goods_detail 组件…...

单元测试的定义

概念 单元测试是对软件中的最小可测试单元进行检查和验证的过程。在面向对象编程中&#xff0c;最小可测试单元通常是一个方法或者一个类。它的目的是隔离各个部分的代码&#xff0c;确保每个单元都能按预期工作&#xff0c;从而提高软件的质量和可维护性。重要性 发现早期错误…...

C语言从头学66—学习头文件 <stdio.h>(二)

关于可变参数&#xff0c;我们曾经在《C语言从头学27》中接触过&#xff0c;下面学习能够接收可变参数作为 参数的几个函数。 一、printf函数的能够接收可变参数的变体函数&#xff1a; 1、函数vprintf() 功能&#xff1a;按照给定格式&#xff0c;将可变参数中的内容输…...

python静默活体检测接口集成-人脸识别API-人脸认证

静默活体检测是一种不需要用户主动配合&#xff08;如眨眼、点头等&#xff09;的活体检测技术&#xff0c;通常通过摄像头采集用户的人脸图像&#xff0c;结合计算机视觉与AI算法来判断用户是否为真人。这项技术被广泛应用于身份验证、金融交易安全、智能门禁等领域。 确定接口…...

Ubuntu安装nvidia显卡驱动

一、安装依赖 1、更新 sudo apt update sudo apt upgrade -y 2、基础工具 sudo apt install -y build-essential cmake 图形界面相关 sudo apt install -y lightdm 注:在弹出对话框选择"lightdm" 下载nvidia驱动&#xff1a; 进入如下网址&#xff1a;http…...

vulnhub-Web Developer 1靶机

vulnhub&#xff1a;Web Developer: 1 ~ VulnHub 导入靶机&#xff0c;放在kali同网段&#xff0c;扫描 靶机在192.168.114.129&#xff0c;扫描端口 有网站服务&#xff0c;访问 没什么东西&#xff0c;扫目录 真不少&#xff0c;访问一下&#xff0c;也只是一些普通的Wordpr…...

安全帽头盔检测数据集 3类 12000张 安全帽数据集 voc yolo

安全帽头盔检测数据集 3类 12000张 安全帽数据集 voc yolo 安全帽头盔检测数据集介绍 数据集名称 安全帽头盔检测数据集 (Safety Helmet and Person Detection Dataset) 数据集概述 该数据集专为训练和评估基于YOLO系列目标检测模型&#xff08;包括YOLOv5、YOLOv6、YOLOv7…...

保姆级教程 | Adobe Illustrator调整颜色透明度

背景 由于课题需要&#xff0c;现需要在Adobe Illustrator里修改部分色块的颜色及透明度 步骤 1. 打开Adobe Illustrator软件&#xff0c;打开或创建一个AI文件&#xff1a; 2. 绘制一个色块&#xff1a; 3. 单击需要调整透明度的对象将其选中 4. 调整颜色的透明度&#xf…...

深入解读DeepSort目标跟踪算法:从状态预测到运动模型

在目标跟踪领域中&#xff0c;DeepSort&#xff08;Deep Simple Online and Realtime Tracking&#xff09;是一种广泛应用且高效的跟踪算法&#xff0c;它结合了深度学习与经典目标跟踪方法&#xff0c;为视觉跟踪任务提供了强大的解决方案。本文将深入探讨DeepSort中的关键概…...

24-10-2-读书笔记(二十二)-《契诃夫文集》(一)上([俄] 契诃夫 [译] 汝龙)啊!真想生活。

文章目录 《契诃夫文集》&#xff08;一&#xff09;上&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;早期生活——塔甘罗格&#xff08;人物家庭简介&#xff09;学生时期——莫斯科&#xff08;写作与学习&#xff09;流浪时期——哈萨林&#xff08;游历与流浪&#xff09…...

【2024】前端学习笔记14-JavaScript常用数据类型-变量常量

学习笔记 1.JavaScript常用数据类型1.1.Number&#xff08;数字&#xff09;1.2.String&#xff08;字符串&#xff09;1.3.Boolean&#xff08;布尔值&#xff09;1.4.Null&#xff08;空值&#xff09;1.5.Undefined&#xff08;未定义&#xff09;1.6.Object&#xff08;对象…...

Leecode热题100-48.旋转图像

给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出…...

重学SpringBoot3-集成Redis(二)之注解驱动

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Redis&#xff08;二&#xff09;之注解驱动 1. 为什么选择 Redis 作为缓存&#xff1f;2. 如何在 Spring Boot 中启用 Redis 缓存&#xff1f;2.1 …...

【React】入门Day04 —— 项目搭建及登录与表单校验、token 管理、路由鉴权实现

项目搭建 创建项目 # 使用npx创建项目 npx create-react-app my-react-app # 进入项目目录 cd my-react-app # 创建项目目录结构 mkdir -p src/{apis,assets,components,pages,store,utils} touch src/{App.js,index.css,index.js} 使用npx create-react-app创建项目&#xff0…...

CMake 属性之目录属性

【写在前面】 CMake 的目录属性是指在特定目录&#xff08;及其子目录&#xff09;范围内有效的设置。 这些属性不同于全局变量或目标&#xff08;Target&#xff09;属性&#xff0c;它们提供了一种机制&#xff0c;允许开发者为项目中的不同部分定义不同的构建行为。 通过目录…...

ChatGPT:引领人工智能新潮流!

一、ChatGPT 是什么&#xff1f; 1. ChatGPT 的强大功能和广泛应用。 ChatGPT 作为一款先进的 AI 语言模型&#xff0c;拥有众多强大功能。它可以进行文本生成、文本分类、情感分析、机器翻译等多种自然语言处理任务。同时&#xff0c;ChatGPT 还能进行对话式交互&#xff0c;…...

【银河麒麟高级服务器操作系统】安全配置基线相关分析全过程及解决方案

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 服务器环境以及配置 【机型】物理机或虚机 【…...

用Python实现图片转ASCII艺术:图像处理与字符艺术的完美结合

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 ASCII艺术是一种通过字符来表现图像的艺术形式,最早用于早期计算机显示器,它仅支持字符显示。如今,尽管图像分辨率和显示技术得到了极大的提升,ASCII艺术作为一种复古而别具一格的图像表现形式,仍然受到许多…...

大数据-162 Apache Kylin 全量增量Cube的构建 Segment 超详细记录 多图

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...