上一节中, 线性表的顺序存储结构的特点是逻辑关系上相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,它的存储位置可用一个简单,直观的公式来表示。然后,另一方面来看,这个特点也造成这种存储结构的弱点,在做插入或删除操作时,需移动大量元素

链式存储结构,由于它不需要逻辑上相邻的元素在物理位置上也相邻,因此它没有顺序存储结构所具有的弱点,但同时也失去了顺序表可随机存取的优点

线性链表

wiki中的定义:

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

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。链表通常由一连串节点组成,每个节点包含任意的实例数据(data fields)和一或两个用来指向明上一个/或下一个节点的位置的链接("links")。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的访问往往要在不同的排列顺序中转换。而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表,双向链表以及循环链表。

链表可以在多种编程语言中实现。像LispScheme这样的语言的内建数据类型中就包含了链表的访问和操作。程序语言或面向对象语言,如C/C++和Java依靠易变工具来生成链表。

线性表的链式存储结构的特点是用一组任意的存储单元储存线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素a(i)与其直接后继数据元素a(i+1)之间的逻辑关系,对数据元素a(i)来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素a(i)的存储映像,称为结点(node)。它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域,指针域中存储的信息称做指针

又由于此链表的每个结点中只包含一个指针域,故又称线性链表单链表

单链表的整个链表的存取必须从头指针开始进行,头指针指示链表中第一个结点(即第一个数据元素的存储映像)的存储位置。

同时,由于最后一个数据元素没有直接后继,则线性链表中最后一个结点的指针为空null。

链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。


一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接

一个单向链表的节点被分成两个部分。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历。

 // 线性表的单链表存储结构
function LNode(data, node) {
this.data = data;
this.next = node || null;
}

假设p是指向线性表中第i个数据元素(结点a(i))的指针,则p->next是指向第i+1个数据元素(结点a(i+1))的指针。

下面我们来看GetElem在单链表中的实现:

 function getElem(i) {
// 初始化,p指向第一个节点,j为计数器
var p = this.next;
var j = 1;
// 顺指针向后查找,知道p指向第i个元素或p为空
while (p && j < i) {
p = p.next;
++j;
}
// 第i个元素不存在
// 或者取第i个元素
return (!p || j > i) ? null : p.data;
}

单链表的基本操作:

假设我们在线性表的两个数据元素a和b之间插入一个数据元素x,已知p为其单链表存储结构中指向结点a的指针。

插入:

假设s为指向结点x的指针,则可用语句描述:s->next = p->next;  p->next = s;

删除:

假设p为指向结点a的指针,则修改指针的语句为: p->next = p->next->next;

实现:

 function listInsert(i, data) {
var j = 0;
var p = this;
// 寻找第i-1个节点
while (p && j < i - 1) {
p = p.next;
++j;
}
// i < 1或者大于表长+1
if (!p || j > i - 1) return false;
// 生成新节点,插入p节点后面
p.next = new LNode(data, p.next);
return true;
} function listDelete(i) {
var j = 0;
var p = this; while (p.next && j < i - 1) {
p = p.next;
++j;
} if (!p.next || j > i - 1) return false;
var q = p.next;
p.next = q.next;
return q.data;
}

单链表的其他操作

逆位序输入n个元素的值,建立带表头结点的单链线性表L。

function createList_L(n) {
var deferred = require('rsvp').defer();
var l = new LNode();
var count = n;
process.stdin.setEncoding('utf8'); process.stdin.on('data', function handler(data) {
console.log(123);
data = data.replace('\n', '');
l.next = new LNode(data, l.next);
if (!(--count)) {
console.log('pausing');
process.stdin.pause();
deferred.resolve(l);
}
}); return deferred.promise;
}

假设头指针为La和Lb的单链表分别为线性表LA和LB的存储结构,先要归并La和Lb得到单链表Lc:

 function mergeList(a, b) {
var pa = a.next;
var pb = b.next;
// 用a的头结点作为c的头结点
var c = a;
var pc = a; while (pa && pb) {
if (pa.data <= pb.data) {
pc.next = pa;
pc = pa;
pa = pa.next;
} else {
pc.next = pb;
pc = pb;
pb = pb.next;
}
} // 插入剩余段
pc.next = pa ? pa : pb; return c;
}

结构图:

完整代码:

 // 单链表
/*
线性链表存储结构
整个链表的存取必须从头指针开始进行,头指针指示链表中第一个结点(即第一个数据元素的存储映像)的存储位置。
同时,由于最后一个数据元素没有直接后继,则线性链表中最后一个结点的指针为空null。
*/ function LNode(data, node) {
this.data = data;
this.next = node || null;
}
LNode.prototype = {
// 时间复杂度O(n)
getElem: function getElem(i) {
// 初始化,p指向第一个节点,j为计数器
var p = this.next;
var j = 1;
// 顺指针向后查找,知道p指向第i个元素或p为空
while (p && j < i) {
p = p.next;
++j;
}
// 第i个元素不存在
// 或者取第i个元素
return (!p || j > i) ? null : p.data;
},
// 时间复杂度O(n)
listInsert: function listInsert(i, data) {
var j = 0;
var p = this;
// 寻找第i-1个节点
while (p && j < i - 1) {
p = p.next;
++j;
}
// i < 1或者大于表长+1
if (!p || j > i - 1) return false;
// 生成新节点,插入p节点后面
p.next = new LNode(data, p.next);
return true;
},
listDelete: function listDelete(i) {
var j = 0;
var p = this; while (p.next && j < i - 1) {
p = p.next;
++j;
} if (!p.next || j > i - 1) return false;
var q = p.next;
p.next = q.next;
return q.data;
}
}; LNode.createList_L = function createList_L(n) {
var deferred = require('D:\\node\\node_modules\\rsvp').defer();
var l = new LNode();
var count = n;
process.stdin.setEncoding('utf8'); process.stdin.on('data', function handler(data) {
console.log(123);
data = data.replace('\n', '');
l.next = new LNode(data, l.next);
if (!(--count)) {
console.log('pausing');
process.stdin.pause();
deferred.resolve(l);
}
}); return deferred.promise;
}; function deepCopy(obj) {
var newObj = {}; for (var i in obj) {
if (typeof obj[i] === 'object') {
newObj[i] = deepCopy(obj[i]);
} else {
newObj[i] = obj[i];
}
} return newObj;
} // TODO
/*
已知单链线性表a和b的元素按值非递减排列。
归并a和b得到新的单链线性表c,c的元素也按值非递减排列。
*/
LNode.mergeList = function mergeList(a, b) {
var pa = a.next;
var pb = b.next;
// 用a的头结点作为c的头结点
var c = a;
var pc = a; while (pa && pb) {
if (pa.data <= pb.data) {
pc.next = pa;
pc = pa;
pa = pa.next;
} else {
pc.next = pb;
pc = pb;
pb = pb.next;
}
} // 插入剩余段
pc.next = pa ? pa : pb; return c;
}; function log(list) {
var arr = []; do {
arr.push(list.data);
list = list.next;
} while (list); console.log(arr.join(','));
} void function test() {
var a1 = new LNode(1);
a1.listInsert(1, 2);
a1.listInsert(2, 3);
a1.listInsert(1, 4);
console.log(a1.getElem(1));
console.log(a1);
log(a1);
a1.listDelete(1);
console.log('a1 linkList:');
console.log(a1);
log(a1);
/*
LNode.createList_L(5)
.then(function(list){
console.log(list);
});
*/
var a2 = new LNode(3);
a2.listInsert(1, 3);
a2.listInsert(2, 8);
a2.listInsert(1, 4);
a2.listDelete(2);
console.log('a2 linkList');
log(a2); var a3 = LNode.mergeList(a2, a1);
console.log('merging linkLists');
console.log(a3);
log(a3);
}();

javascript实现数据结构:线性表--线性链表(链式存储结构)的更多相关文章

  1. c数据结构 -- 线性表之 顺序存储结构 于 链式存储结构 (单链表)

    线性表 定义:线性表是具有相同特性的数据元素的一个有限序列 类型: 1:顺序存储结构 定义:把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构 算法: #include <stdio. ...

  2. [置顶] ※数据结构※→☆线性表结构(queue)☆============优先队列 链式存储结构(queue priority list)(十二)

    优先队列(priority queue) 普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除.在优先队列中,元素被赋予优先级.当访问元素时,具有最高优先级的元素最先删除.优先队列具有 ...

  3. c数据结构 -- 线性表之 复杂的链式存储结构

    复杂的链式存储结构 循环链表 定义:是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环) 优点:从表中任一节点出发均可找到表中其他结点 注意:涉及遍历操作时,终止条件是判断 ...

  4. C++编程练习(2)----“实现简单的线性表的链式存储结构“

    单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素. 对于查找操作,单链表的时间复杂度为O(n). 对于插入和删除操作,单链表在确定位置后,插入和删除时间仅为O(1). 单链表不需要分配存储 ...

  5. C++线性表的链式存储结构

    C++实现线性表的链式存储结构: 为了解决顺序存储不足:用线性表另外一种结构-链式存储.在顺序存储结构(数组描述)中,元素的地址是由数学公式决定的,而在链式储存结构中,元素的地址是随机分布的,每个元素 ...

  6. C语言实现链表(链式存储结构)

    链表(链式存储结构)及创建 链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据.与顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其 ...

  7. C++编程练习(6)----“实现简单的队列的链式存储结构“

    队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出.简称链队列. 实现代码如下: /* LinkQueue.h 头文件 */ #include<iostream> #defi ...

  8. java资料——顺序存储结构和链式存储结构(转)

    顺序存储结构 主要优点 节省存储空间,随机存取表中元素 缺    点 插入和删除操作需要移动元素 在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构. 顺序存储结 ...

  9. C++编程练习(4)----“实现简单的栈的链式存储结构“

    如果栈的使用过程中元素数目变化不可预测,有时很小,有时很大,则最好使用链栈:反之,如果它的变化在可控范围内,使用顺序栈会好一些. 简单的栈的链式存储结构代码如下: /*LinkStack.h*/ #i ...

随机推荐

  1. 网页绘制图表 Google Charts with JavaScript #2 ....与ASP.NET网页结合 (ClientScriptManager.RegisterStartupScript 方法)

    此为文章备份,原文出处(我的网站) 网页绘制图表 Google Charts with JavaScript #2 ....与ASP.NET网页结合 (ClientScriptManager.Regi ...

  2. [FAQ]String(字串相連)與StringBuilder的差別、原理與優缺點?

    原文位於 http://www.dotblogs.com.tw/mis2000lab/archive/2013/09/09/msdn_string_stringbuilder.aspx [FAQ]St ...

  3. yhd日志分析(二)

    yhd日志分析(二) 继续yhd日志分析,统计数据 日期 uv pv 登录人数 游客人数 平均访问时长 二跳率 独立ip数 1 分析 登录人数 count(distinct endUserId) 游客 ...

  4. 《Prism 5.0源码走读》ModuleCatalog

    概念 ModuleCatalog 是Prism中主要概念之一,主要用来保存应用程序可用的modules(模块),每个module都是用ModuleInfo来定义(包含module的名称.类型和位置). ...

  5. 算法系列3《SHA》

    SHA是一种数据加密算法,该算法经过加密专家多年来的发展和改进已日益完善,现在已成为公认的最安全的散列算法之一,并被广泛使用.该算法的思想是接收一段明文,然后以一种不可逆的方式将它转换成一段(通常更小 ...

  6. 应用程序域(Application Domain)

    应用程序域为隔离正在运行的应用程序提供了一种灵活而安全的方法. 应用程序域通常由运行时宿主创建和操作. 有时,您可能希望应用程序以编程方式与应用程序域交互,例如想在不停止应用程序运行的情况下卸载某个组 ...

  7. Android--获取标题栏,状态栏,屏幕高度

    获取状态栏高度 Rect frame = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); int ...

  8. Go安装

    http://www.linuxidc.com/Linux/2015-02/113159.htm https://github.com/astaxie/beego http://www.sizeofv ...

  9. 解压vmlinuz和解压initrd(initramfs)

    有时就算只得到一个Linux kernel的rpm包或者直接是编译后的vmlinuz和initrd的binary文件,也需要了解其中的一些细节,可能需要去查找这些binary有没有将我想要的patch ...

  10. Go channel同步

    我们可以使用Channel来同步不同goroutines的执行.看下面的代码: package main import "fmt" import "time" ...