一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して10日目です。クリックしてイベントの詳細をご覧ください。
みなさん、こんにちは。私の名前はヤン・チェンゴンです。
以前の記事では、配列、スタック、キュー、両端キューなどのデータ構造を学習しましたが、今日は新しい友人である動的データ構造-リンクリストに出会います。
リンクリストとは
リンクリストは、順序付けられたコレクションの動的なセットです。ここで「ダイナミック」を理解する方法は?
最初に、以前に学習した配列を思い出してください。これは最も基本的なデータ構造であり、ほとんどの言語で固定サイズです。リンクリストは配列に基づいており、要素を自由に追加および削除できます。これは「動的」配列に相当します。
この時点で、次のように質問することができます。JavaScriptの配列も動的であり、要素を自由に追加および削除できます。これは事実ですが、JavaScriptによって提供されるネイティブメソッドは、使い勝手は良いものの、パフォーマンスが低くなります。
なんで?配列の先頭または中央に要素を挿入し、この位置から開始して、後続のすべての要素を1つ戻す必要があるとします。現時点では、要素を追加するだけでなく、要素を追加するだけでも簡単です。本質的に、以下のすべての要素が変更されます。
配列が非常に大きい場合、位置を早く追加するほど、より多くの要素を「移動」する必要があります。したがって、配列が大きいほど、配列の要素を操作するパフォーマンスが低下する可能性があります。
しかし、リンクリストは異なります。リンクリストは配列と同じ機能を持っていますが、リンクリストの要素はメモリ内に独立して配置され、連続していません。各要素は、要素自体の値と次の要素への参照で構成されます。構造図は次のとおりです。
この写真を見るとよりはっきりします。任意の位置に要素を追加する必要がある場合は、要素を追加し、現在の要素と前の要素の参照を変更するだけで、他の要素は変更されません。したがって、リンクリストの長さに関係なく、要素を操作するパフォーマンスは非常に高くなります。
しかし、リンクリストは配列よりも優れている必要がありますか?ない。配列では、インデックスによって任意の位置の要素にアクセスできます。リンクリストの各要素は互いに独立しているため、要素を検索する場合は、最初の要素(ヘッダー)から開始して下に検索する必要があります。ターゲットが見つかるまで1つずつ。
リンクリストを実装する
上記のリンクリストを紹介し、それをアレイと比較して、2つの違い、長所、短所を簡単に説明しました。これを理解したので、リンクリストの実装を開始できます。
首先看一下基本结构:
class linkedList {
constructor() {
this.count = 0;
this.head = undefined;
}
}
复制代码
上述代码有两个属性:
count
:表示链表的长度,也就是元素的个数head
:存储第一个元素(表头)的引用
前面说过,链表的元素包含自身的值和下一个元素的引用。我们在添加元素时,需要一个快捷的创建链表元素的方式,因此写一个 Node
类表示链表的元素:
class Node {
constructor(value) {
this.value = value;
this.next = undefined;
}
}
复制代码
有了这两个类,接下来就可以编写操作链表元素的方法了。
本篇只介绍常用的两个方法:
push
:向链表尾部添加一个元素removeAt
:从链表某处移除一个元素
push 实现
向链表尾部添加元素时,可能有两种情况:
- 链表为空,则添加第一个元素
- 链表不为空,在所有元素之后添加元素
push(item) {
let node = new Node(item)
let current;
if(!this.head) {
this.head = node
} else {
current = this.head
while(current.next) {
current = current.next
}
current.next = node;
}
this.count++;
}
复制代码
代码中首先判断存储表头引用的变量 head
是否赋值。如果未赋值则表示链表没有元素,此时将 head 赋值为新创建的元素即可。
如果变量 head
已赋值,则表示链表中已经有元素。我们通过 head.next
一层一层向下找,直到找到最后一个元素,即 next 属性为 undefined 的元素。
此时将链表最后一个元素的 next 属性赋值为新元素,就完成了尾部添加。
最后,不要忘了将长度 count
属性自增一。
removeAt 实现
上面我们实现了添加元素的方法,这里再说如何从某处移除元素。
从某处删除元素,类似于删除数组中某个下标的元素。removeAt 方法要提供一个数值类型的参数,相当于数组的索引,表示要删除这个位置的元素。
基础结构如下:
removeAt(index) {
if(index >= 0 && index < this.count) {
// 具体逻辑
}
return undefined;
}
复制代码
在这个方法中,首先要限制参数 index 在 0 和 链表长度之间,然后再分两种情况去删除元素。
如果 index
等于 0,那么操作就比较简单,只需要将 head
属性的指向后移一位即可。
对应到代码是这样的:
if(index == 0) {
let current = this.head;
this.head = current.next;
}
复制代码
如果 index
大于 0,那么总体的思路分三步:
- 找到 index 对应的元素 B
- 找到 B 的上一个元素 A
- を
A.next
指すB.next
3番目の部分に進むと、Bとのリンクを直接バイパスすることと同じです。つまり、要素Bが削除されます。
対応するコードは次のとおりです。
if(index > 0) {
let current = this.head;
let previous;
for(let i = 0; i < index; i++) {
previous = current;
current = current.next;
}
}
复制代码
2つのケースを組み合わせると、完全なコードは次のようになります。
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;
}
}
this.count--;
return current.value
}
return undefined;
}
复制代码
要約する
この記事では、リンクリストの概念とそれが配列とどのように異なるかを紹介し、push
次にremoveAt
2つのメソッドを実装します。
リンクリストの内容は比較的複雑です。この記事で紹介した2つの方法を理解できます。次の記事では、他のメソッドを追加して、最終結果のテストを行います。
この記事の出典は公式アカウントです:ProgrammerSuccess。これは、JavaScriptのデータ構造とアルゴリズムの学習の9番目の部分です。このシリーズは1か月間更新されます。公式アカウントをフォローし、[グループの追加]をクリックして学習チームに参加してください。