重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)
定场诗
伤情最是晚凉天,憔悴厮人不堪言;
邀酒摧肠三杯醉.寻香惊梦五更寒。
钗头凤斜卿有泪,荼蘼花了我无缘;
小楼寂寞新雨月.也难如钩也难圆。
前言
本章为重读《学习JavaScript数据结构与算法》的系列文章,该章节主要讲述数据结构-链表,以及实现链表的过程和原理。
链表
链表,为什么要有这种数据结构呢?当然,事出必有因!
数组-最常用、最方便的数据结构,But,当我们从数组的起点或中间插入或移动项的成本很高,因为我们需要移动数组元素。
链表,是存储有序的元素集合。链表中的元素在内存中并不是连续放置的,每个元素由一个存储自身的元素节点和一个指向下一个元素的引用(指针或链接)组成。
这个是重点,注意,圈起来,面试肯定考!
实现链表
我们要实现链表的结构以及对应的方法,大致为:元素插入、移除、获取链表长度、查询元素、是否为空等等...更详细可看代码实现逻辑。
数据结构越来越复杂喽...注意领会精神...
/**
* Node 节点类,用于生成自身节点与下一个元素的引用
*/
class Node {
constructor(element) {
this.element = element
this.next = undefined
}
}
/**
* defaultEquals() 用于比较两个非对象类的值是否相等
*/
function defaultEquals (a, b) {
return a === b
}
/**
* LinkedList 链表
* 特点:链表存储有序的元素集合,但元素在内存中并不连续存放。每个元素有一个存储元素本身的节点和一个指向下一个元素的引用
*/
class LinkedList {
constructor (equalsFn = defaultEquals) {
// 链表长度
this.count = 0
// 第一个元素引用
this.head = undefined
// 用于比较元素是否相等,默认为defaultEquals,允许传入自定义的函数来比较两个对象是否相等。
this.equalsFn = equalsFn
}
}
注意:equalsFn参数,在比较对象类元素时,需要传递自定义方法来比较两个对象的的值是否相等。
push()
向链表的尾部添加元素
/**
* push() 添加元素到链表
* @param {*} element 待添加的元素
*/
push (element) {
let node = new Node(element)
let current
// 判断链表是否为空
if (this.head == null) {
this.head = node
} else {
// 查询最后的元素 - 特点 current.next == null
current = this.head
while (current.next != null) {
current = current.next
}
// 将最后元素的下一个元素引用指向node
current.next = node
}
this.count++
}
getElementAt()
获取指定索引位置的元素
/**
* getElementAt() 返回索引位置元素
* @param {Number} index 索引位置
* @returns {*} node
*/
getElementAt (index) {
// 判断索引是否有效
if (index < 0 || index > this.count) {
return undefined
}
let node = this.head
for (let i = 0; i < index && node != null; i++) {
node = node.next
}
return node
}
insert()
在任意索引位置插入元素
/**
* insert() 在任意位置插入元素
* @param {*} element 待插入的元素
* @param {Number} index 指定的索引位置
* @returns {Boolean}
*/
insert (element, index) {
// 判断是否为合法index
if (index < 0 || index > this.count) {
return false
}
// 实例化节点
let node = new Node(element)
// 插入-第一个位置
if (index === 0) {
let current = this.head
node.next = current
this.head = node
} else {
// 查找到指定索引位置的前一个元素
let previous = this.getElementAt(index - 1)
let current = previous.next
// 将上一个元素的next引用指向当前新添加的node,将node的next引用指向current,则完成插入
previous.next = node
node.next = current
}
// 链表长度+1
this.count++
return true
}
removeAt()
移除指定索引位置的元素
实现逻辑:判断是否为链表第一项,若为第一项,则将this.head指向第二个元素即可;如果不是第一项,则获取索引index位置的上一位置索引元素previous,以及下一位置索引元素current.next,将previous.next指向current.next即可
/**
* removeAt() 移除指定位置的元素
* @param {Number} index
* @returns {*} element 移除的元素
*/
removeAt (index) {
// 判断是否是合法索引
if (index < 0 || index > this.count) {
return undefined
}
let current = this.head
// 考虑是否是链表第一项
if (index === 0) {
this.head = current.next
} else {
// 方法一、 使用for循环迭代获取指定索引位置的元素
// 用于获取上一位置元素
// let previous
// for (let i = 0; i < index; i++) {
// previous = current
// current = current.next
// }
// // 跳过当前元素,将上一个元素的next指向下一个元素
// previous.next = current.next
// 方法二、借助getElementAt()方法
// 查询该索引位置的上一个元素
let previous = this.getElementAt(index - 1)
// 跳过中间元素,将上一个元素的next指向下一个元素
current = previous.next
previous.next = current.next
}
this.count--
return current.element
}
indexOf()
查询元素的索引位置
实现逻辑:迭代元素,比较每一个元素与目标元素是否相等;特别需要注意的是,对象类的值请传入自定义的方法进行比较是否相等s
/**
* indexOf() 查询元素的索引位置
* @param {*} element 待查询的元素
* @returns {Number} index 索引位置,不存在则返回-1
*/
indexOf (element) {
let current = this.head
for (let i = 0; i < this.count && current != null; i++) {
// 判断两个元素是否相同
if (this.equalsFn(element, current.element)) {
return i
}
current = current.next
}
return - 1
}
remove()
移除指定的元素
/**
* remove() 移除元素
* @param {*} element 待移除的元素
* @returns {*} element 移除的元素
*/
remove (element) {
// 获取索引位置
let index = this.indexOf(element)
// 移除指定索引位置的元素
return this.removeAt(index)
}
size()
获取链表长度
/**
* size() 返回链表长度
* @returns {Number} 链表长度
*/
size () {
return this.count
}
isEmpty()
判断链表是否为空
/**
* isEmpty() 判断链表是否为空
* @returns {Boolean}
*/
isEmpty () {
return this.count === 0
}
getHead()
获取链表首位元素
/**
* getHead() 获取链表首位元素
* @returns {*}
*/
getHead () {
if (this.head != null) {
return this.head.element
}
return undefined
}
clear()
清空链表
/**
* clear() 清空链表
*/
clear () {
this.count = 0
this.head = undefined
}
toString()
链表字符串处理
/**
* toString() 链表字符串化展示
* @returns {String}
*/
toString () {
// 判断是否为空
if (this.isEmpty()) {
return ''
}
let objStr = `${this.head.element}`
let current = this.head.next
for (let i = 1; i < this.count; i++) {
objStr = `${objStr},${current.element}`
current = current.next
}
return objStr
}
使用链表
let linkedList = new LinkedList()
console.log(linkedList.isEmpty()) // true
// push 尾部压入元素
linkedList.push(15)
linkedList.push(20)
/*
LinkedList {
count: 2,
head: Node {
element: 15,
next: Node {
element: 20,
next: undefined
}
},
equalsFn: [Function: defaultEquals]
}
*/
console.log(linkedList)
console.log(linkedList.isEmpty()) // false
// getElementAt() 指定索引位置的元素
let node = linkedList.getElementAt(1)
console.log(node)
// insert() 任意索引位置插入元素
linkedList.insert(22, 1)
linkedList.insert(33, 1)
console.log(linkedList.toString()) // 15,33,22,20
// removeAt() 移除指定索引位置的元素
console.log(linkedList.removeAt(1)) // 33
// remove() 移除元素
console.log(linkedList.remove(15)) // 15
console.log(linkedList.toString()) // 22,20
console.log(linkedList.getHead()) // 22
console.log(linkedList.size()) // 2
console.log(linkedList.toString()) // 22,20
console.log(linkedList.isEmpty()) // false
linkedList.clear()
console.log(linkedList.isEmpty()) // true
再次强调下:如果链表中存储的是对象类型值,请务必传入指定的比较函数equalsFn自行进行比较,You should know why!
后记
以上就是胡哥今天给大家分享的内容,喜欢的小伙伴记得收藏、转发、点击右下角按钮在看,推荐给更多小伙伴呦,欢迎多多留言交流...
胡哥有话说,一个有技术,有情怀的胡哥!京东开放平台首席前端攻城狮。与你一起聊聊大前端,分享前端系统架构,框架实现原理,最新最高效的技术实践!
长按扫码关注,更帅更漂亮呦!关注胡哥有话说公众号,可与胡哥继续深入交流呦!

重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)的更多相关文章
- 重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈
定场诗 金山竹影几千秋,云索高飞水自流: 万里长江飘玉带,一轮银月滚金球. 远自湖北三千里,近到江南十六州: 美景一时观不透,天缘有分画中游. 前言 本章是重读<学习JavaScript数据结构 ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列
定场诗 马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁! 前言 本章为重读<学习JavaScript数据结构与算法-第三版>的系列文章,主要讲述队列数据 ...
- 重读《学习JavaScript数据结构与算法-第三版》-第2章 ECMAScript与TypeScript概述
定场诗 八月中秋白露,路上行人凄凉: 小桥流水桂花香,日夜千思万想. 心中不得宁静,清早览罢文章, 十年寒苦在书房,方显才高志广. 前言 洛伊安妮·格罗纳女士所著的<学习JavaScript数据 ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第3章 数组(一)
定场诗 大将生来胆气豪,腰横秋水雁翎刀. 风吹鼍鼓山河动,电闪旌旗日月高. 天上麒麟原有种,穴中蝼蚁岂能逃. 太平待诏归来日,朕与先生解战袍. 此处应该有掌声... 前言 读<学习JavaScr ...
- 学习JavaScript数据结构与算法 (二)
学习JavaScript数据结构与算法 的笔记 包含第四章队列, 第五章链表 本人所有文章首发在博客园: http://www.cnblogs.com/zhangrunhao/ 04队列 实现基本队列 ...
- 学习JavaScript数据结构与算法 (一)
学习JavaScript数据结构与算法 的笔记, 包含一二三章 01基础 循环 斐波那契数列 var fibonaci = [1,1] for (var i = 2; i< 20;i++) { ...
- 学习JavaScript数据结构与算法---前端进阶系列
学习建议 1.视频学习---认知 建议:在中国慕课上找"数据结构"相关的视频教程.中国大学MOOC 推荐清华大学.北京大学.浙江大学的教程,可先试看,然后根据自身的情况选择视频进行 ...
- 学习Javascript数据结构与算法(第2版)笔记(1)
第 1 章 JavaScript简介 使用 Node.js 搭建 Web 服务器 npm install http-server -g http-server JavaScript 的类型有数字.字符 ...
- 学习JavaScript数据结构与算法 2/15
第一章 JavaScript简介 js不同于C/C++,C#,JAVA,不是强类型语言. 通常,代码质量可以用全局变量和函数的数量来考量(数量越多越糟).因此,尽可能避免使用全局变量. JS数据类型 ...
随机推荐
- 数据结构-哈夫曼树(python实现)
好,前面我们介绍了一般二叉树.完全二叉树.满二叉树,这篇文章呢,我们要介绍的是哈夫曼树. 哈夫曼树也叫最优二叉树,与哈夫曼树相关的概念还有哈夫曼编码,这两者其实是相同的.哈夫曼编码是哈夫曼在1952年 ...
- python常见模块-collections-time-datetime-random-os-sys-序列化反序列化模块(json-pickle)-subprocess-03
collections模块-数据类型扩展模块 ''' 在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque ...
- 三千字讲清TypeScript与React的实战技巧
很多时候虽然我们了解了TypeScript相关的基础知识,但是这不足以保证我们在实际项目中可以灵活运用,比如现在绝大部分前端开发者的项目都是依赖于框架的,因此我们需要来讲一下React与TypeScr ...
- java练习---1
//程序员:罗元昊 2017.9.6public class Ap{ public static void main(String[] args){ System.out.println(" ...
- Java类什么情况下被初始化?
1.创建类的实例(new 的方式).访问某个类或接口的静态变量,或者对该静态变量赋值,调用类的静态方法 2.反射的方式 3.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需先触发其父类的初始 ...
- [ PyQt入门教程 ] Qt Designer工具的布局管理
这节课很重要..界面整洁美观与否就看布局了..这里讲布局方法,至于设计的天赋与最终界面的美感那就看造化了.. 本文主要讲述Qt Designer工具实现界面控件布局管理,就是排列组合控件.包括水平布局 ...
- JDK容器类Map源码解读
java.util.Map接口是JDK1.2开始提供的一个基于键值对的散列表接口,其设计的初衷是为了替换JDK1.0中的java.util.Dictionary抽象类.Dictionary是JDK最初 ...
- kafka集群跨双网段及多网段通信问题解决
一.问题场景: 实际生产环境总存在很多kafka集群跨网段的问题.kafka集群可能存在多个网卡,对应多个网段.不同网段之间需要同时与集群通信,即跨网段生产消费问题. 二.解决方法:自定义listen ...
- 初次见面C#排坑记录
排一次开发C#遇到的坑,同时说一下自己对C#中文件夹构建方式的体会. 一个开发Java的人留下了痛苦的泪水,变量命名规则不一样,连括号打的都不一样,
- poj 1068 模拟
题目链接 大概题意就是告诉你有个n个小括号,每一个")"左边有多少个"("都告诉你了,然后让你求出每一对括号之间有多少对括号(包含自己本身). 思路: 我先计算 ...