和线性表的链式存储结构相类似,也可采用链式方式存储串值。由于串结构的特殊性--结构中的每个数据元素是一个字符,则用链表存储串值时,存在一个“结点大小”的问题,即每个结点可以存放一个字符,也可以存放多个字符。

下面是结点大小为4(即每个结点存放4个字符)的链表:

head --> (a) --> (b) --> (c) --> ... --> (i)

当结点大小大于1时,由于串长不一定是结点大小的整倍数,则链表中的最后一个结点不一定全被串值占满,此时通常补上“#”或其它非串值字符。

为了便于进行串的操作,当以链表存储串值时,除头指针外还可附设一个尾指针指示链表中的最后一个结点,并给出当前串的长度,称如此定义的串存储结构为块链结构。

由于一般情况下,对串进行操作时,只需要从头向尾顺序扫描即可,则对串值不必建立双向链表。设尾指针的目的是为了便于进行连接操作,但应注意连接时需处理第一个串尾的无效字符。

在链式存储方式中,结点大小的选择和顺序存储方式的格式选择一样都很重要,它直接影响到串处理的效率。如果串很长,这要求我们考虑串值的存储密度:

存储密度 = 串值所占的存储位 / 实际分配的存储位

串值的链式存储结构对某些串操作,如连接操作等有一定方便之处,但总的来说不如另外两种存储结构灵活,它占用存储量大且操作复杂。

结构图:

实现代码:

 function Chunk(chunkSize) {
this.chunkSize = chunkSize || 4;
this.ch = [];
for (var i = 0; i < this.chunkSize; i++) {
this.ch[i] = '#';
}
// type: Chunk
this.next = null;
} exports.LString = LString;
function LString(chunkSize) {
// type Chunk
this.head = null;
// type: chunk
this.tail = null;
// 串的当前长度
this.length = 0;
this.chunkSize = chunkSize || 4;
} LString.prototype = {
// 将字符串转换成LString类型
strAssign: function (chars) {
this.head = this.tail = new Chunk(this.chunkSize);
this.length = chars.length; var current = this.head;
for (var i = 0, len = chars.length; i < len; i++) {
current.ch[i % this.chunkSize] = chars[i];
if (i + 1 < len && (i + 1) % this.chunkSize === 0) {
current.next = new Chunk();
current = current.next;
}
} this.tail = current;
},
// 字符串对比
// TODO 是否去掉chunkSize的对比
strCompare: function (tLString) {
var current = this.head;
var curT = tLString.head; if (this.length !== tLString.length) return false; while (current) {
for (var i = 0; i < this.chunkSize; i++) {
if (current.ch[i] !== curT.ch[i]) return false;
} current = current.next;
curT = curT.next;
} return true;
},
clearString: function () {
this.head = this.tail = null;
this.length = 0;
},
concat: function (tLSting) {
if (!tLSting.length) return; var ret = new LString(this.chunkSize); if (this.head === null) {
copyString(ret, tLSting);
} else {
ret.head = ret.tail = new Chunk(this.chunkSize);
copyString(ret, this); var index = ret.tail.ch.indexOf('#');
if (index === -1) {
copyString(ret, tLSting);
} else {
copyString(ret, tLSting, ret.tail, tLSting.head, index);
}
} return ret;
},
substring: function (pos, len) {
pos = ~~pos || 0;
len = ~~len || this.length;
if (pos < 0 || pos > this.length - 1 || len < 0 || len > this.length - pos)
throw new Error('unexpected parameter'); var sub = new LString(this.chunkSize);
var current = findPosChunk(this, pos);
var curS = sub.head = new Chunk(this.chunkSize);
var i = 0;
sub.length = len; outerloop: while (current) {
for (var j = 0, size = this.chunkSize; j < size; j++) {
if (i === len) {
break outerloop;
} else {
curS.ch[j] = current.ch[(i + pos) % this.chunkSize];
i++;
if ((i + pos) % this.chunkSize === 0) {
current = current.next;
}
if (i % this.chunkSize === 0 && (current.ch[i] || current.next)) {
curS.next = new Chunk(this.chunkSize);
curS = curS.next;
}
}
}
} return sub;
},
toString: function () {
var current = this.head; if (current === null) return ''; var str = '';
while (current) {
for (var i = 0, len = this.chunkSize; i < len; i++) {
var ch = current.ch[i];
if (ch === '#') {
return str;
} else {
str += current.ch[i];
}
}
current = current.next;
} return str;
}
}; function findPosChunk(lString, pos) {
var current = lString.head;
while (current) {
for (var i = 0, len = lString.chunkSize; i < len; i++) {
if (pos-- === 0) return current;
}
current = current.next;
}
} function copyString(destination, target, curD, currT, offset) {
offset = offset || 0;
currT = currT || target.head;
curD = curD || destination.head;
var k = 0; while (currT) {
for (var i = 0, len = target.chunkSize; i < len; i++, k++) {
var j = k % curD.chunkSize + offset;
curD.ch[j % curD.chunkSize] = currT.ch[i]; if ((j + 1) % curD.chunkSize === 0 && (currT.ch[i + 1] || currT.next)) {
curD.next = new Chunk(destination.chunkSize);
curD = curD.next;
}
} currT = currT.next;
} destination.tail = curD;
destination.length += target.length;
} var a = new LString();
var b = new LString();
var c = new LString(); a.strAssign('abcdefg');
console.log(a + '');
b.strAssign('hijklmno');
console.log(b + '');
c.strAssign('abcdefg');
console.log(a.strCompare(b));
console.log(a.strCompare(c));
var t = a.concat(b);
console.log(t + '');
t = t.substring(2, 5);
console.log(t + '');

单元测试代码:

 describe('LString tests', function(){
var a = new LString(5);
var b = new LString(4);
var c = new LString(5);
var t; it('should assign string', function(){
a.strAssign('abcdefg');
expect(a + '').toBe('abcdefg'); b.strAssign('hijklmno');
expect(b + '').toBe('hijklmno'); c.strAssign('abcdefg');
expect(c + '').toBe('abcdefg');
}); it('should compare', function(){
expect(a.strCompare(b)).toBe(false);
expect(a.strCompare(c)).toBe(true);
}); it('should concat', function(){
t = a.concat(b);
expect(t + '').toBe('abcdefghijklmno');
}); it('should substring', function(){
t = t.substring(2, 5);
expect(t + '').toBe('cdefg');
});
});

javascript实现数据结构: 串的块链存储表示的更多相关文章

  1. javascript实现数据结构:串--定长顺序存储表示以及kmp算法实现

    串(string)(或字符串)是由零个或多个字符组成的有限序列.串中字符的数目称为串的长度.零个字符的串称为空串(null string),它的长度为零. 串中任意个连续的字符组成的子序列称为该串的子 ...

  2. 数据结构——串(KMP)

    空串:长度为0的串 空格串:由一个或多个空格组成的串 串常用的3种机内表示方法: 定长顺序存储表示: 用一组地址连续的存储单元存储串的字符序列,每一个串变量都有一个固定长度的存储区,可用定长数组来描述 ...

  3. javascript实现数据结构与算法系列

    1.线性表(Linear list) 线性表--简单示例及线性表的顺序表示和实现 线性表--线性链表(链式存储结构) 线性表的静态单链表存储结构 循环链表与双向链表 功能完整的线性链表 线性链表的例子 ...

  4. javascript实现数据结构与算法系列:栈 -- 顺序存储表示和链式表示及示例

    栈(Stack)是限定仅在表尾进行插入或删除操作的线性表.表尾为栈顶(top),表头为栈底(bottom),不含元素的空表为空栈. 栈又称为后进先出(last in first out)的线性表. 堆 ...

  5. javascript实现数据结构:广义表

    原文:javascript实现数据结构:广义表  广义表是线性表的推广.广泛用于人工智能的表处理语言Lisp,把广义表作为基本的数据结构. 广义表一般记作: LS = (a1, a2, ..., an ...

  6. JavaScript 版数据结构与算法(二)队列

    今天,我们要讲的是数据结构与算法中的队列. 队列简介 队列是什么?队列是一种先进先出(FIFO)的数据结构.队列有什么用呢?队列通常用来描述算法或生活中的一些先进先出的场景,比如: 在图的广度优先遍历 ...

  7. javascript中的作用域与作用域链

    前几天,在写一段js代码时,出现了一些问题,调了很长时间也没有调通,其原因是,我在处理变量的作用域时错误地沿用了C++的作用域机制.因此我回炉了一次. 如果你使用过C++或java等一系列的面向对象的 ...

  8. 深入理解JavaScript中的作用域、作用域链和闭包

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qappleh/article/detai ...

  9. PHP之区域块链

    搭建一个最简单的区块链吧.代码简单易懂. <?php //区域块链 //block 区块 // chain 链 //data  //之前区块的has值 //自己的has值 : 他是由存储在区块链 ...

随机推荐

  1. PC站与H5移动站最佳适配方案

    HTML5是目前HTML的最屌版本,同时也是建设移动站的最佳技术.百度适时推出PC站与H5移动站的最佳适配方案,对站长而言实在是久旱逢甘霖.详情如下: PC站与H5移动站最佳适配方案 pc端: 在pc ...

  2. Hadoop Hive概念学习系列之什么是Hive?

    参考  <Hadoop大数据分析与挖掘实战>的在线电子书阅读                   http://yuedu.baidu.com/ebook/d128cf8e33687e21 ...

  3. sqlalchemy orm数据类型验证方法比较

    1.在定义ORM模型时校验 sqlalchemy提供validates函数支持对字段的校验 from sqlalchemy.orm import validates class EmailAddres ...

  4. linux 工具(1)------终端提示符配置

    Linux环境变量,PS1用于设置终端的提示符. 设置规则 设置方法 设置规则 \d :代表日期,格式为 Weekday Month Date,例如 "Mon Aug 1" \H ...

  5. SQL语句练习45题(从第11题开始)

    CREATE TABLE student (sno VARCHAR(3) NOT NULL, sname VARCHAR(4) NOT NULL, ssex VARCHAR(2) NOT NULL, ...

  6. 让android系统中任意一个view变成进度条

    1.效果 2.进度条背景drawable文件 结束后可以恢复原背景. <?xml version="1.0" encoding="utf-8"?> ...

  7. oracle基础知识(六)----spfile与pfile

    一, 认识参数文件      Oracle中的参数文件是一个包含一系列参数以及参数对应值的操作系统文件.它们是在数据库实例启动时候加载的,决定了数据库的物理 结构.内存.数据库的限制及系统大量的默认值 ...

  8. python 爬虫系列01-连接mysql

    爬虫学习中......................................... import pymysql conn = pymysql.connect(host=',database ...

  9. 抽象工厂方法模式(Abstract Factory Pattern)

    Provide an interface for creating families of related or dependent objects without specifying their ...

  10. (转) 来自: http://man.linuxde.net/tee

    tee命令文件过滤分割与合并 tee命令用于将数据重定向到文件,另一方面还可以提供一份重定向数据的副本作为后续命令的stdin.简单的说就是把数据重定向到给定文件和屏幕上. 存在缓存机制,每1024个 ...