JavaScript 本身提供了十分好用的数据类型,以满足大家的日常使用。单靠 Array  和 Object 也的确足够应付日常的绝大部分需求,这也导致了很多前端er对数据结构这一块不是十分的了解。

数据结构是 CS 的必修科目,前端这个圈子非科班的童鞋比例较高,相信很多人对数据结构的了解并不多。虽然本人大学读的也是 CS,但那时候上课基本都在睡觉,数据结构也是学得一塌糊涂,现在也基本上全忘了。

那下面我们来了解一下,如何用 JavaScript 来实现一个 单向链表

维基百科中,对链表的描述如下:

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

由此可以知道,链表和数据相比,会具有一下优点:

  1. 不需要预先分配内存(在静态语言中,数组是需要预先分配内存空间的,空间分配后不能再改变​​)
  2. 插入和删除元素不需要移动其余元素,效率更高

JavaScript 作为一门动态语言,其中的数组也是动态数组,不需要预先分配内存的。在使用过程中还可以动态增减数组长度,这直接让 Array 类型的适用范围扩大了不少。

数组在进行插入和删除元素时,是十分麻烦的事情,而这方面却是链表的优势。在查找元素的效率方面,链表就比不上数组那么方便快捷了。

单向链表,是链表中最简单的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始。

下面我们看看 单向链表 的图示:

可以看到,链表中每一个 item(元素) 都有两个部分组成:data 和 next。其中 data 用于存储数据,是真正的存储区。而 next 用于存放地址,用于指向下一个 item。

下面我们看看怎么设计一个单向列表。

一般的单向列表,都具有以下的属性和方法:

  • first:指向第一个 item
  • last:指向最后一个 item
  • length:链表的 item 数量
  • get():获取指定位置的 item
  • set():修改指定位置的 item
  • add():插入一个新的 item 到指定位置
  • remove():移除指定位置的 item
  • clear():清空所有 item

为了方便实用,还提供了部分语法糖:

  • addFirst():插入一个新的 item 到链表头部
  • addLast():插入一个新的 item 到链表尾部
  • removeFirst():移除第一个 item
  • removeLast():移除最后一个 item
  • toString():列表以 "10 -> a -> [1,2] -> 3.14" 方式输出

下面是 SinglyLinkedList 的类:

var SinglyLinkedList = function () {

    function SinglyLinkedList() {
this.length = 0;
this.first = null;
this.last = null;
} /**
* 根据索引获取 item
* @param {Number} index 链表索引
* @returns {*}
*/
SinglyLinkedList.prototype.get = function (index) {
if (typeof index !== 'number' || index < 0 || index >= this.length) {
return undefined;
} var item = this.first;
for (var i = 0; i < index; i++) {
item = item.next;
}
return item;
}; /**
* 根据索引设置 item 的内容
* @param {Number} index 链表索引
* @param {*} value 需要设置的值
* @returns {*}
*/
SinglyLinkedList.prototype.set = function (index, value) {
if (typeof index !== 'number' || index < 0 || index >= this.length) {
return false;
} var item = this.get(index);
item.data = value;
return item;
}; /**
* 根据索引位置插入新的 item
* @param {Number} index 链表索引
* @param {*} value 需要设置的值
* @returns {*}
*/
SinglyLinkedList.prototype.add = function (index, value) {
if (typeof index !== 'number' || index < 0 || index > this.length || index === undefined) {
return false;
} var item = {
data: value,
next: null
}; if (this.length > 0) {
if (index === 0) {
item.next = this.first;
this.first = item;
} else if (index === this.length) {
this.last.next = item;
this.last = item;
} else {
var prevItem = this.get(index - 1),
nextItem = this.get(index);
item.next = nextItem;
prevItem.next = item;
}
} else {
this.first = item;
this.last = item;
} this.length++;
return item;
}; /**
* 根据索引删除 item
* @param {Number} index 链表索引
* @returns {boolean}
*/
SinglyLinkedList.prototype.remove = function (index) {
if (typeof index !== 'number' || index < 0 || index >= this.length) {
return false;
} var item = this.get(index); if (this.length > 1) {
if (index === 0) {
this.first = item.next;
} else if (index === this.length - 1) {
this.last = this.get(this.length - 2);
this.last.next = null;
} else {
this.get(index - 1).next = item.next;
}
} else {
this.first = null;
this.last = null;
} item = null;
this.length--;
return true;
}; /**
* 清空整个单链表
* @returns {boolean}
*/
SinglyLinkedList.prototype.clear = function () {
this.first = null;
this.last = null;
this.length = 0;
return true;
}; SinglyLinkedList.prototype.addFirst = function (value) {
return this.add(0, value);
}; SinglyLinkedList.prototype.addLast = function (value) {
return this.add(this.length, value);
}; SinglyLinkedList.prototype.removeFirst = function () {
return this.remove(0);
}; SinglyLinkedList.prototype.removeLast = function () {
return this.remove(this.length - 1);
}; SinglyLinkedList.prototype.toString = function () {
var arr = [],
item = {}; if (this.length) {
do {
item = item.next || this.get(0);
arr.push(typeof item.data === 'object' ? JSON.stringify(item.data).replace(/\"/g, '') : item.data);
} while (item.next);
} return arr.join(' -> ');
}; return SinglyLinkedList;
}();

使用方法很简单:

var sList = new SinglyLinkedList();
sList.addLast('a');
sList.addFirst(10);
sList.addLast(3.14);
sList.add(2, [1, 2]);
sList.addLast({a: 1, b: 2});
console.log(sList.toString()); // "10 -> a -> [1,2] -> 3.14 -> {a:1,b:2}"
console.log(sList.length); //
sList.removeFirst();
sList.removeLast();
console.log(sList.toString()); // "a -> [1,2] -> 3.14"
console.log(sList.length); //

参考资料:

http://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8

http://www.cnblogs.com/skywang12345/p/3561803.html#a33

本文作者:Maple Jan

本文链接:http://www.cnblogs.com/maplejan/p/3903749.html

JavaScript实现单向链表的更多相关文章

  1. JavaScript实现单向链表结构

    参考资料 一.什么是链表结构? 1.1.简介 链表和数组一样, 可以用于存储一系列的元素, 但是链表和数组的实现机制完全不同,链表中的元素在内存不是连续的空间,链表的每个元素由一个存储元素本身(数据) ...

  2. 使用JavaScript实现单向链表

    一.实现功能 1.链表元素头部插入 this.unShift = function(data) {} 2.链表元素尾部插入 this.append= function(data) {} //返回boo ...

  3. 学习javascript数据结构(二)——链表

    前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...

  4. Reverse Linked List II 单向链表逆序(部分逆序)

    0 问题描述 原题点击这里. 将单向链表第m个位置到第n个位置倒序连接.例如, 原链表:1->2->3->4->5, m=2, n =4 新链表:1->4->3-& ...

  5. 【编程题目】输入一个单向链表,输出该链表中倒数第 k 个结点

    第 13 题(链表):题目:输入一个单向链表,输出该链表中倒数第 k 个结点.链表的倒数第 0 个结点为链表的尾指针.链表结点定义如下: struct ListNode {int m_nKey;Lis ...

  6. 输出单向链表中倒数第k个结点

    描述 输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第0个结点为链表的尾指针. 链表结点定义如下: struct ListNode { int       m_nKey; ListNode* ...

  7. Linus:利用二级指针删除单向链表

    Linus大神在slashdot上回答一些编程爱好者的提问,其中一个人问他什么样的代码是他所喜好的,大婶表述了自己一些观点之后,举了一个指针的例子,解释了什么才是core low-level codi ...

  8. 【转】Linus:利用二级指针删除单向链表

    原文作者:陈皓 原文链接:http://coolshell.cn/articles/8990.html 感谢网友full_of_bull投递此文(注:此文最初发表在这个这里,我对原文后半段修改了许多, ...

  9. C语言实现单向链表及其各种排序(含快排,选择,插入,冒泡)

    #include<stdio.h> #include<malloc.h> #define LEN sizeof(struct Student) struct Student / ...

随机推荐

  1. 课堂alpha发布

    项目组名:奋斗吧兄弟 今天七组对于各自项目现有的成果进行了alpha发布,下面是我的一些感想. 天天向上团队的连连看游戏: 令我印象最深的是天天向上团队的连连看项目,他们目前能展示给我们的是核心的连连 ...

  2. MySQL数据库中varchar与char类型的区别

    在数据库中建表时,需要给数据定义一个数据库中的数据库类型,当需要给String类型定义一个数据库中的类型时,可以看见有两个选择,一个是varchar,另一个是char,有很多人不清楚两者的区别,包括自 ...

  3. IPV4和IPV6的区别

    一.扩展了路由和寻址的能力 IPv6 把 IP 地址由 32 位增加到 128 位,从而能够支持更大的地址空间,估计在地球表面每平米有 4*10^18 个 IPv6 地址,使 IP 地址在可预见的将来 ...

  4. Idea(二) 解决IDEA卡顿问题及相关基本配置

    一.IDEA太卡顿,设置使用IDEA的内存 在IDEA的安装目录下的bin目录下: 打开设置: 将idea.exe.vmoptions文件内由-server-Xms128m-Xmx512m-XX:Ma ...

  5. IDEA 开发工具的快捷键

    IDEA 开发工具的快捷键 原文链接:http://blog.csdn.net/wfp458113181wfp/article/details/24579781 1.文本编辑 删除    ctr + ...

  6. C++模式学习------代理模式

    Proxy代理模式 : 为其他对象提供一种代理以控制对这个对象的访问.代理类作为桥梁是请求方和执行方的中间者,将请求方和真正的执行方分割开来,也是两者之间调用的协调者.例如执行类也就是被代理类,可以在 ...

  7. python中使用%与.format格式化文本

    初学python,看来零零碎碎的格式化文本的方法,总结一下python中格式化文本的方法.使用不当的地欢迎指出谢谢. 1.首先看使用%格式化文本 常见的占位符: 常见的占位符有: %d 整数 %f 浮 ...

  8. attention、self-attention、transformer和bert模型基本原理简述笔记

    attention 以google神经机器翻译(NMT)为例 无attention: encoder-decoder在无attention机制时,由encoder将输入序列转化为最后一层输出state ...

  9. 【BZOJ1046】上升序列(动态规划,贪心)

    [BZOJ1046]上升序列(动态规划,贪心) 题面 BZOJ 洛谷 题解 我一开始看错题了,一度以为是字典序最小的序列. 最后发现它要求的字典序是位置的字典序最小. 那就很好办了. 设\(f[i]\ ...

  10. 【POJ2411】Mondriaan's Dream(轮廓线DP)

    [POJ2411]Mondriaan's Dream(轮廓线DP) 题面 Vjudge 题解 这题我会大力状压!!! 时间复杂度大概是\(O(2^{2n}n^2)\),设\(f[i][S]\)表示当前 ...