TypeScript算法专题 - 基于TypeScript语言的单链表实现


李俊才
CSDN:jcLee95
邮箱:291148484@163.com

专题目录:https://blog.csdn.net/qq_28550263/article/details/115718216


1. 链表的概念

链表是一种物理存储单元上非连续、非顺序的存储结构,它由一系列结点组成,其特点在于结点可以在运行时动态生成。

链表的存储结构特点

链表的每个结点包括两个部分:

  • 一个是存储数据元素的数据域;
  • 存储下一个结点地址的指针域。

链表可以用任意一组存储单元来存储其中额数据结构,其存储单元可以是不连续的。

单链表
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起构成单链表。单链表即单向链表,单向指的是其指针域所存储的信息只能为一个方向。具体来说,单链表中的每个存储单元中,除了需要存储每个单元的数据外,还必须附带储存其直接后继存储单元的地址信息。如图所示:

双向链表
它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。

循环链表
循环的最后一个结点指向头结点从而形成一个环。可想而知,从循环链表中的任何一个结点出发都能找到任何其他结点。

循环单链表
尾元结点的后继指针指向表中的首元结点。其特点为:

  • 其每个结点都处于一个循环链中,可以从任何一个位置开始,按照后继方向遍历整个链表。
  • 其在合并分裂时,若非必须从链表头节点开始,可直接在链表指针处进行合并,时间复杂度可达O(1)。

循环双链表
其每个结点包含两个指针指向直接前驱和直接后继,可以在两个方向上遍历某个结点之后以及之前的元素。循环双链表的特点为:

  • 每个几点处于2个链中:1为后继方向的循环链链,2为前驱方向的循环链。
  • 从某个结点出发进到直接前驱或者直接后继节点,时间复杂度均为O(1)。
  • 查找第i个节点、删除第i个节点、在第i个节点处插入,都需要区分时哪一个方向。
  • 修改指针要同时考虑在前驱循环链和后继循环链上的修改。
  • 某个节点的直接前驱的直接后继或其直接后继的直接前驱都是该节点本身。

静态链表
用一维数组来进行描述的一种链表,它利益数组下标号来访问下一个节点,因此需要定义一个足够大的节点数组。

概念归纳

概念名 释义
节点 是链表中的一个存储单元,其包含一块数据存储区域和一块后继信息存储区域
链表 由n个链表单元(节点)连接在一起的存储结构
首元节点 线性链表中第一个元素结点
尾元节点 线性链表中最后一个元素结点
头节点 若链表存在头节点,头节点即链表的第一个节点
头指针 有头单链表的头节点,其指针永远指向链表中头节点的位置
单链表 节点只包含其后继节点信息的链表
双向链表 节点中都有两个指针,分别指向直接后继和直接前驱
循环链表 最后一个结点指向头结点,形成一个环

链表{链表带有【头结点】:{表头指针指示头节点头节点链接指针指示首元结点链表没有【头节点】:表头指针指示首元结点链表 \begin{cases} 链表带有【头结点】:\begin{cases}\pmb{表头指针}指示\pmb{头节点}\\\pmb{头节点链接指针}指示\pmb{首元结点}\end{cases}\\ 链表没有【头节点】:\pmb{表头指针}指示\pmb{首元结点} \end{cases} 链表⎩⎪⎨⎪⎧​链表带有【头结点】:{表头指针表头指针表头指针指示头节点头节点头节点头节点链接指针头节点链接指针头节点链接指针指示首元结点首元结点首元结点​链表没有【头节点】:表头指针表头指针表头指针指示首元结点首元结点首元结点​

单链表进行插入和删除操作实际上不需要移动数据元素,只需要修改相关指针
在TypeScript语言中并没有C和C++中那样指针的概念以用过指针的相关操作带出数据,我们只能来模拟这个过程。

与线性表/顺序表相比
由于链表不必须按顺序存储,它在插入的时候可以达到O(1)的复杂度,比线性表和顺序表(时间复杂度O(n))快得多。但是链表查找一个节点或者访问特定编号的节点却需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

2. 单链表的TypeScript语言实现

2.1 单链表的节点

链表是由一个一个节点通过某种关系建立关联构成的一种数据结构,单链表也是如此。单链表中的所有节点只有一个指向各自直接后继节点的指针域以及各自的数据域:

一个最基本的单链表节点看起来是这个样子的:

class Lnode{/*** 链表节点类* @class Lnode* @constructor* @param next {Lnode} 指向下一节点的指针* @param data {any} 节点的数据*/next: Lnode | null;   // 节点类型(Lnode)的nest也是节点类型Lnode,null是任意类型的子类型也可以不写data: any;constructor(data:any){this.data = data;this.next = null;}
};

2.2 单链表的基本操作:

2.2.1 增

指的是为该单链表增添节点。增加元素的方法有很多,如:

  • pop_left:从链表左端(头)开始增加节点;
  • pop:从链表右端(尾)开始增加;
  • insert:从中间的某个(索引号)位置开始增加;
  • p_insert:在指向链表中的某个指针pointer后面的位置插入新节点;

2.2.2 删

指的是从链表中删除元素。删的方法同样有很多,比如:

  • del(index):按照节点的索引号删除链表中的单个节点;
  • drop(index):按照索引号删除链表中某个节点及其之后所有节点;
  • drop(a,b):给定两个索引号a和b,删除从[a,b]的一段节点;
  • clear:删除链表中的所有节点使之成为一个空链表。

2.2.3 改

对链表是指对链表的节点的内容进行更改。比如:

  • replace:依据节点的索引值将链表中的某一个节点替换成新的节点;
  • reverse:使链表中的所有节点进行反转。

2.2.4 查

的最终目的是为了获取链表中的节点元素。我们知道,单链表中的一个节点包含一个指向节点后继的指针域以及一个存储节点数据的数据域,也就是查在很多情况下是为了获取数据域中的信息。不过也不全是,不如当我们指向查看以下链表是否有节点,这时只要查某个域指针是否指向了一个节点。将要实现的与此有关的功能包括:

  • is_empty:查看头链指针是否为空以判断链表是否为空;
  • top:查看链表头节点的数据内容;

2.3 单链表抽象数据结构

以下是接下来我们在本文以及后续的几篇博客中,基于增删改查将要逐个实现的数据结构。

ADT LinkList{init(id, name)        # 初始化(构造)单链表is_empty()            # 判断单链表是否为空top()                 # 获取链表的头部节点clear()               # 清空链表del(index)            # 从中间删除索引为`index`的一个节点drop(index)           # 删除索引为`i`节点及其之后的所有节点drop(index_1,index_2) # 删除从索引为`index_1`(包含)开始到索引为`index_2`(包含)的一段节点append(node)          # 在链表的最右端嵌入一个新的节点nodeappend_left(node)     # 在链表的最左端嵌入新的节点nodeinsert(index, node)   # 在链表的第`index`个节的位置挂载新的节点`node`p_insert(pointer)     # 在指向链表中的某个指针pointer后面的位置插入新节点pop()                 # 弹出链表尾部节点pop_left()            # 弹出链表头节点并返回toArray()             # 获取链表节点依次构成的数组,可以方便用于链表的遍历和后续利用数组随机取值reverse()             # 反转链表,直接将原链表反转而不返回值getReverse()          # 反转链表,返回反转后的链表,不改变原链表
}

2.4 使用TypeScript语言实现一个单链表:

本章(博客)实现一个具有判空、查头、增加节点、转为节点数组功能的简单的单链表。在本专题的后续博客中,将会逐步实现上面提到的其它功能。
这部分的ADT如下:

ADT LinkList{init(id, name)        # 初始化(构造)单链表is_empty()            # 判断单链表是否为空top()                 # 获取链表的头部节点get(index)            # 获取索引号为`index`的节点append(node)          # 在链表的最右端挂载一个新的节点nodeappend_left(node)     # 在链表的最左端挂载新的节点nodetoArray()             # 获取链表节点依次构成的数组,可以方便链表的遍历

2.4.1 节点类的实现

链表是以节点为基础(元素)构成的节点链。节点可以看作是特殊的链表。

【注意】
TypeScript中没有指针的概念,因此在专题前两篇文章中我们用的是一种比较麻烦的方法去模拟,实际上是建立一个新的结点链来实现功能。
第三篇 《链表实现中的一些问题总结与改进》将讲解在JavaScript和TypeScript中的变量引用,并对我们的链表中的相应内容进行简化。

class Lnode{/*** 链表结点类,结点是构成链表的基础单元。* @class Lnode* @constructor* @param next {Lnode} 指向下一结点的指针* @param data {string | number} 结点的数据。这里只放一些字符串数据或者数字。*/next: Lnode | undefined | null;              // 结点类型(Lnode)的nest也是结点类型Lnode,undefined和null是任意类型的子类型也可以不写data: any;empty: boolean;                              // 描述结点是否为空,这个是配合初始化一个空结点使用的constructor(data?:string | number){if(data!=undefined){                     // 非空结点this.empty = false;                  // 空标识置为假this.data = data;this.next = null;}else{                                   // 空结点,即值构建结点对象但实际上不存在结点this.empty = true;                   // 空标识置为真this.data = null;this.next = null;}}merge(node: Lnode){/*** 将另一个结点(链)合并到本结点(或链)的尾部* @param node {Lnode} 需要插入链表尾部的新结点*/if(this.empty==true){                    // 空结点this.empty =false;this.data = node.data;}else{if(this.next == null){               // 没有链接下一个this.next = node;}else{                               // 有链接下一个this.next.merge(node);}}}
}

2.4.2 链表类的实现

基于2.4.1 节中的节点,实现单链表如下:

class LinkList{/*** 单链表类* @class LinkList* @constructor* @param head {Lnode} 链表的第一个节点,其`next`用于挂载结点* @param length {number} 链表的长度*/head: Lnode | undefined| null;length: number;                       // 链表所容纳结点的个数constructor(datas?:any[]){/**初始化 */if(datas==undefined){          this.head = null;             // 初始化为null算作空链表,不计算长度this.length = 0;}else{for(let i=0; i<datas.length; i++){this.append(new Lnode(datas[i]))}this.length = datas.length;  // 指定一组数据初始化则初始后长度为这组数据元素的个数}}is_empty(){/*** 判断链表是否为空* 只需要判断头结点是否为空,若头结点为空则为空链表,否则不是。* @return {Boolean} true:链表为空; false:链表非空。*/if(this.head == null){return true;}else{return false;}}top():Lnode{/*** 获取链表头结点*/let node = this.head;node.next = null;return node;}clear(){/*** 清空链表,只要把头结点干废,整个链表直接就完蛋(清空)了*/this.head = null;this.length = 0;}append(node: Lnode){/*** 将新结点挂载到链表尾部* @param node {Lnode} 需要插入链表尾部的新结点*/if(this.head == null){this.head = node;this.length = this.length + 1;}else{this.head.merge(node);this.length = this.length + 1;}}append_left(node: Lnode){/*** 将一个新结点插入到链表首部* @param node {Lnode} 要从左侧也就是链头插入的新结点*/if(this.head == null){this.head = node;                 // 若为空链表,直接将链表头设置为该结点this.length = this.length + 1;    // 增加结点长度}else{// 先将新结点的`next`指向原第一个结点node.next = this.head;// 再将`head`指向新的结点this.head = node;this.length = this.length + 1;    // 增加结点长度}}get(index: number):Lnode{/*** 获取索引号为`index`的结点。* 索引号是从数字`0`开始计算的* @param index {number} 索引号* @return node {Lnode} 索引得到地节点*/if(index<0){throw "ValueError: Index must be a positive integer!"}else if(index+1 > this.length){throw "ValueError: Index exceeds the maximum value!"}else{let pointer:Lnode = this.head;       // 从头结点开始range([index]).forEach(() => {pointer = pointer.next;          // 逐个指向下一个结点});pointer.next = null;                 // 斩断后继return pointer;}}get_data(index: number){/*** 索引节点地数据* @param index {number} 索引号* @return data {any} 链表中与索引号对应地节点地数据域内容*/return this.get(index).data}insert(index:number, node:Lnode){/*** 在链表的第`index`个节的位置挂载新的结点`node`,其中从结点为`index = 0`开始编号* 也就是说,新的结点索引号将为`index`,而这个结点将挂载到索引号为`index-1`的结点后面* * @param index {number} 新结点插入的索引号*/if(node!=null && node.next==null){node.next = null;};                    // 只允许插入单个结点,若有后继直接切断if(index==0){node.next = this.head;                        // 先收小弟this.head = node;                             // 再上位}else{let pointer: Lnode = this.head;               // 从头结点开始let results: Lnode = new Lnode();             // 空结点for(let i=0; i<index; i++){results.merge(new Lnode(pointer.data));   // 逐个记录先驱结点pointer = pointer.next;                    // 逐个指向下一个结点}node.next = pointer;                           // 先收小弟results.merge(node);                           // 再上位this.length = this.length + 1;                 // 更新结点长度this.head = results;}}toArray():Lnode[]{/*** 获取链表结点依次构成的数组* @returns elem {Lnode[]} 以此装载了被遍历到的所有结点(这里其中每个结点都是孤立、干掉next的)*/let elm: Lnode[] = [];                              // 准备好用于容纳所有的结点let pointer:Lnode = this.head;                      // 挂载操作一开始时指针指向头部结点、if(pointer==null){                                  // 空链表,不仅要返回一个空数组,还要抛出提示console.warn("Warning: It seems that you hava traversed an empty Linked List!");return elm;}else{                                              // 非空链表while(pointer.next != null){                    // 存在下一个结点时if(pointer == null){                        // 停止(拦截)条件:(直到)结点为`null`break;}else{                                      // 获取当前结点剔除链接关系的`孤立结点`挂载结点数组elm.push(new Lnode(pointer.data));}pointer = pointer.next;                     // 指向后继}elm.push(new Lnode(pointer.data));              // 最后一个元素return elm;}}
}

2.5 使用单链表

本节主要来测试2.4节中实现的单链表。

2.5.1 编译ts相关问题的说明

由于本博文为专题博文中的第一篇博文,我们简单介绍以下与编译相关地事情。由于目前TypeScript还不能直接被多数浏览器所识别,需要先编译成JavaScript代码。
我们简单给出基于TypeScript包的tsc 命令以及使用webpack编译成.js文件的相关配置。如果你还有没这方面的基础,可以在我的另外一个博客专题 《TypeScript 语言基础专题》 中找到详细的教程。

使用tsc 命令

使用编译时的的一些配置文件tsconfig.json代码:

{"compilerOptions": {/* Visit https://aka.ms/tsconfig.json to read more about this file *//* Basic Options */"target": "es5",                                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */"module": "commonjs",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */"outDir": "./es5/",                              /* Redirect output structure to the directory. *//* Strict Type-Checking Options */"strict": false,                                 /* Enable all strict type-checking options. *//* Module Resolution Options */"moduleResolution": "node",                  /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */"allowSyntheticDefaultImports": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */"esModuleInterop": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. *//* Experimental Options */"experimentalDecorators": true,              /* Enables experimental support for ES7 decorators. */// "emitDecoratorMetadata": true,               /* Enables experimental support for emitting type metadata for decorators. *//* Advanced Options */"skipLibCheck": true,                           /* Skip type checking of declaration files. */"forceConsistentCasingInFileNames": true        /* Disallow inconsistently-cased references to the same file. */}
}

这样将生成一个与.ts同名的.js文件在目录./es5下。这里,我的.ts文件命名为singly_linked_list_01.ts,对应的,生成的.js文件就是singly_linked_list_01.js

使用webpack编译

其实我个人更喜欢使用webpack,因为即使使用tsc 编译的代码没有任何问题,但是这样编译的代码由于当前浏览器无法很好地支持impory等一些新的命令,最终将导致程序无法在浏览器中运行。
详细教程也可以在博文专题 《TypeScript 语言基础专题》 中找到,以下是webpack.config.js配置文件

const path = require('path');                           // 对Node.js地标准库`path`进行导包
module.exports = {entry:'./singly_linked_list_01.ts',                   // 打包文件入口output:{filename:'singly_linked_list_01_webpack.js',        // 输出文件名称path:path.resolve(__dirname,'es5')                  // 输出文件夹名为'es5',需要加上前面地路径构成绝对路径},mode: 'development', module:{rules: [{test: /\.tsx?$/,use: 'ts-loader',exclude: /node_modules/}]},resolve: {extensions: ['.ts']},
}

还有就是 node.js 配置文件地script字段更改如下:

  "scripts": {"pack": "webpack"},

这样可以通过npm run pack打包生成’.js’文件 singly_linked_list_01_webpack.js

在html中引入

你需要在html中引入你生成的.js文件它:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>链表</title>
</head>
<body><!-- 这里是直接使用TypeScript编译而成的ts文件 --><!-- <script src="./es5/singly_linked_list_01.js"></script> --><!-- 如果要import语法,目前浏览器还不支持,但是可以通过编写webpack配置文件+ 运行`npm link typescript`(由于先前全局安装了typescript)+ 引入webpack生成的JavaScript文件 替代由typescript生成的typescript文件来使用 --><script src="./es5/singly_linked_list_01_webpack.js"></script>
</body>
</html>

2.5.2 测试:尾部逐个添加节点

// 创建链表
let list: LinkList = new LinkList();// append()添加元素,数据域为字符串数字 "1"、"2"、"3"、"4"、"5"、"6"
for(let i:number =1; i<=6; i++){list.append(new Lnode(i.toString()))
};console.log(list);

2.5.3 测试:将链表转换为节点数组遍历节点

以下代码中用到了写在另一个文件中的range函数,这个实现比较简单,大家可以自己试试,不过import需要webpack打包。如果不会实现的可以参考博文专题 《TypeScript 语言基础专题》TypeScript 重载章节找到源码,也可以采用一般的for循环完成一样的功能。

// range生成序列,与Python的range类似。是一系列重载的range函数。
import {range} from './tools'// 创建链表
let list: LinkList = new LinkList();// append()添加元素
for(let i:number =1; i<=6; i++){list.append(new Lnode(i.toString()))
};// 转换为数组
console.log(list.toArray());// 遍历链表
list.toArray().forEach((val, idx) => {console.log(val)   // val: 当前值console.log(idx)   // idx: 节点再链表中的位置
});

2.5.4 测试:分别在链表的左右各插入几个节点

// 创建链表
let list: LinkList = new LinkList();// append()添加元素
for(let i:number =1; i<=3; i++){list.append(new Lnode(i.toString()))
};// append_left()添加元素
for(let i:number =1; i<=3; i++){list.append_left(new Lnode("向左边插入的第i="+i+"个元素"))
};

2.5.5 测试:在某个链表中,索引为index的位置插入节点

// 创建链表
let list: LinkList = new LinkList();// append()添加元素
for(let i:number =1; i<=6; i++){list.append(new Lnode(i.toString()))
};// 在索引号为`3`的位置插入节点,其数据为字符串"TypeScript"
list.insert(3,new Lnode("TypeScript"));// 转换为数组输出
console.log(list.toArray())

2.5.6 测试:清空链表链表判空

新建一个列表判断是否为空

// 创建链表
let list: LinkList = new LinkList();// 创建链表
let list: LinkList = new LinkList();console.log(list.is_empty())


清空一个链表,清空后判断是否为空

// append()添加元素
for(let i:number =1; i<=6; i++){list.append(new Lnode(i.toString()))
};list.clear()console.log(list.is_empty())

2.5.7 获取链表长度

// 创建链表
let list: LinkList = new LinkList();// append_left()添加元素
for(let i:number =1; i<=3; i++){list.append_left(new Lnode("向左边插入的第i="+i+"个元素"))
};// 输出链表的长度
console.log("链表的长度为: "+list.length);// 输出链表是否为空
console.log(list.is_empty());


附录:


附录1:使用webpack打包后链表代码singly_linked_list_01_webpack.js

/** ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").* This devtool is neither made for production nor for readable output files.* It uses "eval()" calls to create a separate source file in the browser devtools.* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)* or disable the default devtool with "devtool: false".* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).*/
/******/ (() => { // webpackBootstrap
/******/    "use strict";
/******/    var __webpack_modules__ = ({/***/ "./singly_linked_list_01.ts":
/*!**********************************!*\!*** ./singly_linked_list_01.ts ***!\**********************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {eval("\r\n/**\r\n * @author 李俊才 291148484@163.com\r\n * @version 1.0\r\n */\r\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\r\nvar tools_1 = __webpack_require__(/*! ./tools */ \"./tools.ts\");\r\n// 该函数写在另一个文件中,要让浏览器支持则需要 webpack 打包。其用法如下:\r\n// console.log(range([10]));             // [end]\r\n// console.log(range([5,10]));           // [start, end]\r\n// console.log(range([0,10,2]));         // [start, end, step]\r\nvar Lnode = /** @class */ (function () {\r\n    function Lnode(data) {\r\n        if (data != undefined) { // 非空节点\r\n            this.empty = false; // 空标识置为假\r\n            this.data = data;\r\n            this.next = null;\r\n        }\r\n        else { // 空节点,即值构建节点对象但实际上不存在节点\r\n            this.empty = true; // 空标识置为真\r\n            this.data = null;\r\n            this.next = null;\r\n        }\r\n    }\r\n    Lnode.prototype.append = function (node) {\r\n        /**\r\n         * 将新节点挂载到节点链尾部\r\n         * @param node {Lnode} 需要插入链表尾部的新节点\r\n         */\r\n        if (this.empty == true) { // 空节点\r\n            //console.log(\"空节点\")\r\n            this.empty = false;\r\n            this.data = node.data;\r\n        }\r\n        else {\r\n            //console.log(\"非空节点\")\r\n            if (this.next == null) { // 没有链接下一个\r\n                this.next = node;\r\n            }\r\n            else { // 有链接下一个\r\n                this.next.append(node);\r\n            }\r\n        }\r\n    };\r\n    Lnode.prototype.toString = function () {\r\n        return this.data.toString();\r\n    };\r\n    return Lnode;\r\n}());\r\n;\r\nvar LinkList = /** @class */ (function () {\r\n    function LinkList(datas) {\r\n        /**初始化 */\r\n        if (datas == undefined) {\r\n            this.head = null; // 初始化为null算作空链表,不计算长度\r\n            this.length = 0;\r\n        }\r\n        else {\r\n            for (var i = 0; i < datas.length; i++) {\r\n                this.append(new Lnode(datas[i]));\r\n            }\r\n            this.length = datas.length; // 指定一组数据初始化则初始后长度为这组数据元素的个数\r\n        }\r\n        ;\r\n    }\r\n    LinkList.prototype.is_empty = function () {\r\n        /**\r\n         * 判断链表是否为空\r\n         * 只需要判断头节点是否为空,若头节点为空则为空链表,否则不是。\r\n         * @return {Boolean} true:链表为空; false:链表非空。\r\n         */\r\n        if (this.head == null) {\r\n            return true;\r\n        }\r\n        else {\r\n            return false;\r\n        }\r\n    };\r\n    LinkList.prototype.top = function () {\r\n        /**\r\n         * 获取链表头节点\r\n         */\r\n        var node = this.head;\r\n        node.next = null;\r\n        return node;\r\n    };\r\n    LinkList.prototype.clear = function () {\r\n        /**\r\n         * 清空链表,只要把头节点干废,整个链表直接就完蛋(清空)了\r\n         */\r\n        this.head = null;\r\n        this.length = 0;\r\n    };\r\n    LinkList.prototype.append = function (node) {\r\n        /**\r\n         * 将新节点挂载到链表尾部\r\n         * @param node {Lnode} 需要插入链表尾部的新节点\r\n         */\r\n        if (this.head == null) {\r\n            this.head = node;\r\n            this.length = this.length + 1;\r\n        }\r\n        else {\r\n            this.head.append(node);\r\n            this.length = this.length + 1;\r\n        }\r\n    };\r\n    LinkList.prototype.append_left = function (node) {\r\n        /**\r\n         * 将一个新节点插入到链表首部\r\n         */\r\n        if (this.head == null) {\r\n            this.head = node; // 若为空链表,直接将链表头设置为该节点\r\n            this.length = this.length + 1; // 增加节点长度\r\n        }\r\n        else {\r\n            // 先将新节点的`next`指向原第一个节点\r\n            node.next = this.head;\r\n            // 再将`head`指向新的节点\r\n            this.head = node;\r\n            this.length = this.length + 1; // 增加节点长度\r\n        }\r\n    };\r\n    LinkList.prototype.get = function (index) {\r\n        /**\r\n         * 获取索引号为`index`的节点。\r\n         * 索引号是从数字`0`开始计算的\r\n         */\r\n        var pointer = this.head; // 从头节点开始\r\n        tools_1.range([index - 1]).forEach(function () {\r\n            pointer = pointer.next; // 逐个指向下一个节点\r\n        });\r\n        pointer.next = null; // 斩断后继\r\n        return pointer;\r\n    };\r\n    LinkList.prototype.insert = function (index, node) {\r\n        /**\r\n         * 在链表的第`index`个节的位置挂载新的节点`node`,其中从节点为`index = 0`开始编号\r\n         * 也就是说,新的节点索引号将为`index`,而这个节点将挂载到索引号为`index-1`的节点后面\r\n         *\r\n         * @param index {number} 新节点插入的索引号\r\n         */\r\n        if (node != null && node.next == null) {\r\n            node.next = null;\r\n        } // 只允许插入单个节点,若有后继直接切断\r\n        if (index == 0) {\r\n            node.next = this.head; // 先收小弟\r\n            this.head = node; // 再上位\r\n        }\r\n        else {\r\n            var pointer = this.head; // 从头节点开始\r\n            var results = new Lnode(); // 空节点\r\n            for (var i = 0; i < index; i++) {\r\n                results.append(new Lnode(pointer.data)); // 逐个记录先驱节点\r\n                pointer = pointer.next; // 逐个指向下一个节点\r\n            }\r\n            node.next = pointer; // 先收小弟\r\n            results.append(node); // 再上位\r\n            this.length = this.length + 1; // 更新节点长度\r\n            this.head = results;\r\n        }\r\n    };\r\n    LinkList.prototype.toArray = function () {\r\n        /**\r\n         * 获取链表节点依次构成的数组\r\n         * @returns elem {Lnode[]} 以此装载了被遍历到的所有节点(这里其中每个节点都是孤立、干掉next的)\r\n         */\r\n        var elm = []; // 准备好用于容纳所有的节点\r\n        var pointer = this.head; // 挂载操作一开始时指针指向头部节点、\r\n        if (pointer == null) { // 空链表,不仅要返回一个空数组,还要抛出提示\r\n            console.warn(\"Warning: It seems that you hava traversed an empty Linked List!\");\r\n            return elm;\r\n        }\r\n        else { // 非空链表\r\n            while (pointer.next != null) { // 存在下一个节点时\r\n                if (pointer == null) { // 停止(拦截)条件:(直到)节点为`null`\r\n                    break;\r\n                }\r\n                else { // 获取当前节点剔除链接关系的`孤立节点`挂载节点数组\r\n                    elm.push(new Lnode(pointer.data));\r\n                }\r\n                pointer = pointer.next; // 指向后继\r\n            }\r\n            elm.push(new Lnode(pointer.data)); // 最后一个元素\r\n            return elm;\r\n        }\r\n    };\r\n    return LinkList;\r\n}());\r\n\n\n//# sourceURL=webpack://linklist/./singly_linked_list_01.ts?");/***/ }),/***/ "./tools.ts":
/*!******************!*\!*** ./tools.ts ***!\******************/
/***/ ((__unused_webpack_module, exports) => {eval("\r\n/**\r\n * @author 李俊才 291148484@163.com\r\n * @version 1.0\r\n */\r\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\r\nexports.range = void 0;\r\nfunction range(x) {\r\n    var ar = [];\r\n    if (x.length == 1) {\r\n        // 重载:传入数组只有1个元素\r\n        for (var i = 0; i < x[0]; i++) {\r\n            ar.push(i);\r\n        }\r\n    }\r\n    else if (x.length == 2) {\r\n        // 重载:传入2元素数组\r\n        for (var i = x[0]; i < x[1]; i++) {\r\n            ar.push(i);\r\n        }\r\n    }\r\n    else if (x.length == 3) {\r\n        // 重载:传入3元素数组\r\n        for (var i = x[0]; i < x[1]; i += x[2]) {\r\n            ar.push(i);\r\n        }\r\n    }\r\n    return ar;\r\n}\r\nexports.range = range;\r\n;\r\n// console.log(range(\r\n//     [5]\r\n// ));\r\n// console.log(range(\r\n//     [5,10]\r\n// ));\r\n// console.log(range(\r\n//     [0,10,2]\r\n// ));\r\n\n\n//# sourceURL=webpack://linklist/./tools.ts?");/***/ })/******/    });
/************************************************************************/
/******/    // The module cache
/******/    var __webpack_module_cache__ = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {/******/        // Check if module is in cache
/******/        var cachedModule = __webpack_module_cache__[moduleId];
/******/        if (cachedModule !== undefined) {/******/            return cachedModule.exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = __webpack_module_cache__[moduleId] = {/******/            // no module.id needed
/******/            // no module.loaded needed
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/************************************************************************/
/******/
/******/    // startup
/******/    // Load entry module and return exports
/******/    // This entry module can't be inlined because the eval devtool is used.
/******/    var __webpack_exports__ = __webpack_require__("./singly_linked_list_01.ts");
/******/
/******/ })()
;

编译的JavaScript

Sources

/*** @author 李俊才 291148484@163.com* @version 1.0*/
Object.defineProperty(exports, "__esModule", ({ value: true }));
var tools_1 = __webpack_require__(/*! ./tools */ "./tools.ts");
// 该函数写在另一个文件中,要让浏览器支持则需要 webpack 打包。其用法如下:
// console.log(range([10]));             // [end]
// console.log(range([5,10]));           // [start, end]
// console.log(range([0,10,2]));         // [start, end, step]
var Lnode = /** @class */ (function () {function Lnode(data) {if (data != undefined) { // 非空节点this.empty = false; // 空标识置为假this.data = data;this.next = null;}else { // 空节点,即值构建节点对象但实际上不存在节点this.empty = true; // 空标识置为真this.data = null;this.next = null;}}Lnode.prototype.append = function (node) {/*** 将新节点挂载到节点链尾部* @param node {Lnode} 需要插入链表尾部的新节点*/if (this.empty == true) { // 空节点//console.log("空节点")this.empty = false;this.data = node.data;}else {//console.log("非空节点")if (this.next == null) { // 没有链接下一个this.next = node;}else { // 有链接下一个this.next.append(node);}}};Lnode.prototype.toString = function () {return this.data.toString();};return Lnode;
}());
;
var LinkList = /** @class */ (function () {function LinkList(datas) {/**初始化 */if (datas == undefined) {this.head = null; // 初始化为null算作空链表,不计算长度this.length = 0;}else {for (var i = 0; i < datas.length; i++) {this.append(new Lnode(datas[i]));}this.length = datas.length; // 指定一组数据初始化则初始后长度为这组数据元素的个数};}LinkList.prototype.is_empty = function () {/*** 判断链表是否为空* 只需要判断头节点是否为空,若头节点为空则为空链表,否则不是。* @return {Boolean} true:链表为空; false:链表非空。*/if (this.head == null) {return true;}else {return false;}};LinkList.prototype.top = function () {/*** 获取链表头节点*/var node = this.head;node.next = null;return node;};LinkList.prototype.clear = function () {/*** 清空链表,只要把头节点干废,整个链表直接就完蛋(清空)了*/this.head = null;this.length = 0;};LinkList.prototype.append = function (node) {/*** 将新节点挂载到链表尾部* @param node {Lnode} 需要插入链表尾部的新节点*/if (this.head == null) {this.head = node;this.length = this.length + 1;}else {this.head.append(node);this.length = this.length + 1;}};LinkList.prototype.append_left = function (node) {/*** 将一个新节点插入到链表首部*/if (this.head == null) {this.head = node; // 若为空链表,直接将链表头设置为该节点this.length = this.length + 1; // 增加节点长度}else {// 先将新节点的`next`指向原第一个节点node.next = this.head;// 再将`head`指向新的节点this.head = node;this.length = this.length + 1; // 增加节点长度}};LinkList.prototype.get = function (index) {/*** 获取索引号为`index`的节点。* 索引号是从数字`0`开始计算的*/var pointer = this.head; // 从头节点开始tools_1.range([index - 1]).forEach(function () {pointer = pointer.next; // 逐个指向下一个节点});pointer.next = null; // 斩断后继return pointer;};LinkList.prototype.insert = function (index, node) {/*** 在链表的第`index`个节的位置挂载新的节点`node`,其中从节点为`index = 0`开始编号* 也就是说,新的节点索引号将为`index`,而这个节点将挂载到索引号为`index-1`的节点后面** @param index {number} 新节点插入的索引号*/if (node != null && node.next == null) {node.next = null;} // 只允许插入单个节点,若有后继直接切断if (index == 0) {node.next = this.head; // 先收小弟this.head = node; // 再上位}else {var pointer = this.head; // 从头节点开始var results = new Lnode(); // 空节点for (var i = 0; i < index; i++) {results.append(new Lnode(pointer.data)); // 逐个记录先驱节点pointer = pointer.next; // 逐个指向下一个节点}node.next = pointer; // 先收小弟results.append(node); // 再上位this.length = this.length + 1; // 更新节点长度this.head = results;}};LinkList.prototype.toArray = function () {/*** 获取链表节点依次构成的数组* @returns elem {Lnode[]} 以此装载了被遍历到的所有节点(这里其中每个节点都是孤立、干掉next的)*/var elm = []; // 准备好用于容纳所有的节点var pointer = this.head; // 挂载操作一开始时指针指向头部节点、if (pointer == null) { // 空链表,不仅要返回一个空数组,还要抛出提示console.warn("Warning: It seems that you hava traversed an empty Linked List!");return elm;}else { // 非空链表while (pointer.next != null) { // 存在下一个节点时if (pointer == null) { // 停止(拦截)条件:(直到)节点为`null`break;}else { // 获取当前节点剔除链接关系的`孤立节点`挂载节点数组elm.push(new Lnode(pointer.data));}pointer = pointer.next; // 指向后继}elm.push(new Lnode(pointer.data)); // 最后一个元素return elm;}};return LinkList;
}());

TypeScript算法专题 - blog1.基于TypeScript语言的单链表实现相关推荐

  1. TypeScript算法专题 - blog3 - 对TypeScript链表实现中的一些问题总结与改进

    TypeScript算法专题 - [单链表3] 链表的合并 以及对TypeScript链表实现中的一些问题总结与改进 李俊才 CSDN:jcLee95 邮箱:291148484@163.com 专题目 ...

  2. TypeScript算法专题 - [双链表1] - 双链的概念及其实现

    TypeScript算法专题 - [双链表1] 双链的概念及其实现 李俊才 CSDN:jcLee95 邮箱:291148484@163.com 专题目录:https://blog.csdn.net/q ...

  3. TypeScript算法专题 - blog2 - 单链表节点的索引、结点删除与链表反转

    TypeScript算法专题 - [单链表2] 单链表节点的索引.结点删除与链表反转 李俊才 CSDN:jcLee95 邮箱:291148484@163.com 专题目录:https://blog.c ...

  4. Go语言-实现单链表反转算法

    Go语言实现链表的逆序_头插法 头插法与尾插法 头插法 概念 特点 核心过程 Go语言实现 ==注意==:上述方法是带头指针的头插法的实现,如果是带头节点的头插法需要做一定的修改 如果是带头节点的,则 ...

  5. 用c语言实现单链表的初始化,建表,查找,求长度,插入,删除等操作,【YTU+2430+C语言习题+链表建立+插入+删除+输(5)...

    的打印.判断链表是否为空.计算链表长度.插入节点.删除节点.删除整个链表.(2) 线性表adt顺序存储实现中的创建.查找.插入和删除等基本操作及相关算法,线性表adt链式存储实现中单链表.循环链表和双 ...

  6. c语言字符就地逆置,高手看看我的C语言代码单链表实现就地逆置

    高手看看我的C语言代码单链表实现就地逆置 单链表实现就地逆置#include #include struct type{ int date; struct type * next;}first;int ...

  7. Acwing算法基础课学习笔记(四)--数据结构之单链表双链表模拟栈模拟队列单调栈单调队列KMP

    单链表 算法题中最常考的单链表就是邻接表(用来存储图和数),比如最短路问题,最小生成树问题,最大流问题.双链表用于优化某些问题. 利用数组来表达单链表:存储值和指针的两个数组利用下标进行关联. 需要注 ...

  8. C语言实现单链表(带头结点)的基本操作(创建,头插法,尾插法,删除结点,打印链表)

    http://blog.csdn.net/xiaofeige567/article/details/27484137 C语言实现单链表(带头结点)的基本操作(创建,头插法,尾插法,删除结点,打印链表) ...

  9. c语言实现可变单链表,c语言实现单链表

    c语言实现单链表 c语言实现单链表 定义 创建 销毁 清空 打印 获取长度 查询 删除第pos个结点 在第pos插入结点 定义 首先来看一看单链表的定义(看看百度的) 单链表是一种链式存取的数据结构, ...

最新文章

  1. Animation动画:
  2. Apache POI和EasyExcel 第五集:Apache POI的Excel读取不同类型的数据
  3. Spring2.5的新特性
  4. mysql特效_MySQL树 – 前端开发,JQUERY特效,全栈开发,vue开发
  5. 我为什么重新做起了公众号?
  6. Serverless那么火,2019年的采用如何?
  7. 手把手带你入门Python爬虫(四、ORM与peewee)
  8. java中获取路径_java中获取路径的几种基本的方法
  9. 万卷书 - 如何在股市中赚钱 How to Make Money in Stocks
  10. 《Redis系列第五篇、hset与hget的使用|CSDN创作打卡》
  11. C++中2、8、10、16进制数字的表示及计算
  12. linux系统硬盘 转换格式,怎么在LINUX系统里修改硬盘格式呢?
  13. 容器化部署与传统部署的区别及优势
  14. PrinTao CANON Home Studio Edition 8.0r12 17 Mac 佳能专业打印软件
  15. Android 源代码在线查看 目录结构 不完全指南
  16. 中国银联正式推出在线支付和手机支付业务zt
  17. DNS发生错误导致网页打不开
  18. gBuilder内测活动圆满收官
  19. 什么是计算机网络及主要功能有哪些?
  20. jupyter中配置多种虚拟环境

热门文章

  1. 关于Savitzky-Golay滤波器
  2. 【React】设计高质量的React组件
  3. ubuntu 上 nvidia-smi 没显示所有的GPU
  4. keras TimeDistributed 描述
  5. Servlet+jsp用户登录加上验证码
  6. hystrix文档翻译之Dashboard
  7. Key为数字的Json数据标准化成标准Json格式
  8. iOS开发之注册推送通知权限
  9. Android中常见布局
  10. JS 操作 HTML 和 AJAX 请求后台数据