R-Tree的简单介绍

一、R-Tree简介

R-Tree,全称是“Real Tree”,是一种专门为处理多维空间数据(尤其是二维空间数据,如地理坐标)设计的树形数据结构。

简单来说,它就像是一个特殊的目录,将空间数据按照它们的位置和大小进行分组,存储在一系列的矩形区域(称为“节点”)中。每个节点不仅包含空间数据本身,还包含一个能完全覆盖其内部所有数据的矩形边界。这样,当我们查询某个特定区域内的数据时,R-Tree可以通过比较查询区域与节点矩形边界的关系,快速筛选出可能包含所需数据的节点,再逐层深入到更细粒度的节点进行精确查找,大大减少了需要检查的数据量。

二、R-Tree的结构特点

1. 分层结构:R-Tree就像一棵倒立的树,根节点在最上方,叶节点在最下方。根节点包含最少的数据,而叶节点包含最多的数据。每个非叶节点(内部节点)代表一个矩形区域,其子节点(可能是内部节点或叶节点)的矩形区域完全被父节点的矩形区域所覆盖。

2. 节点填充因子:为了保证查询效率,R-Tree通常会限制每个节点容纳的数据数量或其矩形区域的面积。这个比例被称为“填充因子”。合理的填充因子既能减少查询时需要检查的节点数量,又能避免树的高度过高,导致查询效率下降。

3. 超矩形划分:R-Tree的核心在于如何将空间数据划分为大小适中、相互覆盖关系合理的超矩形。常见的划分方法有最小边界矩形(MBR,Minimum Bounding Rectangle)、最小面积包围盒(Min-Area Bounding Box)等。

三、R-Tree的底层实现

1. 插入操作:当向R-Tree插入一个新数据时,需要找到一个合适的叶节点来存放。首先从根节点开始,沿着树向下遍历,直到找到一个与新数据边界有重叠的叶节点。然后,检查该节点是否已满(根据填充因子判断)。如果不满,直接将新数据加入;如果已满,则需要对该节点进行分裂,形成两个新的节点,将部分数据和新数据均匀分配到这两个节点中,并向上更新父节点的超矩形边界。如果父节点也因此满员,继续分裂和更新的过程,直至到达根节点。如果根节点也需要分裂,那么就创建一个新的根节点,将原来的根节点和新分裂的节点作为其子节点。

2. 查询操作:查询时,提供一个目标区域,从根节点开始,依次检查其子节点的超矩形边界是否与目标区域有重叠。如果有重叠,继续深入到子节点及其子孙节点进行相同的操作,直到到达叶节点。最后,收集所有与目标区域有重叠的叶节点中的数据,即为查询结果。

3. 删除操作:删除一个数据时,先找到包含该数据的叶节点,将其从节点中移除。如果移除后节点的数据量低于某个阈值(通常为填充因子的一半),可能需要进行节点合并或兄弟节点间的元素重平衡操作,以保持树的结构稳定和查询效率。

四、示例说明

示例1:插入操作

假设我们有一个空的R-Tree,现在要插入四个城市的位置(矩形边界):北京、上海、广州、深圳。

  1. 首先,根节点为空,直接将北京插入,作为第一个叶节点。
  2. 插入上海,由于根节点未满,直接放入同一叶节点。
  3. 插入广州,根节点依然未满,放入同一叶节点。
  4. 插入深圳,此时叶节点已满(假设填充因子为1),需要分裂。将北京、上海分为一组,广州、深圳分为另一组,形成两个新的叶节点。更新根节点的超矩形边界,使其覆盖这两个新叶节点。

示例2:查询操作

假设我们要查询所有位于长江以南的城市。

  1. 从根节点开始,其超矩形边界包含了整个中国,与查询区域(长江以南)有重叠。
  2. 深入到包含广州和深圳的叶节点,其超矩形边界与查询区域有重叠,所以返回这两个城市。
  3. 继续深入到包含北京和上海的叶节点,其超矩形边界与查询区域无重叠,结束搜索。

示例3:删除操作

假设我们要删除广州。

  1. 找到包含广州的叶节点,将其从节点中移除。
  2. 由于该节点只剩下深圳一个数据,低于填充因子的一半,考虑合并或重平衡。假设选择合并,将相邻的北京、上海节点合并到此节点,形成一个新的叶节点,包含北京、上海、深圳三个城市,并更新父节点的超矩形边界。

 简单作答:

(简化的示例,没有涵盖完整的R-Tree实现细节(如节点分裂的具体算法、填充因子的管理等),旨在展示基本的插入逻辑。实际应用中,建议使用成熟的R-Tree库(如JTS Topology Suite或GeoTools))

Java版:

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;// 城市位置用矩形表示
class City {String name;Rectangle2D.Float rectangle;public City(String name, float x1, float y1, float x2, float y2) {this.name = name;this.rectangle = new Rectangle2D.Float(x1, y1, x2 - x1, y2 - y1);}@Overridepublic String toString() {return name;}
}// R-Tree节点
class RTreeNode {Rectangle2D.Float boundingBox;List<RTreeNode> children = new ArrayList<>();List<City> cities = new ArrayList<>();public RTreeNode(Rectangle2D.Float boundingBox) {this.boundingBox = boundingBox;}public void insertCity(City city) {// 此处仅模拟插入操作,简化版R-Tree直接将城市添加到当前节点cities.add(city);boundingBox = calculateBoundingBox(cities);}private static Rectangle2D.Float calculateBoundingBox(List<City> cities) {float xmin = Float.MAX_VALUE, ymin = Float.MAX_VALUE, xmax = Float.MIN_VALUE, ymax = Float.MIN_VALUE;for (var city : cities) {xmin = Math.min(xmin, (float) city.rectangle.getMinX());ymin = Math.min(ymin, (float) city.rectangle.getMinY());xmax = Math.max(xmax, (float) city.rectangle.getMaxX());ymax = Math.max(ymax, (float) city.rectangle.getMaxY());}return new Rectangle2D.Float(xmin, ymin, xmax - xmin, ymax - ymin);}public void addChild(RTreeNode child) {children.add(child);boundingBox = combineBoundingBoxes(boundingBox, child.boundingBox);}private static Rectangle2D.Float combineBoundingBoxes(Rectangle2D.Float bbox1, Rectangle2D.Float bbox2) {float xmin = Math.min(bbox1.getMinX(), bbox2.getMinX());float ymin = Math.min(bbox1.getMinY(), bbox2.getMinY());float xmax = Math.max(bbox1.getMaxX(), bbox2.getMaxX());float ymax = Math.max(bbox1.getMaxY(), bbox2.getMaxY());return new Rectangle2D.Float(xmin, ymin, xmax - xmin, ymax - ymin);}public List<City> query(Rectangle2D.Float queryArea) {var result = new ArrayList<City>();searchNodes(this, queryArea, result);return result;}private void searchNodes(RTreeNode node, Rectangle2D.Float queryArea, List<City> result) {if (queryArea.intersects(node.boundingBox)) {for (var child : node.children) {searchNodes(child, queryArea, result);}for (var city : node.cities) {if (queryArea.contains(city.rectangle)) {result.add(city);}}}}public void removeCity(City city) {cities.remove(city);boundingBox = calculateBoundingBox(cities);if (cities.size() < 2) {  // 假设填充因子为1,最多容纳两个城市// 合并相邻节点(简化版R-Tree仅考虑相邻节点合并)if (!children.isEmpty()) {var firstChild = children.get(0);children.clear();cities.addAll(firstChild.cities);boundingBox = combineBoundingBoxes(boundingBox, firstChild.boundingBox);for (var grandchild : firstChild.children) {addChild(grandchild);}}}}
}// R-Tree类,包含根节点和操作方法
class RTree {public RTreeNode root;public RTree() {root = new RTreeNode(new Rectangle2D.Float(0, 0, .png, 1000));  // 假设根节点包含整个中国}public void insertCity(City city) {root.insertCity(city);}public List<City> query(Rectangle2D.Float queryArea) {return root.query(queryArea);}public void removeCity(City city) {root.removeCity(city);}
}public class Main {public static void main(String[] args) {// 创建R-Tree实例并插入城市(与示例1相同)RTree rTree = new RTree();rTree.insertCity(new City("北京", 100, 100, 200, 200));rTree.insertCity(new City("上海", 300, 300, 400, 400));rTree.insertCity(new City("广州", 500, 500, 600, 600));rTree.insertCity(new City("深圳", 700, 700, 800, 800));// 示例2查询操作// 假设长江以南的查询区域为:(x1, y1) = (0, 0), (x2, y2) = (1000, ½ height of China)float queryHeight = 500;  // 示例中未提供中国高度,此处假设为500Rectangle2D.Float queryArea = new Rectangle2D.Float(0, 0, 1000, queryHeight);var result = rTree.query(queryArea);System.out.println("Cities located in the south of the Yangtze River:");for (var city : result) {System.out.println(city);}// 示例3删除操作rTree.removeCity(new City("广州", 500, 500, 600, 600));}
}

我们创建了一个接近实际R-Tree结构的简化实现,包括节点的层级结构(尽管在这个简化版本中,所有城市都直接存储在根节点中)和插入、查询、删除方法。RTreeNode类包含节点的边界、子节点列表和城市列表,以及计算边界、搜索节点、合并边界、插入城市、查询、删除城市等方法。RTree类包含根节点和对应的插入、查询、删除方法。

Main方法中,我们首先创建一个RTree实例并插入四个城市(与示例1相同)。然后,我们根据示例2的描述定义了一个查询区域,表示长江以南的部分。接着,我们调用query方法进行查询,并打印出位于查询区域内的城市名称。最后,我们根据示例3的描述删除城市广州,并触发节点合并。

C++版:

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits>// 城市位置用矩形表示
struct City {std::string name;float x1, y1, x2, y2;City(const std::string& name, float x1, float y1, float x2, float y2): name(name), x1(x1), y1(y1), x2(x2), y2(y2) {}
};// R-Tree节点
class RTreeNode {
public:struct BoundingBox {float xmin, ymin, xmax, ymax;BoundingBox(float xmin, float ymin, float xmax, float ymax): xmin(xmin), ymin(ymin), xmax(xmax), ymax(ymax) {}bool intersects(const BoundingBox& other) const {return !(other.xmax <= xmin || xmax <= other.xmin ||other.ymax <= ymin || ymax <= other.ymin);}};std::vector<RTreeNode> children;std::vector<City> cities;BoundingBox boundingBox;void insertCity(const City& city) {// 此处仅模拟插入操作,简化版R-Tree直接将城市添加到当前节点cities.push_back(city);updateBoundingBox();}void addChild(const RTreeNode& child) {children.push_back(child);updateBoundingBox();}std::vector<City> query(const BoundingBox& queryArea) const {std::vector<City> result;searchNodes(*this, queryArea, result);return result;}void removeCity(const City& city) {auto it = std::find(cities.begin(), cities.end(), city);if (it != cities.end()) {cities.erase(it);updateBoundingBox();if (cities.size() < 2) {  // 假设填充因子为1,最多容纳两个城市// 合并相邻节点(简化版R-Tree仅考虑相邻节点合并)if (!children.empty()) {children[0].cities.insert(children[0].cities.end(), cities.begin(), cities.end());cities.clear();boundingBox = children[0].boundingBox;children.clear();}}}}private:static BoundingBox calculateBoundingBox(const std::vector<City>& cities) {float xmin = std::numeric_limits<float>::max(), ymin = std::numeric_limits<float>::max(),xmax = std::numeric_limits<float>::min(), ymax = std::numeric_limits<float>::min();for (const auto& city : cities) {xmin = std::min(xmin, city.x1);ymin = std::min(ymin, city.y1);xmax = std::max(xmax, city.x2);ymax = std::max(ymax, city.y2);}return BoundingBox{xmin, ymin, xmax, ymax};}void updateBoundingBox() {boundingBox = calculateBoundingBox(cities);for (const auto& child : children) {boundingBox.xmin = std::min(boundingBox.xmin, child.boundingBox.xmin);boundingBox.ymin = std::min(boundingBox.ymin, child.boundingBox.ymin);boundingBox.xmax = std::max(boundingBox.xmax, child.boundingBox.xmax);boundingBox.ymax = std::max(boundingBox.ymax, child.boundingBox.ymax);}}static void searchNodes(const RTreeNode& node, const BoundingBox& queryArea, std::vector<City>& result) {if (node.boundingBox.intersects(queryArea)) {for (const auto& child : node.children) {searchNodes(child, queryArea, result);}for (const auto& city : node.cities) {if (queryArea.xmin <= city.x1 && city.x2 <= queryArea.xmax &&queryArea.ymin <= city.y1 && city.y2 <= queryArea.ymax) {result.push_back(city);}}}}
};// R-Tree类,包含根节点和操作方法
class RTree {
public:RTreeNode root;RTree() {// 假设根节点包含整个中国root.boundingBox = RTreeNode::BoundingBox{0, 0, 1000, 1000};}void insertCity(const City& city) {root.insertCity(city);}std::vector<City> query(const RTreeNode::BoundingBox& queryArea) const {return root.query(queryArea);}void removeCity(const City& city) {root.removeCity(city);}
};int main() {// 创建R-Tree实例并插入城市(与示例1相同)RTree rTree;rTree.insertCity({ "北京", 100, 100, 200, 200 });rTree.insertCity({ "上海", 300, 300, 400, 400 });rTree.insertCity({ "广州", 500, 500, 600, 600 });rTree.insertCity({ "深圳", 700, 700, 800, 800 });// 示例2查询操作// 假设长江以南的查询区域为:(x1, y1) = (0, 0), (x2, y2) = (1000, ½ height of China)float queryHeight = 500;  // 示例中未提供中国高度,此处假设为500RTreeNode::BoundingBox queryArea{0, 0, 1000, queryHeight};auto result = rTree.query(queryArea);std::cout << "Cities located in the south of the Yangtze River:" << std::endl;for (const auto& city : result) {std::cout << city.name << std::endl;}// 示例3删除操作rTree.removeCity({ "广州", 500, 500, 600, 600 });return 0;
}

在这个示例中,我们创建了一个接近实际R-Tree结构的简化实现,包括节点的层级结构(尽管在这个简化版本中,所有城市都直接存储在根节点中)和插入、查询、删除方法。

RTreeNode类包含节点的边界、子节点列表和城市列表,以及计算边界、搜索节点、插入城市、查询、删除城市等方法。RTree类包含根节点和对应的插入、查询、删除方法。

main方法中,我们首先创建一个RTree实例并插入四个城市(与示例1相同)。然后,我们根据示例2的描述定义了一个查询区域,表示长江以南的部分。接着,我们调用query方法进行查询,并打印出位于查询区域内的城市名称。最后,我们根据示例3的描述删除城市广州,并触发节点合并。

C#版:

using System;
using System.Collections.Generic;
using System.Drawing;// 城市位置用矩形表示
class City
{public string Name { get; set; }public RectangleF Rectangle { get; set; }public City(string name, float x1, float y1, float x2, float y2){Name = name;Rectangle = new RectangleF(x1, y1, x2 - x1, y2 - y1);}
}// R-Tree节点
class RTreeNode
{public RectangleF BoundingBox { get; set; }public List<RTreeNode> Children { get; } = new List<RTreeNode>();public List<City> Cities { get; } = new List<City>();public void InsertCity(City city){// 此处仅模拟插入操作,简化版R-Tree直接将城市添加到当前节点Cities.Add(city);UpdateBoundingBox();}public void AddChild(RTreeNode child){Children.Add(child);UpdateBoundingBox();}public List<City> Query(RectangleF queryArea){var result = new List<City>();SearchNodes(this, queryArea, result);return result;}public void RemoveCity(City city){Cities.Remove(city);UpdateBoundingBox();if (Cities.Count < 2)  // 假设填充因子为1,最多容纳两个城市{// 合并相邻节点(简化版R-Tree仅考虑相邻节点合并)if (Children.Count > 0){Children[0].Cities.AddRange(Cities);Cities.Clear();BoundingBox = Children[0].BoundingBox;Children.Clear();}}}private void UpdateBoundingBox(){BoundingBox = CalculateBoundingBox(Cities);foreach (var child in Children){BoundingBox = RectangleF.Union(BoundingBox, child.BoundingBox);}}private static RectangleF CalculateBoundingBox(List<City> cities){float xmin = float.MaxValue, ymin = float.MaxValue, xmax = float.MinValue, ymax = float.MinValue;foreach (var city in cities){xmin = Math.Min(xmin, city.Rectangle.X);ymin = Math.Min(ymin, city.Rectangle.Y);xmax = Math.Max(xmax, city.Rectangle.Right);ymax = Math.Max(ymax, city.Rectangle.Bottom);}return new RectangleF(xmin, ymin, xmax - xmin, ymax - ymin);}private static void SearchNodes(RTreeNode node, RectangleF queryArea, List<City> result){if (queryArea.IntersectsWith(node.BoundingBox)){foreach (var child in node.Children){SearchNodes(child, queryArea, result);}foreach (var city in node.Cities){if (queryArea.Contains(city.Rectangle)){result.Add(city);}}}}
}// R-Tree类,包含根节点和操作方法
class RTree
{public RTreeNode Root { get; }public RTree(){// 假设根节点包含整个中国Root = new RTreeNode { BoundingBox = new RectangleF(0, 0, 1000, 1000) };}public void InsertCity(City city){Root.InsertCity(city);}public List<City> Query(RectangleF queryArea){return Root.Query(queryArea);}public void RemoveCity(City city){Root.RemoveCity(city);}
}class Program
{static void Main(string[] args){// 创建R-Tree实例并插入城市(与示例1相同)RTree rTree = new RTree();rTree.InsertCity(new City("北京", 100, 100, 200, 200));rTree.InsertCity(new City("上海", 300, 300, 400, 400));rTree.InsertCity(new City("广州", 500, 500, 600, 600));rTree.InsertCity(new City("深圳", 700, 700, 800, 800));// 示例2查询操作// 假设长江以南的查询区域为:(x1, y1) = (0, 0), (x2, y2) = (1000, ½ height of China)float queryHeight = 500;  // 示例中未提供中国高度,此处假设为500RectangleF queryArea = new RectangleF(0, 0, 1000, queryHeight);var result = rTree.Query(queryArea);Console.WriteLine("Cities located in the south of the Yangtze River:");foreach (var city in result){Console.WriteLine(city.Name);}// 示例3删除操作rTree.RemoveCity(new City("广州", 500, 500, 600, 600));}
}

在这个示例中,我们创建了一个接近实际R-Tree结构的简化实现,包括节点的层级结构(尽管在这个简化版本中,所有城市都直接存储在根节点中)和插入、查询、删除方法。

RTreeNode类包含节点的边界、子节点列表和城市列表,以及计算边界、搜索节点、插入城市、查询、删除城市等方法。RTree类包含根节点和对应的插入、查询、删除方法。

Main方法中,我们首先创建一个RTree实例并插入四个城市(与示例1相同)。然后,我们根据示例2的描述定义了一个查询区域,表示长江以南的部分。接着,我们调用Query方法进行查询,并打印出位于查询区域内的城市名称。最后,我们根据示例3的描述删除城市广州,并触发节点合并。

注意:实际应用中,请使用成熟的R-Tree库以获得完整的功能和优化。

以上就是对R-Tree的详细介绍,包括其基本概念、结构特点、底层实现以及通过示例说明其插入、查询、删除操作。R-Tree作为一种高效的空间索引结构,极大地提升了大规模空间数据的检索效率,广泛应用于地理信息系统、搜索引擎、图像处理等领域。希望这次口语化的讲解能让大家对R-Tree有更深刻的理解。

如果有任何疑问,欢迎随时提问!

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

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

相关文章

NIKKE胜利女神PC怎么设置中文 手把手教你设置中文教程

这个游戏中的妮姬分四个企业&#xff0c;其中朝圣者这个派别的妮姬很少而且不在愿望单理&#xff0c;朝圣者的所有姐姐都很哇塞&#xff0c;红莲更是其中的大姐大。一般想抽朝圣者只能靠歪或者出限定卡池&#xff0c;举个栗子&#xff0c;我入坑的时候 朝圣者 神罚 是限定卡池&…

计算系数(acwing,数论)

题目描述&#xff1a; 给定一个多项式 (axby)^k&#xff0c;请求出多项式展开后 x^n*y^m 项的系数。 输入格式&#xff1a; 共一行&#xff0c;包含 5 个整数&#xff0c;分别为 a&#xff0c;b&#xff0c;k&#xff0c;n&#xff0c;m&#xff0c;每两个整数之间用一个空格…

【STL】stack与queue的底层原理及其实现

文章目录 stack的介绍库中stack的使用栈的模拟实现queue的介绍库中queue的使用queue的模拟实现 stack的介绍 &#xff08;图片来自知乎&#xff09; 1.stack是一种容器适配器&#xff0c;模拟了栈的数据结构。数据只能从一端进去&#xff0c;另一端出来&#xff08;先进后出&am…

基于单片机水质检测仪系统仿真设计

**单片机设计介绍&#xff0c; 基于单片机水质检测仪系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机水质检测仪系统仿真设计的主要目标是&#xff0c;通过仿真手段验证基于单片机的水质检测仪系统的设计方…

硬盘容量骤降为0?原因解析与数据恢复秘籍

当我们打开计算机&#xff0c;准备进入日常的工作或娱乐时&#xff0c;突然发现硬盘容量显示为0&#xff0c;这无疑是一个令人头痛的问题。硬盘&#xff0c;作为我们存储重要数据和文件的关键设备&#xff0c;一旦容量显示为0&#xff0c;就意味着我们可能面临数据丢失的风险。…

Arduino开发 esp32cam+opencv人脸识别距离+语音提醒

效果 低于20厘米语音提醒字体变红 QQ录屏20240406131651 Arduino代码 可直接复制使用&#xff08;修改自己的WIFI) #include <esp32cam.h> #include <WebServer.h> #include <WiFi.h> // 设置要连接的WiFi名称和密码 const char* WIFI_SSID "gumou&q…

MYSQL 8.0版本修改用户密码(知道登录密码)和Sqlyog错误码2058一案

今天准备使用sqlyog连接一下我Linux上面的mysql数据库&#xff0c;然后就报如下错误 有一个简单的办法就是修改密码为password就完事!然后我就开始查找如何修改密码! 如果是需要解决Sqlyog错误码2058的话&#xff0c;执行以下命令&#xff0c;但是注意root对应host是不是loca…

Fecify站点斗篷cloak

斗篷cloak站点斗篷模式功能发布&#xff01;全新的应用场景&#xff0c;该模式是针对推广不用GMC&#xff0c;而是通过facebook&#xff0c;或者其他的一些平台/工具推广&#xff0c;这些推广方式的特点是&#xff1a;不需要商品的图片&#xff0c;或者说不会排查商品图片的侵权…

基础语法复习

常用的定义&#xff1a; 读取数据加速&#xff1a; input sys.stdin.readline 设置递归深度&#xff1a; sys.setrecursionlimit(100000) 记忆化搜索&#xff1a; from functools import lru_cache lru_cache(maxsizeNone) 计数器&#xff1a; Counter 类是一个非常有…

P8707 [蓝桥杯 2020 省 AB1] 走方格

原题链接&#xff1a;[蓝桥杯 2020 省 AB1] 走方格 - 洛谷 目录 1.题目描述 2.思路分析 3.代码实现 1.题目描述 2.思路分析 题目大意&#xff1a;现在有个人站在第 1 行第 1 列&#xff0c;要走到第 i 行第 j 列&#xff08;每次只能向右或者向下走&#xff09;&#xff0…

计算机的发展趋势

本文 我们来说计算机的发展趋势 目前来讲 计算机是朝着 巨型化 微型化 网络化 智能化发展 巨型化 指功能巨型化 是指其高速运算、大存储容量和强功能的巨型计算机。其运算能力一般在每秒百亿次以上、内存容量在几百兆字节以上。 主要用于航空航天、军事、气象、人工智能、生…

单链表学习

//静态链表&#xff0c;只往后看&#xff0c;找前面必须遍历 //算法题用数组解题更快速 //初始化,头节点为空 //将x插入到头节点 //将x插到结点k的后面 //将下标k的后面的点删掉 #include<algorithm> #include<iostream> #include<cstring> #include<queu…

Leetcode:27.移除元素

题目要求 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出…

智能单灯控制器 计讯物联TL112

智能单灯控制器集成了先进技术的照明控制设备&#xff0c;它极大地改变了传统照明方式&#xff0c;并使之更加智能化。计讯物联智能单灯控制器TL112广泛应用于道路、景区、园区、隧道等场景智慧路灯杆使用。      智能单灯控制器通过集成最先进的传感器、控制模块和通信技术…

基于vue实现动态table

1、代码 <div style"height: 600px; overflow: scroll;"> <!-- height: 600px; overflow: scroll;作用是超出页面可以滑动 --><div ng-repeat"row in entity.procedureList"><cb-title title"工序{{row.procedireLocation}}&quo…

SpringBoot整合RabbitMQ,三种交换机类型示例

SpringBoot整合RabbitMQ&#xff0c;三种交换机类型示例 1、流程概括 2、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>3、配置RabbitMQ连接 在a…

【C++成长记】C++入门 |auto、范围for、nullptr

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、auto关键字 1、auto简介 2、auto的使用细则 &#xff08;1&#xff09; auto与指针和引用结合起…

多线程入门

文章目录 多线程相关的概念什么是多线程多线程的创建方式继承Thread方式实现Runable方式两种方式比较Thread类中常用方法 多线程相关的概念 并发与并行 并行&#xff1a;在同一时刻&#xff0c;有多个任务在多个CPU上同时执行。并发&#xff1a;在同一时刻&#xff0c;有多个任…

[网鼎杯 2020 玄武组]SSRFMe

[网鼎杯 2020 玄武组]SSRFMe 源码 <?php function check_inner_ip($url) {$match_resultpreg_match(/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/,$url);if (!$match_result){die(url fomat error);}try{$url_parseparse_url($url);}catch(Exception $e){die(url foma…

Linux内核

一、 内核开发与逻辑开发对比 内核开发&#xff1a; 需要应用程序调用API去在操作系统里对硬件进行操作。操作系统不允许用户直接去通过置GPIO口1或者0去控制硬件&#xff0c;为了管理这一切。操作系统将整个开发中分成了两个空间&#xff0c;一个是用户空间&#xff08;上两…