一緒に書く習慣を身につけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して12日目です。クリックしてイベントの詳細をご覧ください。
みなさん、こんにちは。私の名前はヤン・チェンゴンです。
最初の2つの記事では、リンクリストを詳しく紹介しました。リンクリストは、要素が互いに独立しているが互いに接続されている順序付けられたコレクションであることがわかっています。要素をクエリするときは、ヘッダーから開始して、レベルごとに逆方向に検索する必要があります。
二重リンクリストは、実際にはリンクリストに基づいており、「後ろから前」のクエリ関数が追加されています。リンクリストはヘッダーからのみチェックでき、逆方向にチェックされているためです。二重にリンクされたリストを使用すると、最後の要素から検索して、前方に検索を続けることができます。
したがって、二重リンクリストの要素には、前の要素への参照と次の要素への参照の2つの参照があります。
二重リンクリストを実装する
二重リンクリストはリンクリストの一種であり、基本的な機能はリンクリストによって提供されます。したがって、二重リンクリストを実装するには、元のリンクリストメソッドを直接拡張できます。
ES6のクラス継承の方法を使用して、二重にリンクされたリストを実装します。
class doubLinkedList extend linkedList {
constructor(whereFn) {
super(whereFn)
this.tail = undefined
}
}
复制代码
上記の基本的なコードは、最後に要素参照を表すlinkedListにtail
属性。このsuper
メソッドlinkedListのコンストラクターを呼び出して、完全な継承を実現することです。
コンストラクターはこの方法で変更できます。リンクリスト要素では、前の要素を指すprev
属性ため、同じ継承メソッドを使用して以下を実装します。
class doubNode extend Node {
constructor(value) {
super(value)
this.prev = undefined
}
}
复制代码
二重リンクリストから要素を追加および削除する方法を見てみましょう。
挿入方法のアップグレード
リンクリストのinsertメソッドは、新しい要素への次の参照を追加するだけで済みますが、二重リンクリストは、を挿入するときにprev
参照。リンクリストの挿入方法に基づいてアップグレードを行います。
insert(item, index) {
if(index >= 0 && index <= this.count) {
let node = new doubNode(item)
let current = this.head;
if(index == 0) {
// 1. 首位添加逻辑
if(!this.head) {
this.head = node
this.tail = node
} else {
node.next = current
current.prev = node
this.head = node
}
} else if(index === this.count) {
// 2. 末尾添加逻辑
current = this.tail
current.next = node
node.prev = current
this.tail = node
} else {
// 3. 中间位置添加逻辑
let previous = this.getItemAt(index - 1)
current = previous.next
previous.next = node
node.prev = previous
node.next = current
current.prev = node
}
this.count++;
return true;
}
return false;
}
复制代码
説明のために、コード内のcurrent
変数はインデックス位置の要素を表します。
コード内のコメント部分は変換される部分で、全部で3つの部分があります。それらについて1つずつ話しましょう。
最初に追加
首位添加就是添加第一个元素,这个时候要分情况。如果是空链表,那么将 head
和 tail
属性赋值为新元素即可。因为新元素既是表头也是表尾。
如果链表不为空,则说明表头表尾已存在,我们要新元素的 next
赋值为表头,再将表头的 prev
赋值为新元素,最后再将新元素设置为新的表头即可。
末尾添加
末尾添加主要改变的是 tail
属性。首先要将表尾的 next 赋值为新元素,然后将新元素的 prev 再指向表尾,最后将新元素赋值为新的表尾。
中间位置添加
中间位置添加是指,插入的位置不是第一个,也不是最后一个。这种情况下意味着表头和表尾都不需要动,只要将新元素与前后元素关联即可。
首先,获取索引位置的前一个元素 previous
;然后再拿到索引位置的元素 current
,也就是 previous.next。接下来将新元素放到它们两之间。
方法就是逐个设置 prev 和 next 属性。
升级 removeAt 方法
removeAt 方法与上面的 insert 方法改造原则一致,功能不变,只需要将删除对象前后的元素对应的 prev 和 next 属性修改,并且涉及到表尾时修改 tail
属性即可。
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head;
if (index === 0) {
// 1. 首位删除逻辑
if (!current) {
return undefined;
}
this.head = current.next;
if (this.count === 1) {
this.tail = undefined;
} else {
this.head.prev = undefined;
}
this.head = current.next;
} else if (index === this.count) {
// 2. 末尾删除逻辑
current = this.tail;
this.tail = current.prev;
this.tail.next = undefined;
} else {
// 3. 中间位置删除逻辑
current = this.getItemAt(index);
let previous = current.prev;
previous.next = current.next;
current.next.prev = previous;
}
this.count--;
return current.value;
}
return undefined;
}
复制代码
代码中注释的部分就是要改造的部分,具体如下。
首位删除
首位删除就是删除第一个元素,这个时候要分情况。
如果是空链表,删除没有意义,直接返回 undefined;如果只有一个元素,删除后会变成空链表,所以要把 head
和 tail
属性全都置为 undefined。如果链表长度大于 1,则只需要将表头向后挪动一位,并且将远离啊的 prev 置为 undefined 即可。
末尾删除
这个比较简单,主要改变的是 tail
属性。
フッターを現在の要素として設定してから、フッターをcurrent
1つ前に移動し、新しいフッターのprevをundefinedに設定します。
中間位置削除
中央の位置を削除する場合、ヘッダーとフッターの状況を考慮する必要はありません。クラスメソッドを介してインデックス位置の要素をgetItemAt
直接current.prev
取得し、次に。を介して前の要素を取得します。
このとき、前の要素の次の属性と次の要素の前の属性を変更し、現在のインデックス位置の要素をバイパスします。
二重リンクリストを使用する
上記の2つの方法を試してみましょう。
var doublinked = new doubLinkedList()
doublinked.insert('北京', 0)
doublinked.insert('上海', 1)
doublinked.insert('成都', 2)
console.log(doublinked.toString()) // 北京,上海,成都
复制代码
このように見てから、真ん中の要素を取得してもかまいません。
let node = doublinked.getItemAt(1)
console.log(node.prev.value) // 北京
console.log(node.next.value) // 成都
复制代码
テスト結果は正しく、これで完了です。
要約する
この記事では、二重リンクリストの概念を紹介し、クラス継承を使用して、リンクリストクラスに基づいて二重リンクinsert
リストのとremoveAt
2つのメソッドを実装します。
2つのメソッドのみが記述されていますが、他のメソッドは、リンクリスト操作とリンクリスト要素操作の間に違いtail
がないことを除いて、これら2つのメソッドと同じ方法で変更されます。prev
この記事の出典は公式アカウントです:ProgrammerSuccess。これは、JavaScriptのデータ構造とアルゴリズムの学習の第11部です。このシリーズは、1か月間更新されます。公式アカウントをフォローし、[グループの追加]をクリックして学習チームに参加してください。