链表!比数组更适合做增删操作的数据结构

什么是链表?

  • 链表和数组的对比:在大多数语言中,数组的大小是固定的,从数组的起点或中间添加或删除元素的成本很高,因为需要移动元素。
  • 链表中的每一个元素在内存中不是连续放置的,和它左右两侧元素是没有关系的。
  • 每个元素有一个存储元素本身的节点和指向下一个元素的引用组成。
  • 相对于数组,链表的好处在于添加或删除元素的时候不需要移动其它元素。
  • 在数组中我们可以直接访问任何位置的任何元素,而要想访问链表中的某一个元素,则需要从起点(链表头)开始迭代链表直到找到所需的元素。

举个栗子: 一列火车是由一系列车厢组成的。每节车厢或车皮都相互连接,你很容易分离一节车箱,改变它的位置、添加或移除它。每节车厢相当于链表的元素,车厢间的对接扣就是元素的引用。

创建一个链表类

const defaultCompare = function (a, b) { // 一个比较函数 if (a === b) return 0; return a < b ? -1 : 1; } class Node { // 一个助手类,用来表示我们想要添加到链表中的元素 constructor(element, next) { this.element = element; // 元素的值 this.next = next; // 指向链表中下一个元素的指针 } } class LinkedList { // 链表类 constructor(equalsFn = defaultEquals) { this.equalsFn = equalsFn; // 比较链表中的元素是否相等,默认a===b this.count = 0; // 链表中的元素数量 this.head = undefined; // 表头 } } 

创建几个链表的方法

  1. 向链表的尾部添加元素
push(element) {const node = new Node(element); // 创建node项let current; // 声明一个指向链表中的临时项 if (this.head == undefined) { // 如果链表头为空,即链表为空 this.head = node; // 直接让表头等于当前元素就好了,下一项(next)未传,因此为undefined } else { current = this.head; // 先让current等于第一个元素 while (current.next != null) { // 只要当前元素的下一项元素不是假的,便继续循环 current = current.next; } current.next = node; // 找到最后一个元素后,让它的下一个元素等于传进来的元素 } this.count++;// 最后把总长度自增就好了 } 
  • 首先初始化node类,把element作为值传入。
  • 尾部添加元素分为两种情况,一种是链表为空,一种是链表有值,在后者时,因为链表只有链表头的引用,因此在向链表尾部添加元素时,我们需要循环列表,直到找到最后一个元素,为此 我们需要一个指向链表中current项的变量。
  • 如果链表头没值表示在向链表添加第一个元素,直接让表头等于当前元素就好了,下一项的引用(next)未传,因此为undefined
  • 然后就是第二种情况,首先让current等于链表头,然后循环访问列表,直到找到最后一个元素,然后就是让最后一个元素的下一项的引用指向想要添加到链表的节点。
  • 最后把总长度自增就好了
  1. 从特定位置移除一个元素
removeAt(index) {if (index >= 0 && index < this.count) { // 检查越界值let current = this.head; if (index === 0) { // 如果是表头 this.head = current.next; // 就让表头等于下一个引用 } else { let previous; for (let i = 0; i < index; i++) { // 嗯,开始迭代把~~~ previous = current; current = current.next; } previous.next = current.next; // 上一个的下一个等于现在的下一个,,,(现在内心os:我是谁,我在哪???)当前节点就会被丢弃在计算机内存中,等着被垃圾回收器移除 } this.count--;// 长度自减 return current.element; // 返回移除的元素 } return undefined; } 
  • 由于该方法需要得到移除元素的index(位置),我们需要验证该index是从0到链表的长度之间的。如果不是就返回undefined。
  • 如果移除的是链表中的第一个元素,就要让head指向列表的第二个元素。我们将current变量创建一个对链表中第一个元素的引用。这样current变量就是对链表中第一个元素的引用。这时候如果如果把head赋为current.next,就会移除第一个元素。我们也可以直接把head赋为head.next,不使用current。
  • 如果我们要移除的是链表的最后一个元素或者中间的某个元素。就需要对链表进行迭代,直到到达目标位置。
  • 在到达目标位置后,current变量就会变成我们想要从链表中移除的节点。因此,要从链表中移除当前元素,要做的就是将previous.next和current.next链接起来。这样,当前节点就会被丢弃在计算机内存中,等着被垃圾回收器清除。
  1. 循环迭代链表直到目标位置
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; } 
  • 在remove方法中,我们需要迭代整个链表直到到达我们的目标索引index(位置)。循环到目标index的代码片段在链表方法中会经常用到。因此,我们可以将这部分逻辑独立为单独的办法,这样就可以在不同的地方复用它。
  • 然后我们可以使用刚刚创建的getElementAt方法来重构remove方法
if(index===0){// 第一个位置的逻辑
} else {const previous = this.getElementAt(index - 1); current = previous.next; previous.next = current.next; } this.count--; 
  1. 在任何位置插入元素
insert(element, index) {if (index >= 0 && index <= this.count) { // 边界处理const node = new Node(element); // 实例化当前元素 if (index === 0) { // 如果插在表头 const current = this.head;// 声明一个变量,等于原来的表头 node.next = current;// 传入元素的下一个引用等于current this.head = node; // 当前表头等于传入的元素 } else { const previous = this.getElementAt(index - 1);// 找到传入索引的上一个值 previous.next = node;// 上一个的引用等于传入的值 node.next = previous.next;// 传入值的下一个引用等于上一个的下一个引用 } this.count++;// 总长度自增 return true; // 最后返回true } return false; // 如果位置未找到返回false } 
  • 先惯例的做一下边界处理。
  • 首先如果是插在链表头,我们先声明一个变量等于原来的链表头,再让插入元素的先一个引用等于原来的current变量,最后让当前表头等于传入的元素。
  • 如果是在链表中间或者末尾我们需要用getElementAt方法先找到目标位置的上一个元素,然后让上一个的引用等于传入的值。再把传入值的下一个引用等于上一个的下一个引用。最后一定记得把总长度加一,返回true
  1. 返回一个元素的位置
indexOf(element) {let current = this.head; // 等于表头for (let i = 0; i < this.size() && current != null; i++) { // 循环迭代所有元素 if (this.equalsFn(element, current.element)) { // 找到和当前元素相等的第一个元素 return i;// 返回索引 } current = current.next;// 如果不相等,就继续迭代下一个 } return -1; // 如果都没找到,就返回-1 } 
  • indexOf方法接收一个元素的值,如果在链表中找到了它,就返回元素的位置,否则返回-1。
  • 一如既往,需要一个变量来帮助我们循环访问列表。该变量是current,它的初始值是head
  • 然后迭代元素,从链表头开始,直到链表长度为止。为了确保不会发生运行时错误,我们可以验证一下current变量是否为null或undefined。
  • 循环迭代所有元素直到找到和当前元素相等的第一个元素,返回它的所在位置
  • 如果没找到,就返回-1
  1. 移除传入的元素
remove(element) { const index = this.indexOf(element); // 先找到这个元素的第一个索引return this.removeAt(index); // 利用删除指定位置元素的方法,搞掉它 } 
  • 我们已经有了一个用来移除给定位置元素的方法,也有了indexOf方法。利用indexOf方法找到它的位置,利用删除指定位置元素的方法,搞掉它。
  1. 检查是否为空,长度,获取链表头
isEmpty() {return this.size() === 0;}size() {return this.count; } getHead() { return this.head; } 
  • 还是比较简单的。
  1. 把所有元素转换成字符串
toString() {if (this.head == null) { // 如果列表为空,就返回空字符串return ''; } let objString = `${this.head.element}`; // 创建一个变量,先让他等于表头的元素 let current = this.head.next; // 等于表头的下一个引用 for (let i = 1; i < this.size() && current != null; i++) { // 循环迭代所有元素 objString = `${objString},${current.element}`; // 让这个字符串等于原来的字符串加上当前元素 current = current.next; // 当前元素等于当前的下一个引用 } return objString; // 最后把这个字符串返回 } 
  • 首先,如果链表为空,我们就返回一个空字符串。
  • 如果有值我们就用链表第一个元素的值来初始化方法最后返回的字符串。
  • 然后我们迭代链表中的所有其它元素,将元素值添加到字符串上。
  • 最后把这个字符串返回。

最后

  • 今天的随手笔记就记到这里了,等有时间我会再写一篇关于链表的各种增强版本。总之,在我阅读完这一章后觉得链表相比数组,更适合做增删操作,而数组更适合存储一些比较固定不变的有序集合。

转载于:https://www.cnblogs.com/xingyongwang/p/11125934.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 8中的Base64 –加入乐趣为时不晚

最后&#xff0c;Java 8发布了。 最后&#xff0c;有一种执行Base64编码的标准方法。 长期以来&#xff0c;我们一直依赖于Apache Commons Codec&#xff08;无论如何还是很棒的&#xff09;。 内存敏感的编码人员将拼命使用sun.misc.BASE64Encoder和sun.misc.BASE64Decoder&am…

多层架构模型中的BLL 与 Model的解释

理想状态这两个可以合并到一起来&#xff0c;就是业务对象&#xff0c;但model是把这个业务对象中的数据提取了出来&#xff0c;作为一个数据载体&#xff0c;可以在层间传递。业务对象中除去这些数据剩下的方法就构成了BLL层。显示层Presentation Layer业务逻辑层Business Log…

java创建方法并引用_java – 创建非捕获方法引用,它将调用超类方法

我正在尝试重构以下代码&#xff1a;class Base {private Object a, b, <...>; // theres like 10 of these attributes of different typepublic Object a() {return a;}public Object b() {return b;}// more getters like the ones above}class RootNode extends Base…

maven快速入门

一。前提条件 你的电脑必须安装maven和java 二。检验安装 执行命令&#xff1a;mvn --version 输出&#xff1a; Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T20:41:4702:00) Maven home: D:\apache-maven-3.6.0\bin\.. Java version: 1.8.0_16…

如何使用JSR107缓存注释

最近&#xff0c;我对JSR107缓存注释以及JSR107的实现是否提供它们有一些疑问。 可以将缓存注释添加到Java类中&#xff0c;并将其作为方法调用缓存操作。 例如&#xff0c;下面是带注释的BlogManager。 CacheDefaults(cacheName "blgMngr") public class BlogMan…

js之作用域

1.什么是作用域 作用域是用于收集存储维护变量&#xff0c;以及当前执行代码声明的变量所拥有的权限&#xff0c; 例如 &#xff1a; function foo(a){ console.log(a); -------- 1 }; foo(666); console.log(a); ------- 2 此时执行该段代码 1 -- 处打印我们得…

TMS320F28335——IO控制/定时计操作

一、实现GPIO控制 1.硬件连接 从电路原理图上看来&#xff0c;LED灯是接在GPIO34 上的。 2.IO设置 2.1设置功能 GPXMUX1/2&#xff1a;功能选择寄存器   GPXMUX1/2 每组 IO 一般有 32 个 IO 口可以配置。GPXMUX1 对应每组的低 16 个 IO 口&#xff0c;GPXMUX2 对应高 16 个…

网页弹出窗口代码【来源于网络】

网页弹出窗口代码 1、最基本的弹出窗口代码 其实代码非常简单&#xff1a; < SCRIPT LANGUAGE"java script"> < !-- window.open ("page.html") --> < /SCRIPT> 因为这是一段java script代码&#xff0c;所以它们应该放在< SCRIPT L…

比Python、Java更快的 Go 语言,能否称霸江湖?

关注之后加星标&#xff0c;江湖要事早知道 ​ 文章来源&#xff1a;jb51.net 有一种语言堪称比语言排行榜前五热门选手的Python、Java更快&#xff0c;它就是GO语言。 Go于2009年11月正式宣布推出&#xff0c;成为开放源代码项目&#xff0c;并在Linux及Mac OS X平台上进行了实…

嗨,您好 。 。 ! 您如何评价Java / Java EE技能?

要知道&#xff0c;就是要知道你一无所知。 那就是真正知识的含义。 苏格拉底 这篇文章旨在为读者提供Java生态系统及其技术堆栈的快速概述。 老实说&#xff0c;从Java EE 7&#xff0c;Java SE 8到Java Embedded 8…&#xff0c;Java平台进行了许多革命性的更改和添加。 哇…

simulinkveristandlabview联合仿真——模型导入搭建人机界面

目录 1.软件版本 2.搭建simulink仿真模型 编译错误 3.导入veristand并建立工程 4.veristand导入labview labview显示veristand工程数据 labview设置veristand工程数据 运行labview工程 1.软件版本 matlab2020a&#xff0c;veristand2020 R4&#xff0c;labview2020 SP…

jquery appendTo用法

$("#top_cartWarp").appendTo($("#top_main_right")).css(position,relative).css(top,0px); 返回值:jQueryappendTo(content) V1.0概述 把所有匹配的元素追加到另一个指定的元素元素集合中。 实际上&#xff0c;使用这个方法是颠倒了常规的$(A).append(B)…

Java程序员的典型工作过程有哪些_Java程序员都要经历哪些阶段

原标题&#xff1a;Java程序员都要经历哪些阶段程序员从菜鸟到优秀的过程是需要时间&#xff0c;经验&#xff0c;不断的进阶&#xff0c;小编和大家一起看看每个阶段都要经历哪些成长阵痛&#xff1a;没有防卫这个阶段很快就会过去&#xff0c;因为有无数的书本和”前辈”在提…

关于DataFormWebPart中CreatedModifiedInfo信息的分开使用

以下是问题的描述,我先从数据说起.数据存储在一个叫pages的自定义列表中,该列表除默认的栏外,另增加了一栏"Abstract",为多行文本,用于存储文章摘要信息.当该列表中录入数据后如图: 这时,当我们点列表项如"page2"时,会进入该项的 DispForm.aspx页面.如图:这…

Day47 Django基础部分、路由配置、空间名称

1.最简单的路由配置 URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表&#xff1b;你就是以这种方式告诉Django&#xff0c;对于客户端发来的某个URL调用哪一段逻辑代码对应执行。 1.1 例1&#xff1a; 第一步&#xff1a;在…

6-3 向二分搜索树中添加元素 6-4 改进添加操作:深入理解递归终止条件

二分搜索树添加新元素60, 60>41(根节点)&#xff0c;所以一定要插入到41的右子树。 接着在和58比较 1 public class BST<E extends Comparable<E>> {//对于二分搜索树所存储的内容支持泛型&#xff0c;所以在这里写一个E&#xff0c;此外二分搜索树不是支持所有…

ClassNotFoundException:是否减慢了您的JVM?

大多数Java开发人员都熟悉臭名昭著且非常常见的java.lang.ClassNotFoundException 。 虽然通常已经很好地了解了此问题的根源&#xff08;类路径中缺少类/库&#xff0c;类加载器委派问题等&#xff09;&#xff0c;但对整体JVM和性能的影响通常是未知的。 这种情况可能会对您的…

jQuery 遍历 each()方法

输出每个 li 元素的文本&#xff1a; $("button").click(function(){$("li").each(function(){alert($(this).text())});});亲自试一试 定义和用法 each() 方法规定为每个匹配元素规定运行的函数。 提示&#xff1a;返回 false 可用于及早停止循环。 语法…

java服务器端测试_java-在服务器端测试Spring Web Services端点?

我正在使用Spring WS 2.0.我已经看到了端点和测试用例以测试端点.Endpointpublic class CustomerEndpoint {ResponsePayloadpublic CustomerCountResponse getCustomerCount(RequestPayload CustomerCountRequest request) {CustomerCountResponse response new CustomerCount…

CSS样式表的规划与组织

如果你的工作过程中遇到以下问题&#xff0c;那么请你阅读此文章。 1&#xff1a;样式表文件里面的代码混乱&#xff0c;随着项目的进展&#xff0c;样式表里面的先有代码不敢做任何改变&#xff0c;因为连自己也不知道改了以后会给现有项目造成什么影响。更不敢删除。因为连自…