Angry Liver JavaScriptデータ構造-リンクリスト(1)

一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して10日目です。クリックしてイベントの詳細をご覧ください

みなさん、こんにちは。私の名前はヤン・チェンゴンです。

以前の記事では、配列、スタック、キュー、両端キューなどのデータ構造を学習しましたが、今日は新しい友人である動的データ構造-リンクリストに出会います。

リンクリストとは

リンクリストは、順序付けられたコレクションの動的なセットです。ここで「ダイナミック」を理解する方法は?

最初に、以前に学習した配列を思い出してください。これは最も基本的なデータ構造であり、ほとんどの言語で固定サイズです。リンクリストは配列に基づいており、要素を自由に追加および削除できます。これは「動的」配列に相当します。

この時点で、次のように質問することができます。JavaScriptの配列も動的であり、要素を自由に追加および削除できます。これは事実ですが、JavaScriptによって提供されるネイティブメソッドは、使い勝手は良いものの、パフォーマンスが低くなります。

なんで?配列の先頭または中央に要素を挿入し、この位置から開始して、後続のすべての要素を1つ戻す必要があるとします。現時点では、要素を追加するだけでなく、要素を追加するだけでも簡単です。本質的に、以下のすべての要素が変更されます。

配列が非常に大きい場合、位置を早く追加するほど、より多くの要素を「移動」する必要があります。したがって、配列が大きいほど、配列の要素を操作するパフォーマンスが低下する可能性があります。

しかし、リンクリストは異なります。リンクリストは配列と同じ機能を持っていますが、リンクリストの要素はメモリ内に独立して配置され、連続していません。各要素は、要素自体の値と次の要素への参照で構成されます。構造図は次のとおりです。

image.png

この写真を見るとよりはっきりします。任意の位置に要素を追加する必要がある場合は、要素を追加し、現在の要素と前の要素の参照を変更するだけで、他の要素は変更されません。したがって、リンクリストの長さに関係なく、要素を操作するパフォーマンスは非常に高くなります。

しかし、リンクリストは配列よりも優れている必要がありますか?ない。配列では、インデックスによって任意の位置の要素にアクセスできます。リンクリストの各要素は互いに独立しているため、要素を検索する場合は、最初の要素(ヘッダー)から開始して下に検索する必要があります。ターゲットが見つかるまで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,那么总体的思路分三步:

  1. 找到 index 对应的元素 B
  2. 找到 B 的上一个元素 A
  3. 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次にremoveAt2つのメソッドを実装します。

リンクリストの内容は比較的複雑です。この記事で紹介した2つの方法を理解できます。次の記事では、他のメソッドを追加して、最終結果のテストを行います。

この記事の出典は公式アカウントです:ProgrammerSuccessこれは、JavaScriptのデータ構造とアルゴリズムの学習の9番目の部分です。このシリーズは1か月間更新されます。公式アカウントをフォローし、[グループの追加]をクリックして学習チームに参加してください。

おすすめ

転載: juejin.im/post/7085374698971742221