链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。
好处:可以添加或移除任意项,它会按需扩容,且不需要移动其他元素。
 
与数组的区别:
    数组:可以直接访问任何位置的任何元素;
    链表:想要访问链表中的一个元素,需要从起点(表头)开始迭代列表直到找到所需的元素。
 
现实案例:康佳舞队、寻宝游戏
 
单向链表:一个节点只有链向下一个节点的链接;
双向链表:链接是双向的,一个链向下一个元素,另一个链向前一个元素
优点:双向链表提供了两种迭代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节点的下一个或前一个元素。在单向链表中,如果迭代列表时错过了要找的元素,就需要回到列表起点,重新开始迭代。
-------------------------------------------------------------------------------------------------------------
链表方法声明:
1. 定义Node辅助类;
2. 内部属性 length;
2. 存储第一个节点(head)的引用
序号
方法
说明
1
append(element) 向列表尾部添加一个新的项
2
insert(position, element) 向列表的特定位置插入一个新的项
3
remove(element)
从列表中移除一项
4
indexOf (element )
返回元素在列表中的索引。如果列表中没有该元素则返回-1
5
removeAt(position)
从列表的特定位置移除一项
6
isEmpty()
如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false
7
size ( )
返回链表包含元素个数。与数组的 length 属性类似
8
toString ( )
由于列表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值

链表实现:

 function LinkedList() {
// 定义辅助类Node
var Node = function(element) {
this.element = element; // element属性,即要添加到列表的元素
this.next = null; // next属性,即指向列表中下一个节点项的指针
} var length = 0; // 内部属性/私有变量
var head = null; // 第一个节点的引用 // 向列表的尾部添加一个新的项
this.append = function(element) {
var node = new Node(element), // 把element作为值传入,创建Node项
current; if (head === null) { // 列表中第一个节点,如果head元素为null,则意味着向列表添加第一个元素
head = node; // head指向node元素,下一个node将会自动生成null
} else {
current = head; // 循环列表,直到找到最后一项
while(current.next) {
current = current.next;
} // 找到最后一项,将其next赋为node,建立连接
current.next = node; // 列表中最后一个节点的下一个元素始终是null
} length++; // 更新链表长度,这样就能控制它,轻松地得到列表的长度
}; // 向列表的特定位置插入一个新的项
this.insert = function(position, element) {
// 检查越界值
if (position >= 0 && position <= length) {
var node = new Node(element),
current = head,
previous,
index = 0; if (position === 0) { // 在第一个位置添加
node.next = current;
head = node;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
node.next = current;
previous.next = node;
} length++; return true; } else {
return false;
}
}; // 从列表的特定位置移除一项
this.removeAt = function(position) {
// 检查越界值
if (position > -1 && position < length) {
var current = head, // current变量总是为对所循环列表的当前元素的引用
previous, // previous变量为对当前元素的前一个元素的引用
index = 0; // 移除第一项
if (position === 0) {
head = current.next;
} else {
while (index++ < position) { // 使用用于内部控制和递增的index变量来迭代列表
previous = current;
current = current.next;
} // 将previous与current的下一项链接起来:跳过current,从而移除它
previous.next = current.next;
} length--; return current.element;
} else {
return null;
}
}; // 从列表中移除一项
this.remove = function(element) {
var index = this.indexOf(element);
return this.removeAt(index);
}; // 返回元素在列表中的索引。如果列表中没有该元素则返回-1
this.indexOf = function(element) {
var current = head,
index = -1; while (current) {
if (element === current.element) {
return index;
}
index++;
current = current.next;
} return -1;
}; // 如果链表中不包含任何元素,返回 true,如果链表长度大于 0 则返回 false
this.isEmpty = function() {
return length === 0;
}; // 返回链表包含元素个数。与数组的 length 属性类似
this.size = function() {
return length;
}; // 由于列表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值
this.toString = function() {
var current = head,
string = ''; while (current) {
string += current.element;
current = current.next;
} return string;
}; this.getHead = function () {
return head;
};
}

LinkedList.js

 
双向链表实现:
 function DoublyLinkedList() {
var Node = function(element) {
this.element = element;
this.next = null;
this.prev = null;
}; var length = 0;
var head = null;
var tail = null; // 新增的 // 特定位置插入元素
this.insert = function(position, element) {
// 检查越界值
if (position >= 0 && position <= length) {
var node = new Node(element),
current = head,
previous,
index = 0; if (position === 0) { // 在第一个位置添加
if (!head) { // 新增的
head = node;
tail = node;
} else {
node.next = current;
current.prev = node;
head = node;
}
} else if (position === length-1) { // 最后一项 //新增的
current = tail;
current.next = node;
node.prev = current;
tail = node;
} else {
while (index++ < position) {
previous = current;
previous.next = node;
} node.next = current;
previous.next = node; current.prev = node; //新增的
node.prev = previous; //新增的
} length++;
return true; } else {
return false;
}
}; // 特定位置删除元素
this.removeAt = function(position) {
// 检查越界值
if (position > -1 && position < length) {
var current = head,
previous,
index = 0; // 移除第一项
if (position === 0) {
head = current.next; // 如果只有一项,更新tail //新增的
if (length === 1) {
tail = null;
} else {
head.prev = null;
}
} else if (position === length-1) {
current = tail;
tail = current.prev;
tail.next = null;
} else {
while (index++ < position) {
previous = current;
current = current.next;
}
// 将prvious与current的下一项链接起来——跳过current
previous.next = current.next;
current.next.prev = previous; // 新增的
} length--; return current.element;
} else {
return null;
}
};
}

DoublyLinkedList.js

参考书籍:《学习JavaScript数据结构与算法》

JavaScript数据结构——链表的更多相关文章

  1. JavaScript数据结构——链表的实现

    前面楼主分别讨论了数据结构栈与队列的实现,当时所用的数据结构都是用的数组来进行实现,但是数组有的时候并不是最佳的数据结构,比如在数组中新增删除元素的时候需要将其他元素进行移动,而在javascript ...

  2. JavaScript数据结构——链表的实现与应用

    链表用来存储有序的元素集合,与数组不同,链表中的元素并非保存在连续的存储空间内,每个元素由一个存储元素本身的节点和一个指向下一个元素的指针构成.当要移动或删除元素时,只需要修改相应元素上的指针就可以了 ...

  3. javascript数据结构-链表

    gihtub博客地址 链表 是一种物理存储单元上非连续.非顺序的存储结构,它既可以表示线性结构,也可以用于表示非线性结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每 ...

  4. JavaScript数据结构——字典和散列表的实现

    在前一篇文章中,我们介绍了如何在JavaScript中实现集合.字典和集合的主要区别就在于,集合中数据是以[值,值]的形式保存的,我们只关心值本身:而在字典和散列表中数据是以[键,值]的形式保存的,键 ...

  5. JavaScript数据结构——树的实现

    在计算机科学中,树是一种十分重要的数据结构.树被描述为一种分层数据抽象模型,常用来描述数据间的层级关系和组织结构.树也是一种非顺序的数据结构.下图展示了树的定义: 在介绍如何用JavaScript实现 ...

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

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

  7. 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表

    这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...

  8. JavaScript数据结构与算法-链表练习

    链表的实现 一. 单向链表 // Node类 function Node (element) { this.element = element; this.next = null; } // Link ...

  9. 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)

    定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...

随机推荐

  1. Java:标示符 基本数据类型

    标示符: 在程序中自定义的一些名称,例如:变量.类名.方法名…… 组成有数字0~9.大小写英文字母.“$”和下划线“_”组成,且不能由数字开头,以及不能使用java已使用和保留的关键字. Java中的 ...

  2. 在现有 SharePoint 服务器上安装 PowerPivot for SharePoint

    步骤1: 检查 SQL Server 2008 R2 Analysis Services 实例的“程序”文件夹.如果您找到了现有安装或之前安装的证据,则执行剩余步骤.否则,直接执行步骤 2:安装 Po ...

  3. sql2008 附加数据库出错解决方法

    当遇到 无法为此请求检索数据,(Microsoft.SqlServer.SmoEnum)其他信息执行Transact-Sql语句或批处理时发生了异常, Microsoft.SqlServer.Conn ...

  4. Codeigniter 集成sphinx搜索 这里采用的是coreseek中文搜索引擎,具体安装请参考官方网站

    先上效果图 加入sphinx类库(/application/libraries/sphinx_client.php) 0001 <?php 0002 0003 // 0004 // $Id: s ...

  5. java面向对象编程— —第七章 继承

    7.1继承的起源 继承(Inheritance),即在面向对象编程中,可以通过扩展(extends)一个已有的类,并继承该类的属性的行为,来创建一个新的类. 已有的类称为父类(也可以称为基类,超类), ...

  6. 国内HTML5前端开发框架汇总

    国内HTML5前端开发框架汇总 Dawei Cheng 程大伟... 于 星期日, 02/12/2012 - 20:53 提交 国外很有多优秀的HTML5前端开发框架相信大家都耳熟能详:JQuery ...

  7. Convert.ToInt16 与 Convert.ToInt32 区别

    取值的范围不同: int16:-32768 到 32767 int32:-2,147,483,648 到 2,147,483,647

  8. C# Async/Await异步函数原理

    原理 与同步函数相比,CLR在执行异步函数时有几个不同的特点: 1.        并非一次完成,而且分多次完成 2.        并非由同一个线程完成,而是线程池每次动态分配一个线程来处理: 结合 ...

  9. vim的编码设置

    VIM的相关字符编码主要有三个参数 fencs: 它是一个编码格式的猜测列表.当用vim打开某个文件时,会依次取这里面的编码进行解码,如果某个编码格式从头至尾解码正确,那么就用那个编码 fenc:它是 ...

  10. 图解傅里叶变换(so easy)

    话不多说先上两个GIF图. 第一个动画和第二个动画其实都是对时域的周期矩形形波(近似看成矩形波,并不是严格意义的矩形方波)进行傅里叶变换分析. 对于第一个图形来说,它侧重展示变换的本质之一:叠加性,每 ...