1、背景

前段时间公司有个需求,需要在一个图表中展示两条折线,并且绘制出两条线的交点。为了满足需求大哥的需求,我也是着实想了有一会。下面我就把具体的实现过程给大家展示一下。

1.1、ECharts 简介

个人很喜欢Echarts这个图表库,就先给大家介绍一下,方便大家更好的了解。

ECharts 是一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。

ECharts 功能很强大,提供了常见的二维图表,比如折线图、柱图、散点图、饼图、K线图,以及地图、热力图、线图(又称路径图),还有用于表示数据关系的关系图、旭日图等,此外还可以通过GL实现绚丽的三维可视化图。

Echarts 使用起来很方便,展示不同的图表只需要设置简单的配置项;官方的文档写的也很清楚,从简单的实现,到复杂的配置,以及内含的API,应有尽有;除此之外,还有丰富的实例库,以及Echarts的使用者的作品库,很大程度上降低了Echarts的使用门槛,方便初学者使用。而且现在成为了Apache孵化器项目,未来的前景很好。

2、初始绘制图表

需要展示的数据是与日期相关的两条线,每个数据均和一个日期相对应。如下:

// 两条折线图的数据,横坐标是日期,纵坐标为当前日期的数值
var chartData = {
line1: ["114", "114", "118", "114", "130", "130", "126", "130", "126", "130", "130", "134", "135", "135", "135", "135", "134", "132", "130", "128", "126", "124", "121", "119", "117", "116", "114", "112", "111", "110", "109", "108", "107", "106", "106", "105", "105", "105", "105", "106", "106", "108", "109", "112", "113", "115", "116", "118", "120", "123", "125", "128", "129", "130", "131", "131", "131", "131", "131", "130", "128", "126", "123", "119", "117", "115", "113", "112", "111", "110", "109", "107", "106", "105", "103", "103", "102", "101", "100", "99", "99", "99", "99", "101", "102", "104", "107", "109", "112", "114", "117", "119", "122", "124", "126", "127", "128", "129", "129", "130", "129", "128", "127", "126", "124", "122", "120", "117", "115", "113", "111", "109", "107", "105", "103", "100", "99", "98", "97", "97", "97", "98", "99", "101", "104", "107", "109", "112", "115", "117", "120", "123", "125", "128", "129", "130", "131", "131", "131", "131", "131", "130", "128", "126", "123", "119", "104"],
line2: ["124", "124", "128", "124", "140", "140", "136", "140", "136", "140", "140", "136", "132", "128", "128", "124", "124", "120", "116", "112", "112", "112", "112", "112", "108", "108", "108", "104", "108", "104", "104", "100", "104", "104", "108", "104", "108", "104", "108", "112", "116", "120", "124", "128", "124", "120", "124", "128", "132", "136", "140", "140", "136", "132", "128", "128", "124", "124", "120", "116", "112", "108", "104", "100", "104", "108", "112", "116", "112", "108", "104", "100", "96", "92", "88", "92", "96", "100", "100", "104", "104", "108", "112", "116", "116", "116", "120", "120", "124", "132", "132", "132", "132", "136", "132", "132", "128", "128", "124", "124", "120", "120", "116", "116", "112", "108", "108", "104", "104", "104", "100", "96", "96", "92", "92", "88", "92", "96", "100", "104", "108", "112", "116", "120", "124", "128", "124", "120", "124", "128", "132", "136", "140", "140", "136", "132", "128", "128", "124", "124", "120", "116", "112", "108", "104", "100", "103"],
date: ['200701', '200702', '200703', '200704', '200705', '200706', '200707', '200708', '200709', '200710', '200711', '200712', '200801', '200802', '200803', '200804', '200805', '200806', '200807', '200808', '200809', '200810', '200811', '200812', '200901', '200902', '200903', '200904', '200905', '200906', '200907', '200908', '200909', '200910', '200911', '200912', '201001', '201002', '201003', '201004', '201005', '201006', '201007', '201008', '201009', '201010', '201011', '201012', '201101', '201102', '201103', '201104', '201105', '201106', '201107', '201108', '201109', '201110', '201111', '201112', '201201', '201202', '201203', '201204', '201205', '201206', '201207', '201208', '201209', '201210', '201211', '201212', '201301', '201302', '201303', '201304', '201305', '201306', '201307', '201308', '201309', '201310', '201311', '201312', '201401', '201402', '201403', '201404', '201405', '201406', '201407', '201408', '201409', '201410', '201411', '201412', '201501', '201502', '201503', '201504', '201505', '201506', '201507', '201508', '201509', '201510', '201511', '201512', '201601', '201602', '201603', '201604', '201605', '201606', '201607', '201608', '201609', '201610', '201611', '201612', '201701', '201702', '201703', '201704', '201705', '201706', '201707', '201708', '201709', '201710', '201711', '201712', '201801', '201802', '201803', '201804', '201805', '201806', '201807', '201808', '201809', '201810', '201811', '201812', '201901', '201902', '201903']
}

下面开始初始绘制图表:

  1. 初始化 echarts 实例。ECharts 图表的展示需要依存在一个设置好 width、height 的 dom 中,Echarts 提供了一个初始化 echarts 实例的方法 echarts.init,此方法返回一个 echarts 实例,后续对图表的设置均是在此实例中实现的。这个方法的第一个参数为 dom,第二个参数设置图表的 theme,第三个参数则是额外的一些配置。一般情况下设置第一个参数即可。
  2. 设置图表的配置项。配置项是一个 js 对象,配置项的解释在下面的代码中,更多的配置可以参考官网。
  3. 绘制图表。echarts 图表的绘制、修改均通过 echarts实例的 setOption 方法完成。此方法可以接受四个参数,第一个为图表的配置项 option,其余三个均为额外的设置。平时使用第一个即可。

代码如下:

<style>
.charts {
width: 1000px;
height: 600px;
}
</style>
<!-- 准备好一个 dom,并且设置好 width 和 height,后续在此 dom 中绘制图表 -->
<div id="chart" class="charts"></div>
<script>
// 基于准备好的dom,初始化一个 echarts 实例。
var myChart = echarts.init(document.getElementById('chart')); // 设置图表的配置项
var option = {
// 标题
title: {
text: "初始绘制折线图"
},
// 提示框组件,鼠标放置上去后,会展示所在位置信息
tooltip: {},
// 图例组件,与 series 中的 name 对应,用于表示不同系列的标记、颜色和名字。也可以通过点击图例控制哪些系列不显示。
legend: {
data: ['line1', 'line2']
},
// x 轴的配置,默认类型为 type="category" 类目轴。
xAxis: {
data: chartData.date // 设置 x 轴的数据
},
// y 轴的配置,虽然没有写其他配置,但是必须有,否则会报错
yAxis: {},
// 系列列表,一个系列即可理解为一个图表,通过 type 决定所展示的图表类型。
series: [{
name: 'line1', // 单个图表系列的 name, 和 legend 中的 data 对应
type: 'line',
data: chartData.line1
}, {
name: 'line2', // 单个图表系列的 name, 和 legend 中的 data 对应
type: 'line',
data: chartData.line2
}]
} // 使用数据和配置项展示图表
myChart.setOption(option)
</script>

执行完上面的代码,初始的图表展示就欧克了:

3、计算折线交点

3.1、计算前的准备

计算交点前,需要把数据进行处理,且根据处理的数据,调整图表的展示。

首先,搞一个 计算两个线段交点 的方法,这个方法通过传入 两条线段四个端点横纵坐标 值,来计算两者交点的坐标:

// 求两条线段交点,a,b 为第一条线段的始末点,c,d 为第二条线段的始末点。x,y 为点的横纵坐标
function segmentsIntr({ a, b, c, d } = {}) {
var denominator = (b.y - a.y) * (d.x - c.x) - (a.x - b.x) * (c.y - d.y)
var x = ((b.x - a.x) * (d.x - c.x) * (c.y - a.y) +
(b.y - a.y) * (d.x - c.x) * a.x -
(d.y - c.y) * (b.x - a.x) * c.x) / denominator
var y = -((b.y - a.y) * (d.y - c.y) * (c.x - a.x) +
(b.x - a.x) * (d.y - c.y) * a.y -
(d.x - c.x) * (b.y - a.y) * c.y) / denominator
return [x, y]
}

然后传入同一个水平刻度内的两条线段的四个点坐标,不过目前水平刻度所代表的是日期字符串,不适用于计算交点坐标。

所以可以将 x 轴 的数据替换成日期在数组中的序列号 1...n,然后展示 x 轴刻度的时候通过 xAxis 的 formatter 属性,实现自定义刻度,将其转换成对应日期即可。

下面是 xAxis 修改后的配置:

xAxis: {
// 设置 x 轴的数据, 使用 日期 在数据中的 序列号 来表示 横坐标数值。
data: chartData.date.map((seg, idx) => {
return idx
}),
// 设置 x 轴的 展示标签, 使其根据 当前标签的序列号转换为 日期
axisLabel: {
formatter: function (params) {
return chartData.date[params]
}
}
},

调整后的图表如下:

会发现,X 轴已经好了,但是 提示框 tooltip 还需要再调整一下,那么我们利用 formatter 设置一下 自定义 的 tooltip。通过 formatter 自带参数 params 中的值,拼接好返回的格式即可。

// 提示框组件,鼠标放置上去后,会展示 鼠标 所在位置信息
tooltip: {
trigger: 'axis', // 设置提示框为:坐标轴触发。此项主要用于柱图、折线图的配置。
formatter: function (params) { // params 为一个数组,数组的每个元素 包含了 该折线图的点 所有的参数信息,比如 value(数值)、seriesName(系列名)、dataIndex(数据项的序号)
let dateIndex = 0; // 当前指示点的 日期序号
let tipList = params.map((seg) => {
let { value, seriesName, dataIndex } = seg;
dateIndex = dataIndex;
return `${seriesName}:${value}`
})
tipList.unshift(`${chartData.date[dateIndex]}`)
return tipList.join('<br/>')
}
},

展示效果如下:

这下子 tooltip 的格式调整好了,不过样式有点怪,原来是 tooltip 缺少了 系列名 前面的 小圆点dot

搞个生成 小圆点dot 的方法:

//获取tooltip的dot,radius 为圆点半径,color 为圆点颜色
function getTipDot({ radius = 5, color = "red" } = {}) {
return `<span style='width:${radius * 2}px;height:${radius * 2}px;display:inline-block;border-radius: ${radius}px;background:${color};margin:0px 3px;'></span>`
}

把 tooltip 的配置调整一下,添加对于 dot 的设置:

// 提示框组件,鼠标放置上去后,会展示 鼠标 所在位置信息
tooltip: {
trigger: 'axis', // 设置提示框为:坐标轴触发。此项主要用于柱图、折线图的配置。
// params 为一个数组,数组的每个元素 包含了 该折线图的点 所有的参数信息,
// 比如 value(数值)、seriesName(系列名)、dataIndex(数据项的序号)、color(系列颜色)
formatter: function (params) {
let dateIndex = 0; // 当前指示点的 日期序号
let tipList = params.map((seg) => {
let { value, seriesName, dataIndex, color } = seg;
dateIndex = dataIndex;
return `${getTipDot({ color })}${seriesName}:${value}` // 添加对于 dot 的配置
})
tipList.unshift(`${chartData.date[dateIndex]}`)
return tipList.join('<br/>')
}
},

调整后的效果如下:

3.2、计算交点

遍历数据,取出两条线每两个相邻的点组成线段,利用现有的方法 segmentsIntr 开始计算交点。

不过在计算之前,需要判断当前线段内是否有交点,避免不必要的计算。

// 判断两条线段是否有交点, a1、b1 为两条线在 x1 处的值;a2、b2 为两条线在 x2 处的值;
// 只要不是一条线段的两个点都高于另一个点就会有交点;
function ifHaveIntersectionPoint(a1, b1, a2, b2) {
return (+a1 > +b1) != (+a2 > +b2)
}

除了判断是否有交点,还需要判断是否是遍历的最后一组数据,因为最后一组数据idx,是不会有idx+1的数据的。

// 是否执行后续的计算 ? 不是最后一个点,且有交点时
function ifCalculatePoint(idx, lth, [a1, b1, a2, b2] = []) {
return idx !== (lth - 1) && ifHaveIntersectionPoint(a1, b1, a2, b2)
}

执行计算

// 获取两线所有交点
function getIntersectionPoint({ line1, line2, date } = {}) {
// 交点数组
var intersectionPointList = []
date.map((seg, idx) => {
// 分别是两条线在相邻两处的数值,用于通过比较大小,来确定此段内是否有交点
var valueGroup = [line1[idx], line2[idx], line1[idx + 1], line2[idx + 1]]
if (ifCalculatePoint(idx, date.length, valueGroup)) {
var dotGroup = {
a: { x: idx, y: line1[idx] },
b: { x: idx + 1, y: line1[idx + 1] },
c: { x: idx, y: line2[idx] },
d: { x: idx + 1, y: line2[idx + 1] }
}
// 计算交点的位置
var intersectionPoint = this.segmentsIntr(dotGroup)
intersectionPointList.push(intersectionPoint)
}
})
return intersectionPointList;
}

4、绘制交点

通过上面的交点计算,得出的结果如下:

intersectionPointList: [
[11.4, 134.4],
[33.5, 106],
[34.666666666666664, 105.33333333333333],
[35.25, 105],
[36.75, 105],
[37.25, 105],
[53.4, 130.4],
[66.2, 112.8],
[68.33333333333333, 110.66666666666667],
[78, 100],
[96, 128],
[117.4, 97.6],
[135.4, 130.4]
]

可以看到,计算出来的交点很不规律,大部分是小数,在当前的类目轴上并不能很好的展示。

不过好在,Echarts 可以在同一个直角坐标系中同时绘制多个坐标轴。

我们可以在坐标系中添加一条数值轴类型的x轴,并且用散点图绘制出交点。初始绘制效果如下:

可以看到现在有两条 X 轴,调整一下配置项,隐藏掉上层的 X 轴展示。

// x 轴的配置,默认类型为 type="category" 类目轴。
xAxis: [{
// 设置 x 轴的数据, 使用 日期 在数据中的 序列号 来表示 横坐标数值。
data: chartData.date.map((seg, idx) => {
return idx
}),
// 设置 x 轴的 展示标签, 使其根据 当前标签的序列号转换为 日期
axisLabel: {
formatter: function (params) {
return chartData.date[params]
}
}
},
// 添加一个x轴用于展示散点图(交点)
{
type: 'value',
min: 0,
max: chartData.date.length - 1,
show: false
}]

如上可见,简单的交点已经算是计算并绘制出来了。但是实在是太丑了,下面我来优化一下。

5、图表美化

5.1、整体基调的确认

因为现在流行深邃科技风(随口一编~),那咱们取色就顺着这个方向去。

设置背景颜色为 #010139,不过这里背景我是使用了一个div当做背景层来搞的。代码如下:

<style>
.chartsArea {
width: 1000px;
height: 600px;
position:relative;
} .chartsAreaBack{
background-color: #010139;
width: 100%;
height: 100%;
position: absolute;
opacity: 0.7;
top: 0;
} .charts {
width: 100%;
height: 100%;
}
</style>
<div class="chartsArea">
<div class="chartsAreaBack"></div>
<!-- 准备好一个 dom,并且设置好 width 和 height,后续在此 dom 中绘制图表 -->
<div id="chart" class="charts"></div>
</div>

然后调整一下图表里面文字的颜色。

option:{
...
// 设置标题的颜色
title: {
text: "设置图表的主基调色",
textStyle: {
color: '#fff'
}
},
...
// x 轴的配置,默认类型为 type="category" 类目轴。
xAxis: [{
// 设置 x 轴的数据, 使用 日期 在数据中的 序列号 来表示 横坐标数值。
data: chartData.date.map((seg, idx) => {
return idx
}),
// 设置 x 轴的 展示标签, 使其根据 当前标签的序列号转换为 日期
axisLabel: {
formatter: function (params) {
return chartData.date[params]
},
color: '#04a5bd',//设置标签的样式
fontWeight: 'bold'
}
},
// 添加一个x轴用于展示散点图(交点)
{
type: 'value',
min: 0,
max: chartData.date.length - 1,
show: false
}],
// y 轴配置标签颜色
yAxis: {
axisLabel: {
color: '#04a5bd',
fontWeight: 'bold'
}
}
...
}

然后,就是这样了,还是挺丑的哈~

5.2、对坐标轴进行优化

我们可以看到当下的x、y轴以及里面的轴线都很难看,我们再继续调整一下。

x轴调整坐标轴颜色以及刻度线的样式。

// 坐标轴线的颜色调整一下
axisLine: {
lineStyle: {
color: '#00386d',
opacity: 0.6
}
},
// 坐标轴刻度取消展示
axisTick: {
show: false
}

y 轴将分割线的颜色调整一下,并且取消y轴主轴线的展示。

splitLine: {
show: true,
lineStyle: {
color: '#00386d',
opacity: 0.4
}
},
axisLine: {
show: false
}

图表的取值靠在上半区,所以我们可以调整一下y轴的展示范围。

scale: true

调整完上面的以后,大概就是这个样子了(我偷偷加了个背景图~)

这样看起来就舒服一点了,不过还得继续调整。

5.3、调整折线图以及散点图的样式

我们先调整一下折线图的线条颜色、宽度以及形状等地方。

{
name: 'line1', // 单个图表系列的 name, 和 legend 中的 data 对应
type: 'line',
symbol:'none', // 取消折线图上圆点的展示
smooth:true, // 将折线进行平滑展示
itemStyle: { // 设置折线图颜色
color: '#6B72E7'
},
lineStyle: { // 设置线条的宽度
width: 0.7,
},
data: chartData.line1
}, {
name: 'line2', // 单个图表系列的 name, 和 legend 中的 data 对应
type: 'line',
symbol:'none',
smooth:true,
itemStyle: {
color: '#E93AC8'
},
lineStyle: {
width: 0.7,
},
data: chartData.line2
}

至于散点图,我想着上升交点以及下降交点的颜色给区分开来,所以需要改造一下前面计算交点的方法。

// 获取两线所有交点
function getIntersectionPoint({ line1, line2, date } = {}) {
// 交点数组
var intersectionPointList = []
date.map((seg, idx) => {
// 分别是两条线在相邻两处的数值,用于通过比较大小,来确定此段内是否有交点
var valueGroup = [line1[idx], line2[idx], line1[idx + 1], line2[idx + 1]]
if (ifCalculatePoint(idx, date.length, valueGroup)) {
var dotGroup = {
a: { x: idx, y: line1[idx] },
b: { x: idx + 1, y: line1[idx + 1] },
c: { x: idx, y: line2[idx] },
d: { x: idx + 1, y: line2[idx + 1] }
}
// 计算交点的位置
var intersectionPoint = this.segmentsIntr(dotGroup)
// 给每个数据拼接第三个值,代表是上升还是下降【新增】
intersectionPoint = [].concat(intersectionPoint,line1[idx + 1] > line2[idx + 1])
intersectionPointList.push(intersectionPoint)
}
})
return intersectionPointList;
}

然后,根据得出的值,设置散点图的样式。调整散点图的点形状、颜色。

{
name: 'scatter',
type: 'scatter',
xAxisIndex: 1,
data: getIntersectionPoint(chartData).map((seg) => {
return {
value: seg.slice(0, 2),
symbol:'pin',
symbolSize:30,
itemStyle: {
// 根据上升还是下降,来设置渐变色
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: seg[2]?'#E93AC8':'#6B72E7'
}, {
offset: 0.9,
color: '#08178c'
}, {
offset: 1,
color: '#08178c'
}], false)
}
}
})
}

哦!对了,前面忘记改图例的文字颜色,这里补充一下。

legend: {
data: ['line1', 'line2'],
textStyle: {
color: '#fff'
},
},

下面看一下效果,感觉是不是像个样了?

6、结语

其实针对这个图表还有很多可以做的地方,我这里只是给出了一个计算图表中两折线交点并绘制的大概方法,以及对于图表进行了一些简单的优化,希望此文章能够帮助你熟悉 Echarts 的用法,能够帮助你解决一些问题那就更好了~

github地址:https://github.com/JHCan333/can-Share/blob/master/demos-tips/getTwoLineNode.html

我是 JHCan333,公众号:爱生活的前端狗 的作者。公众号专注前端工程师方向,包括但不限于技术提高、职业规划、生活品质、个人理财等方面,会持续发布优质文章,从各个方面提升前端开发的幸福感。关注公众号,我们一起向前走!

详细介绍如何计算两条折线的交点并使用Echarts展示以及图表优化的更多相关文章

  1. 计算两条直线的交点(C#)

    PS:从其他地方看到的源码是有问题的.下面是修正后的 /// <summary> /// 计算两条直线的交点 /// </summary> /// <param name ...

  2. C++ 根据两点式方法求直线并求两条直线的交点

    Line.h #pragma once //Microsoft Visual Studio 2015 Enterprise //根据两点式方法求直线,并求两条直线的交点 #include"B ...

  3. Intersecting Lines--POJ1269(判断两条直线的关系 && 求两条直线的交点)

    http://poj.org/problem?id=1269 我今天才知道原来标准的浮点输出用%.2f   并不是%.2lf  所以wa了好几次 题目大意:   就给你两个线段 然后求这两个线段所在的 ...

  4. 求两条直线相交点 AS3代码

    ,); ,); ,); ,); var p:Point = new Point(); trace(checkPoint()) function checkPoint() { if (p1Start.x ...

  5. AS3 求两条直线的交点

    //粘贴到帧上运行即可 var p1Start:Point = new Point(0,0); var p1End:Point = new Point(50,50); var p2Start:Poin ...

  6. python中如何在一张图上画两条折线

    摘自:https://segmentfault.com/q/1010000002760775

  7. poj 1269(两条直线交点)

    Intersecting Lines Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 13481   Accepted: 59 ...

  8. 判断两条直线的位置关系 POJ 1269 Intersecting Lines

    两条直线可能有三种关系:1.共线     2.平行(不包括共线)    3.相交. 那给定两条直线怎么判断他们的位置关系呢.还是用到向量的叉积 例题:POJ 1269 题意:这道题是给定四个点p1, ...

  9. Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

随机推荐

  1. jieba库的使用

    jieba库的使用 jeiba库是一款很优秀的用于中文分词的第三方库,它通过一个汉词词典来确定汉字之间的关联概率,将概率较大的组成分词. 精准模式 把文本精准的分割开来,不存在冗余单词. jieba. ...

  2. 使用logstash同步MongoDB数据到es

    input{ mongodb{ codec => "json" uri => 'mongodb://127.0.0.1:27017/kuaibao' placehold ...

  3. shell判断用户是否已经在系统中登录

  4. MySQL05-- 客户端工具及SQL语句

    目录 MySQL客户端工具及SQL语句 一.客户端命令介绍 二.接收用户的SQL语句 三.字符集定义 四.字符集设置 五.select的高级用法(扩展) MySQL客户端工具及SQL语句 一.客户端命 ...

  5. PLC 控制系统资源

    之前整理的PC高级语言与PLC通讯代码下载链接:三菱:http://blog.sina.com.cn/s/blog_16d7d3ecb0102x6wj.html倍福:http://bbs.elecfa ...

  6. 请求体中需要的true和requests包put请求冲突了

    python  put请求,添加请求头 不知道怎么解决

  7. Python3.5-20190504-自我笔记浅拷贝和深拷贝

    浅拷贝和深拷贝 (自己一直搞不懂的) 1.直接赋值(把一个变量直接赋值给另一个变量),他们指向同一个内存的数据(右边的图不知道怎么画,就直接截图过来了.将就看看) ------------- 2.使用 ...

  8. python基础:5.请编写一个函数实现将IP地址转换成一个整数。

    如 10.3.9.12 转换规则为:        10            00001010
         3            00000011
         9          ...

  9. 双十一高并发场景背后的数据库RDS技术揭秘

    [战报]11月11日聚石塔(阿里云数据库RDS产品形态)峰值QPS突破X00w,Proxy 峰值QPS超过X00w. 双十一就要来了,全世界都为其疯狂,但是在双十一抢购中经常会出现几万人抢一个红包或者 ...

  10. JS中数据结构之栈

    1.栈的基本介绍 栈是一种高效的数据结构,因为数据只能在栈顶添加或删除,所以这样的操作很快,而且容易实现. 栈是一种特殊的列表,栈内的元素只能通过列表的一端访问,这一端称为栈顶.栈被称为一种后入先出( ...