三十四、openlayers官网示例Dynamic clusters解析——动态的聚合图层

官网demo地址:
https://openlayers.org/en/latest/examples/clusters-dynamic.html
这篇绘制了多个聚合图层。
先初始化地图 ,设置了地图视角的边界extent,限制了地图缩放的范围
initMap() {const raster = new TileLayer({source: new XYZ({attributions:'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",}),});this.map = new Map({layers: [raster],target: "map",view: new View({center: [0, 0],zoom: 2,maxZoom: 19,extent: [...fromLonLat([16.1793, 48.1124]),...fromLonLat([16.5559, 48.313]),],showFullExtent: true,}),});this.map.on("pointermove", this.moveEvent);this.map.on("click", this.clickEvent);},
创建一个聚合数据源,数据是geoJson格式的。
const vectorSource = new VectorSource({format: new GeoJSON(),url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",});const clusterSource = new Cluster({attributions:'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',distance: 35,source: vectorSource,});
然后创建一个聚合图层
//聚合图层this.clustersLayer = new VectorLayer({source: clusterSource,style: this.clusterStyle,});this.map.addLayer(this.clustersLayer);
因为每个feature的样式不一样,所以样式这里都绑定了函数。
这里的outerCircle定义为全局变量而并非局部变量,主要是因为clusterStyle函数是个高频触发函数,将outerCircle写成全局的可以不用new那么多次。使用样式组的方法绘制出来了外发光效果,其实就是画了两次圆形。
//黄色圆圈 内外两层 发光效果clusterStyle(feature) {const size = feature.get("features").length;//还有下级if (size > 1) {return [new Style({image: this.outerCircle,}),new Style({image: this.innerCircle,text: new Text({text: size.toString(),fill: this.textFill,stroke: this.textStroke,}),}),];}//没有下级const originalFeature = feature.get("features")[0];return this.clusterMemberStyle(originalFeature);},
this.textFill = new Fill({color: "#fff",});this.textStroke = new Stroke({color: "rgba(0, 0, 0, 0.6)",width: 3,});this.innerCircle = new CircleStyle({radius: 14,fill: new Fill({color: "rgba(255, 165, 0, 0.7)",}),});this.outerCircle = new CircleStyle({radius: 20,fill: new Fill({color: "rgba(255, 153, 102, 0.3)",}),});
有下级的时候图形是橙色发光的样式,没有下级的时候。根据feature的LEISTUNG字段显示为不同的icon图形。
clusterMemberStyle(clusterMember) {return new Style({geometry: clusterMember.getGeometry(),image:clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,});},
this.darkIcon = new Icon({src: "data/icons/emoticon-cool.svg",});this.lightIcon = new Icon({src: "data/icons/emoticon-cool-outline.svg",});
创建一个凸包图层。
凸包是包含给定点集的最小凸多边形,在许多计算几何应用中非常重要,如图形学、地理信息系统(GIS)和形状分析。计算凸包的算法通常基于点的排序和几何性质,可以有效地处理大规模的数据。
下载引入monotone-chain-convex-hull
npm i monotone-chain-convex-hull
import monotoneChainConvexHull from "monotone-chain-convex-hull";
//凸包图层样式this.convexHullFill = new Fill({color: "rgba(255, 153, 0, 0.4)",});this.convexHullStroke = new Stroke({color: "rgba(204, 85, 0, 1)",width: 1.5,});
//凸包图层this.clusterHulls = new VectorLayer({source: clusterSource,style: this.clusterHullStyle,});
clusterHullStyle(cluster) {if (cluster !== this.hoverFeature) {return null;}const originalFeatures = cluster.get("features");const points = originalFeatures.map((feature) =>feature.getGeometry().getCoordinates());return new Style({geometry: new Polygon([monotoneChainConvexHull(points)]),fill: this.convexHullFill,stroke: this.convexHullStroke,});},
当鼠标移动到点图层时,显示凸包效果。
moveEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features[0] !== this.hoverFeature) {this.hoverFeature = features[0];this.clusterHulls.setStyle(this.clusterHullStyle);this.map.getTargetElement().style.cursor =this.hoverFeature && this.hoverFeature.get("features").length > 1? "pointer": "";}});},
然后是点线图层
this.clusterCircles = new VectorLayer({source: clusterSource,style: this.clusterCircleStyle,});
当前视图的缩放级别达到了最大缩放级别或者范围的宽度和高度都小于当前视图的分辨率
往点线图层的style数组中添加两个样式。。
clusterCircleStyle(cluster, resolution) {if (cluster !== this.clickFeature || resolution !== this.clickResolution) {return null;}const clusterMembers = cluster.get("features");const centerCoordinates = cluster.getGeometry().getCoordinates();return this.generatePointsCircle( clusterMembers.length,cluster.getGeometry().getCoordinates(),resolution).reduce((styles, coordinates, i) => {const point = new Point(coordinates);const line = new LineString([centerCoordinates, coordinates]);styles.unshift(new Style({geometry: line,stroke: this.convexHullStroke,}));styles.push(this.clusterMemberStyle(new Feature({...clusterMembers[i].getProperties(),geometry: point,})));return styles;}, []);},
generatePointsCircle 方法根据聚类成员的数量、中心坐标和当前分辨率,生成一个圆周上的点坐标数组。
generatePointsCircle(count, clusterCenter, resolution) {//计算圆周长度和每个点的角度步长const circumference =this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);let legLength = circumference / (Math.PI * 2); const angleStep = (Math.PI * 2) / count;const res = [];let angle;//调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。legLength = Math.max(legLength, 35) * resolution; //生成圆周上的点坐标for (let i = 0; i < count; ++i) {angle = this.circleStartAngle + i * angleStep;res.push([clusterCenter[0] + legLength * Math.cos(angle),clusterCenter[1] + legLength * Math.sin(angle),]);}return res;},
点击事件时,获取当前点击的feature的边界值,定位到指定位置。
clickEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features.length > 0) {const clusterMembers = features[0].get("features");if (clusterMembers.length > 1) {const extent = createEmpty();clusterMembers.forEach((feature) =>extend(extent, feature.getGeometry().getExtent()));const view = this.map.getView();const resolution = this.map.getView().getResolution();//如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率if (view.getZoom() === view.getMaxZoom() ||(getWidth(extent) < resolution && getHeight(extent) < resolution)) {this.clickFeature = features[0];this.clickResolution = resolution;this.clusterCircles.setStyle(this.clusterCircleStyle);} else {view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });}}}});},

完整代码:
<template><div class="box"><h1>Dynamic clusters</h1><div id="map"></div></div>
</template><script>
import Feature from "ol/Feature.js";
import GeoJSON from "ol/format/GeoJSON.js";
import Map from "ol/Map.js";
import View from "ol/View.js";
import {Circle as CircleStyle,Fill,Icon,Stroke,Style,Text,
} from "ol/style.js";
import { Cluster, Vector as VectorSource, XYZ } from "ol/source.js";
import { LineString, Point, Polygon } from "ol/geom.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { createEmpty, extend, getHeight, getWidth } from "ol/extent.js";
import { fromLonLat } from "ol/proj.js";
// import convexHull from "convex-hull";
import monotoneChainConvexHull from "monotone-chain-convex-hull";
export default {name: "",components: {},data() {return {map: null,hoverFeature: null,convexHullFill: null,convexHullStroke: null,textFill: null,textStroke: null,innerCircle: null,outerCircle: null,darkIcon: null,lightIcon: null,circleDistanceMultiplier: 1,circleFootSeparation: 28,circleStartAngle: Math.PI / 2,clickFeature: null,clustersLayer: null,clusterHulls: null,clusterCircles: null,clickResolution:null,};},computed: {},created() {},mounted() {this.initMap();this.initStyle();this.addClusterLayers();},methods: {initMap() {const raster = new TileLayer({source: new XYZ({attributions:'Base map: <a target="_blank" href="https://basemap.at/">basemap.at</a>',url: "https://maps{1-4}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png",}),});this.map = new Map({layers: [raster],target: "map",view: new View({center: [0, 0],zoom: 2,maxZoom: 19,extent: [...fromLonLat([16.1793, 48.1124]),...fromLonLat([16.5559, 48.313]),],showFullExtent: true,}),});this.map.on("pointermove", this.moveEvent);this.map.on("click", this.clickEvent);},addClusterLayers() {const vectorSource = new VectorSource({format: new GeoJSON(),url: "https://openlayers.org/en/latest/examples/data/geojson/photovoltaic.json",});const clusterSource = new Cluster({attributions:'Data: <a href="https://www.data.gv.at/auftritte/?organisation=stadt-wien">Stadt Wien</a>',distance: 35,source: vectorSource,});//凸包图层this.clusterHulls = new VectorLayer({source: clusterSource,style: this.clusterHullStyle,});//聚合图层this.clustersLayer = new VectorLayer({source: clusterSource,style: this.clusterStyle,});//特定情况下的图层this.clusterCircles = new VectorLayer({source: clusterSource,style: this.clusterCircleStyle,});this.map.addLayer(this.clusterHulls);this.map.addLayer(this.clustersLayer);this.map.addLayer(this.clusterCircles);},initStyle() {//凸包图层样式this.convexHullFill = new Fill({color: "rgba(255, 153, 0, 0.4)",});this.convexHullStroke = new Stroke({color: "rgba(204, 85, 0, 1)",width: 1.5,});this.textFill = new Fill({color: "#fff",});this.textStroke = new Stroke({color: "rgba(0, 0, 0, 0.6)",width: 3,});this.innerCircle = new CircleStyle({radius: 14,fill: new Fill({color: "rgba(255, 165, 0, 0.7)",}),});this.outerCircle = new CircleStyle({radius: 20,fill: new Fill({color: "rgba(255, 153, 102, 0.3)",}),});this.darkIcon = new Icon({src: "data/icons/emoticon-cool.svg",});this.lightIcon = new Icon({src: "data/icons/emoticon-cool-outline.svg",});},clusterMemberStyle(clusterMember) {return new Style({geometry: clusterMember.getGeometry(),image:clusterMember.get("LEISTUNG") > 5 ? this.darkIcon : this.lightIcon,});},clusterCircleStyle(cluster, resolution) {if (cluster !== this.clickFeature || resolution !== this.clickResolution) {return null;}const clusterMembers = cluster.get("features");const centerCoordinates = cluster.getGeometry().getCoordinates();return this.generatePointsCircle( clusterMembers.length,cluster.getGeometry().getCoordinates(),resolution).reduce((styles, coordinates, i) => {const point = new Point(coordinates);const line = new LineString([centerCoordinates, coordinates]);styles.unshift(new Style({geometry: line,stroke: this.convexHullStroke,}));styles.push(this.clusterMemberStyle(new Feature({...clusterMembers[i].getProperties(),geometry: point,})));return styles;}, []);},generatePointsCircle(count, clusterCenter, resolution) {//计算圆周长度和每个点的角度步长const circumference =this.circleDistanceMultiplier * this.circleFootSeparation * (2 + count);let legLength = circumference / (Math.PI * 2); const angleStep = (Math.PI * 2) / count;const res = [];let angle;//调整线段长度 确保线段长度至少为 35,并根据分辨率进行调整。legLength = Math.max(legLength, 35) * resolution; //生成圆周上的点坐标for (let i = 0; i < count; ++i) {angle = this.circleStartAngle + i * angleStep;res.push([clusterCenter[0] + legLength * Math.cos(angle),clusterCenter[1] + legLength * Math.sin(angle),]);}return res;},clusterHullStyle(cluster) {if (cluster !== this.hoverFeature) {return null;}const originalFeatures = cluster.get("features");const points = originalFeatures.map((feature) =>feature.getGeometry().getCoordinates());return new Style({geometry: new Polygon([monotoneChainConvexHull(points)]),fill: this.convexHullFill,stroke: this.convexHullStroke,});},//黄色圆圈 内外两层 发光效果clusterStyle(feature) {const size = feature.get("features").length;//还有下级if (size > 1) {return [new Style({image: this.outerCircle,}),new Style({image: this.innerCircle,text: new Text({text: size.toString(),fill: this.textFill,stroke: this.textStroke,}),}),];}//没有下级const originalFeature = feature.get("features")[0];return this.clusterMemberStyle(originalFeature);},moveEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features[0] !== this.hoverFeature) {this.hoverFeature = features[0];this.clusterHulls.setStyle(this.clusterHullStyle);this.map.getTargetElement().style.cursor =this.hoverFeature && this.hoverFeature.get("features").length > 1? "pointer": "";}});},clickEvent(event) {this.clustersLayer.getFeatures(event.pixel).then((features) => {if (features.length > 0) {const clusterMembers = features[0].get("features");if (clusterMembers.length > 1) {const extent = createEmpty();clusterMembers.forEach((feature) =>extend(extent, feature.getGeometry().getExtent()));const view = this.map.getView();const resolution = this.map.getView().getResolution();//如果当前视图的缩放级别达到了最大缩放级别 如果范围的宽度和高度都小于当前视图的分辨率if (view.getZoom() === view.getMaxZoom() ||(getWidth(extent) < resolution && getHeight(extent) < resolution)) {this.clickFeature = features[0];this.clickResolution = resolution;this.clusterCircles.setStyle(this.clusterCircleStyle);} else {view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });}}}});},},
};
</script><style lang="scss" scoped>
#map {width: 100%;height: 500px;
}
.box {height: 100%;
}#info {width: 100%;height: 24rem;overflow: scroll;display: flex;align-items: baseline;border: 1px solid black;justify-content: flex-start;
}
</style>
相关文章:
三十四、openlayers官网示例Dynamic clusters解析——动态的聚合图层
官网demo地址: https://openlayers.org/en/latest/examples/clusters-dynamic.html 这篇绘制了多个聚合图层。 先初始化地图 ,设置了地图视角的边界extent,限制了地图缩放的范围 initMap() {const raster new TileLayer({source: new XYZ…...
SpringBoot登录认证--衔接SpringBoot案例通关版
文章目录 登录认证登录校验-概述登录校验 会话技术什么是会话呢?cookie Session令牌技术登录认证-登录校验-JWT令牌-介绍JWT SpringBoot案例通关版,上接这篇 登录认证 先讲解基本的登录功能 登录功能本质就是查询操作 那么查询完毕后返回一个Emp对象 如果Emp对象不为空,那…...
vue3状态管理,pinia的使用
状态管理 我们知道组件与组件之间可以传递信息,那么我们就可以将一个信息作为组件的独立状态(例如,单个组件的颜色)或者共有状态(例如,多个组件是否显示)在组件之传递,…...
入门到实践,手把手教你用AI绘画!
前言 一款无需魔法的PS插件!下载即用,自带提示词插件,无论你是小白还是大神都能轻松上手,无配置要求,win/mac通通能用! AI绘画工具——StartAI 官网:StartAI官网 (istarry.com.cn) 近段时间…...
大模型应用框架-LangChain
LangChain的介绍和入门 💥 什么是LangChain LangChain由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架,LLMs使用机器学习算法和海量数据来分析和理解自然语言,GPT3.5、GPT4是…...
探索Linux中的强大文本处理工具——sed命令
探索Linux中的强大文本处理工具——sed命令 在Linux系统中,文本处理是一项日常且重要的任务。sed命令作为一个流编辑器,以其强大的文本处理能力而著称。它允许我们在不修改原始文件的情况下,对输入流(文件或管道)进行…...
冯喜运:6.3黄金原油晚间最新行情及独家操作策略指导
【黄金消息面分析】:在全球经济的波动和不确定性中,黄金作为传统的避险资产,其价格走势和市场分析一直是投资者关注的焦点。本周一(北京时间6月3日),现货黄金价格基本持平,交易商正在等待本周公…...
Spark_SparkOnHive_海豚调度跑任务写入Hive表失败解决
背景 前段时间我在海豚上打包程序写hive出现了一个问题,spark程序向hive写数据时,报了如下bug, org.apache.spark.sql.AnalysisException: The format of the existing table test.xx is HiveFileFormat It doesnt match the specified for…...
SaaS 电商设计 (十一) 那些高并发电商系统的限流方案设计
目录 一.什么是限流二.怎么做限流呢2.1 有哪些常见的系统限流算法2.1.1 固定窗口2.1.1 滑动窗口2.1.2 令牌桶2.1.3 漏桶算法 2.2 常见的限流方式2.2.1 单机限流&集群限流2.2.2 前置限流&后置限流 2.3 实际落地是怎么做的2.3.1 流量链路2.3.2 各链路限流2.3.2.1 网关层2…...
【算法】MT2 棋子翻转
✨题目链接: MT2 棋子翻转 ✨题目描述 在 4x4 的棋盘上摆满了黑白棋子,黑白两色棋子的位置和数目随机,其中0代表白色,1代表黑色;左上角坐标为 (1,1) ,右下角坐标为 (4,4) 。 现在依次有一些翻转操作&#…...
头颈肿瘤在PET/CT中的分割:HECKTOR挑战赛| 文献速递-深度学习肿瘤自动分割
Title 题目 Head and neck tumor segmentation in PET/CT: The HECKTOR challenge 头颈肿瘤在PET/CT中的分割:HECKTOR挑战赛 01 文献速递介绍 高通量医学影像分析,常被称为放射组学,已显示出其在揭示定量影像生物标志物与癌症预后之间关…...
Kafka重平衡导致无限循环消费问题
1. 问题描述 Kafka消费者消费消息超过了5分钟,不停的触发重平衡,消费者的offset因为重平衡提交失败,重复拉取消费,重复消费。 2. 问题原因 kafka默认的消息消费超时时间max.poll.interval.ms 300000, 也就是5分钟,…...
执行shell脚本时为什么要写成./test.sh,而不是test.sh?
一定要写成 ./test.sh,而不是 test.sh 运行其它二进制的程序也一样! 直接写 test.sh,linux 系统会去 PATH (系统环境)里寻找有没有叫 test.sh 的! 而只有 /bin, /sbin, /usr/bin,/usr/sbin 这…...
【人工智能】第一部分:ChatGPT的基本概念和技术背景
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...
雪花算法详解及源码分析
雪花算法的简介: 雪花算法用来实现全局唯一ID的业务主键,解决分库分表之后主键的唯一性问题,所以就单从全局唯一性来说,其实有很多的解决方法,比如说UUID、数据库的全局表的自增ID 但是在实际的开发过程中࿰…...
Golang TCP网络编程
文章目录 网络编程介绍TCP网络编程服务器监听客户端连接服务器服务端获取连接向连接中写入数据从连接中读取数据关闭连接/监听器 简易的TCP回声服务器效果展示服务端处理逻辑客户端处理逻辑 网络编程介绍 网络编程介绍 网络编程是指通过计算机网络实现程序间通信的一种编程技术…...
先进制造aps专题十 aps项目成功指南
aps项目成功指南 为了保证aps项目的成功 现在国内的aps项目 一是看aps软件本身是不是实现了复杂的排程算法和优化算法,算法引擎使用c高性能编译语言开发,支持工序的复杂关系,考虑副资源约束和特殊规格约束,提供了能考虑各种约束…...
实现Dropdown下拉菜单监听键盘上下键选中功能-React
用过ant design的小伙伴都知道,select组件是支持联想搜索跟上下键选中的效果的,但是在项目中我们可能会遇到用select组件无法实现我们的需求的情况,比如说一个div框,里面有input,又有tag标签,在input中输入…...
Ubuntu系统升级k8s节点的node节点遇到的问题
从1.23版本升级到1.28版本 node节点的是Ubuntu系统20.04的版本 Q1 node节点版本1.23升级1.28失败 解决办法: # 改为阿里云镜像 vim /etc/apt/sources.list.d/kubernetes.list# 新增 deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main# 执…...
前端将DOM元素导出为图片
前端工作中经常会用到把一些元素导出,比如表格,正好项目有遇到导出为excel和导出为图片,就都封装实现了一下,以供其他需求的开发者使用: 1.导出为文档 这个说白了就是下载的功能,传过去检索参数ÿ…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
