列表[List]的定义

列表是一组有序的数据。每个列表中的数据项称为元素。在 JavaScript 中,列表中的元素 可以是任意数据类型。列表中可以保存多少元素并没有事先限定,实际使用时元素的数量 受到程序内存的限制。

不包含任何元素的列表称为空列表。列表中包含元素的个数称为列表的 length。在内部实 现上,用一个变量 listSize 保存列表中元素的个数。可以在列表末尾 append 一个元素, 也可以在一个给定元素后或列表的起始位置 insert 一个元素。使用 remove 方法从列表中 删除元素,使用 clear 方法清空列表中所有的元素。

还可以使用 toString() 方法显示列表中所有的元素,使用 getElement() 方法显示当前元素。列表拥有描述元素位置的属性。列表有前有后(分别对应 front 和 end)。使用 next() 方 法可以从当前元素移动到下一个元素,使用 prev() 方法可以移动到当前元素的前一个元 素。还可以使用 moveTo(n) 方法直接移动到指定位置,这里的 n 表示要移动到第 n 个位置。 currPos 属性表示列表中的当前位置。

列表的抽象数据类型定义

属性/方法 描述
listSize 列表的元素个数
pos 列表的当前位置
length 返回列表中元素的个数
clear(方法) 清空列表中的所有元素
toString(方法) 返回列表的字符串形式
getElement(方法) 返回当前位置的元素
insert(方法) 在现有元素后插入新元素
append(方法) 在列表的末尾添加新元素
remove(方法) 从列表中删除元素
front(方法) 将列表的当前位置设移动到第一个元素
end(方法) 将列表的当前位置移动到最后一个元素
prev(方法) 将当前位置后移一位
next(方法) 将当前位置前移一位
currPos(方法) 返回列表的当前位置
moveTo(方法) 将当前位置移动到指定位置
contains(方法) 判断给定值是否在列表中
getElement(方法) 显示当前值

实现列表类

根据上面定义的列表抽象数据类型,可以直接实现一个 List 类。让我们从定义构造函数开 始,虽然它本身并不是列表抽象数据类型定义的一部分:

class List {
constructor() {
this.dataSource = [];
this.listSize = 0;
this.pos = 0;
}
clear() {}
find() {}
toString() {}
insert() {}
append() {}
remove() {}
front() {}
end() {}
prev() {}
next() {}
length() {}
currPos() {}
moveTo() {}
getElement() {}
contains() {}
}

append:给列表添加元素

append(element) {
this.dataStore[this.listSize++] = element;
}

当新元素就位后,变量 listSize 加 1。

find:在列表中查找某一元素

find() 方法通过对数组对象 dataStore 进行迭代,查找给定的元素。如果找到,就返回该 元素在列表中的位置,否则返回 -1,这是在数组中找不到指定元素时返回的标准值。我们 可以在 remove() 方法中利用此值做错误校验。

find(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return i;
}
}
return -1;
}

remove:从列表中删除元素

remove() 方法是 List 类中较难实现的 一个方法。首先,需要在列表中找到该元素,然后删除它,并且调整底层的数组对象以填 补删除该元素后留下的空白。好消息是,可以使用数组的 splice() 方法简化这一过程。

remove(element) {
const index = this.find(element);
if (index === -1) {
return false
}
this.dataStore.splice(index, 1);
--this.listSize;
return true;
}

length:列表中有多少个元素

length() 方法返回列表中元素的个数:

length() {
return this.listSize;
}

toString:显示列表中的元素

现在是时候创建一个方法,用来显示列表中的元素了。

toString() {
return this.dataStore.toString();
}

insert:向列表中插入一个元素

接下来要讨论的方法是 insert()。如果在List中间位置删除了一个元素,但是现在又想将 它放回原来的位置,该怎么办? insert() 方法需要知道将元素插入到什么位置,因此现在 我们假设插入是指插入到列表中某个元素之后。

insert(element, index) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index + 1, 0, element);
++this.listSize;
return true;
}

clear:清空列表中所有的元素

clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}

contains:判断给定值是否在列表中

当需要判断一个给定值是否在列表中时,contains() 方法就变得很有用。

contains(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return true;
}
}
return false;
}

也可以使用之前实现的find方法

遍历列表

最后的一组方法允许用户在列表上自由移动

front() {
this.pos = 0;
}
end() {
this.pos = this.listSize - 1;
}
prev() {
if (this.pos > 0) {
--this.pos;
}
}
next() {
if (this.pos < this.listSize - 1) {
++this.pos;
}
}
currPos() {
return this.pos;
}
moveTo(position) {
this.pos = position;
}
getElement() {
return this.dataStore[this.pos];
}

使用迭代器访问列表

使用迭代器,可以不必关心数据的内部存储方式,以实现对列表的遍历。前面提到的方法 front()end()prev()next() currPos 就实现了 List 类的一个迭代器。以下是和使 用数组索引的方式相比,使用迭代器的一些优点。

访问列表元素时不必关心底层的数据存储结构。

当为列表添加一个元素时,索引的值就不对了,此时只用更新列表,而不用更新迭代器。

可以用不同类型的数据存储方式实现 List 类,迭代器为访问列表里的元素提供了一种 统一的方式。 了解了这些优点后,来看一个使用迭代器遍历列表的例子:

for(lists.front(); lists.currPos() < lists.length(); lists.next()) {
console.log(lists.getElement());
}

在 for 循环的一开始,将列表的当前位置设置为第一个元素。只要 currPos 的值小于列表 的长度,就一直循环,每一次循环都调用 next() 方法将当前位置向前移动一位。

同理,还可以从后向前遍历列表,代码如下:

for(lists.end(); lists.currPos() >= 0; lists.prev()) {
console.log(lists.getElement());
}

循环从列表的最后一个元素开始,当当前位置大于或等于 0 时,调用 prev() 方法后移 一位。

迭代器只是用来在列表上随意移动,而不应该和任何为列表增加或删除元素的方法一起 使用。

完整代码

class List {
constructor() {
this.dataStore = [];
this.listSize = 0;
this.pos = 0;
}
clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
find(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return i;
}
}
return -1;
}
toString() {
return this.dataStore.toString();
}
insert(element, index) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index + 1, 0, element);
++this.listSize;
return true;
}
append(element) {
this.dataStore[this.listSize++] = element;
}
remove(element) {
const index = this.find(element);
if (index === -1) {
return false;
}
this.dataStore.splice(index, 1);
--this.listSize;
return true;
}
length() {
return this.listSize;
}
contains(element) {
for (let i = 0, len = this.dataStore.length; i < len; ++i) {
if (this.dataStore[i] == element) {
return true;
}
}
return false;
}
front() {
this.pos = 0;
}
end() {
this.pos = this.listSize - 1;
}
prev() {
if (this.pos > 0) {
--this.pos;
}
}
next() {
if (this.pos < this.listSize - 1) {
++this.pos;
}
}
currPos() {
return this.pos;
}
moveTo(position) {
this.pos = position;
}
getElement() {
return this.dataStore[this.pos];
}
}

参考资料

  • 数据结构与算法JavaScript描述

数据结构之List | 让我们一块来学习数据结构的更多相关文章

  1. 数据结构之Queue | 让我们一块来学习数据结构

    前面的两篇文章分别介绍了List和Stack,下面让我们一起来学习Queue 数据结构之List | 让我们一块来学习数据结构 数据结构之Stack | 让我们一块来学习数据结构 队列的概况 队列是一 ...

  2. 数据结构之LinkedList | 让我们一块来学习数据结构

    highlight: monokai theme: vue-pro 上一篇文章中使用列表(List)对数据排序,当时底层储存数据的数据结构是数组.本文将讨论另外一种列表:链表.我们会解释为什么有时链表 ...

  3. 数据结构之Set | 让我们一块来学习数据结构

    数组(列表).栈.队列和链表这些顺序数据结构对你来说应该不陌生了.现在我们要学习集合,这是一种不允许值重复的顺序数据结构.我们将要学到如何创建集合这种数据结构,如何添加和移除值,如何搜索值是否存在.你 ...

  4. 数据结构之Stack | 让我们一块来学习数据结构

    栈的介绍 栈就是和列表类似的一种数据结构,它可用来解决计算机世界里的很多问题.栈是一种高 效的数据结构,因为数据只能在栈顶添加或删除,所以这样的操作很快,而且容易实现. 栈的使用遍布程序语言实现的方方 ...

  5. 《Java程序设计与数据结构教程(第二版)》学习指导

    <Java程序设计与数据结构教程(第二版)>学习指导 欢迎关注"rocedu"微信公众号(手机上长按二维码) 做中教,做中学,实践中共同进步! 原文地址:http:// ...

  6. SqList *L 和 SqList * &L的区别/学习数据结构突然发现不太懂 小祥我查找总结了一下

    小祥在学习李春葆的数据结构教程时发现一个小问题,建立顺序表和输出线性表,这两个函数的形参是不一样的. 代码在这里↓↓↓ //定义顺序表L的结构体 typedef struct { Elemtype d ...

  7. 在Object-C中学习数据结构与算法之排序算法

    笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速 ...

  8. MySQL 学习 --- 数据结构和索引

    本文参考了多篇文章集成的笔记,希望各位学习之前可以阅读以下参考资料先 概述 文章分几个部分 :第一部分介绍了B-Tree 和 B+Tree 这种数据结构作为索引:第二部分介绍索引的最左前缀原则和覆盖索 ...

  9. 省选算法学习-数据结构-splay

    于是乎,在丧心病狂的noip2017结束之后,我们很快就要迎来更加丧心病狂的省选了-_-|| 所以从写完上一篇博客开始到现在我一直深陷数据结构和网络流的漩涡不能自拔 今天终于想起来写博客(只是懒吧.. ...

随机推荐

  1. 研究了一下 Webpack 打包原理,顺手挣了个 AirPods Pro

    这些年,Webpack 基本成了前端项目打包构建的标配.关于它的原理和用法的文章在网上汗牛充栋,大家或多或少都看过一些.我也一样,大概了解过它的构建过程以及常用 loader 和 plugin 的配置 ...

  2. KDE 桌面不显示背景和状态栏

    在arch的一次更新后,kde的桌面背景,插件,状态栏变成了一片黑,如下图所示. 解决办法:删除/home/stone/.config/plasma-org.kde.plasma.desktop-ap ...

  3. OAID 文档与获取DemoAPK

    OAID 查看器 接入 OAID SDK 的 Demo 工程 完整项目地址 Github 文档与 Demo 下载 release 提供 APK 下载 支持设备见 说明文档 常见问题见 F&Q文 ...

  4. 全局解决Vue跳转相同路由导致报错的问题

    大家使用Vue做开发的时候应该都遇到过这个问题,就是某个页面下调用this.$router.push(path),而path指向的页面和当前页面是同一页面时,就会发生报错,vue-router会提示你 ...

  5. 理解ASP.NET Core 中的WebSocket

    在本文中,我们将详细介绍RFC 6455 WebSocket规范,并配置一个通用的.NET 5应用程序通过WebSocket连接与SignalR通信. 我们将深入底层的概念,以理解底层发生了什么. 关 ...

  6. 【Azure 服务总线】Azure Service Bus中私信(DLQ - Dead Letter Queue)如何快速清理

    在博文ServiceBus 队列中死信(DLQ - Dead Letter Queue)问题一文中,介绍了服务总线产生私信的原因及可以通过代码的方式来清楚私信队列中的消息,避免长期占用空间(因为私信中 ...

  7. IDEA修改jar包中class文件后重新生成jar包

    一.背景 最新想要修改rebeyond大佬的冰蝎项目,特地去网上搜索如何修改jar包中的源码再替换回去的方法,但由于现在的一些文章写的太烂,导致走了很多弯路,因此写下这篇快速使用IDEA修改源码并替换 ...

  8. 如何报告FreeBSD 的bug?

    https://bugs.freebsd.org/bugzilla/ 注册个账号即可,请使用英语,把程序在不同程序上的运行结果列出来即可- ​​​​ 注意标记架构,如果有log还请一并附上,英语差可以 ...

  9. C# 应用 - 封装类访问 Mysql 数据库

    个人经历的项目主要都是用 Postgresql 或 Oracle 数据库,本文非原创,从他处整理而来. 1. 库类 mysql.data.dll using MySql.Data.MySqlClien ...

  10. EmEditor, 在正则使用()匹配后 使用$1 $2进行对括号内的值进行引用

    $1表示第一个括号,$2表示第二个括号,以此类推