From f06543aa8475f76de1910bf3d29914e312b19642 Mon Sep 17 00:00:00 2001 From: javaswing Date: Thu, 16 Nov 2023 18:28:50 +0800 Subject: [PATCH] feat: complete doubly linked list --- code/linked-list/book/LinkedList.ts | 7 +- code/linked-list/book/LinkedNode.ts | 4 +- .../doubly-linked-list/doubly-linked-list.ts | 93 +++++++++++++++++++ .../book/doubly-linked-list/doubly-node.ts | 14 +++ .../test/doubly-linked-list.test.ts | 17 ++++ docs/zh/guide/book/chap6.mdx | 4 + 6 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 code/linked-list/book/doubly-linked-list/doubly-linked-list.ts create mode 100644 code/linked-list/book/doubly-linked-list/doubly-node.ts create mode 100644 code/linked-list/test/doubly-linked-list.test.ts diff --git a/code/linked-list/book/LinkedList.ts b/code/linked-list/book/LinkedList.ts index 217df3e..8cfedde 100644 --- a/code/linked-list/book/LinkedList.ts +++ b/code/linked-list/book/LinkedList.ts @@ -49,11 +49,8 @@ export default class LinkedList implements LinkedListInterface { getElementAt(index: number) { if (index >= 0 && index < this.count) { let current = this.head; - for (let i = 0; i < this.count; i++) { - if (i === index) { - break; - } - current = current?.next; + for (let i = 0; i < index && current != undefined; i++) { + current = current.next; } return current; } diff --git a/code/linked-list/book/LinkedNode.ts b/code/linked-list/book/LinkedNode.ts index f4497c2..caa1954 100644 --- a/code/linked-list/book/LinkedNode.ts +++ b/code/linked-list/book/LinkedNode.ts @@ -4,8 +4,8 @@ export class LinkedNode { val: T; next: undefined | LinkedNode; - constructor(v: T) { + constructor(v: T, next: undefined | LinkedNode = undefined) { this.val = v; - this.next = undefined; + this.next = next; } } diff --git a/code/linked-list/book/doubly-linked-list/doubly-linked-list.ts b/code/linked-list/book/doubly-linked-list/doubly-linked-list.ts new file mode 100644 index 0000000..8b4514c --- /dev/null +++ b/code/linked-list/book/doubly-linked-list/doubly-linked-list.ts @@ -0,0 +1,93 @@ +import { defaultEquals } from '../../../utils'; +import LinkedList from '../LinkedList'; +import DoublyNode from './doubly-node'; + +export default class DoublyLinkedList extends LinkedList { + tail: undefined | DoublyNode; + head: undefined | DoublyNode; + + constructor(eqFn = defaultEquals) { + super(eqFn); + this.tail = undefined; + } + + push(element: T): void { + const node = new DoublyNode(element); + if (this.head === undefined) { + this.head = node; + } else { + let c = this.head; + while (c.next != undefined) { + c = c.next; + } + c.next = node; + node.prev = c; + } + this.count++; + } + + insert(e: T, index: number) { + if (index >= 0 && index <= this.count) { + const node = new DoublyNode(e); + if (index == 0) { + let c = this.head; + node.next = c; + c && (c.prev = node); + this.head = node; + } else if (index === this.count - 1) { + let c = this.tail; + node.next = c; + c && (c.prev = node); + this.tail = node; + } else { + let c = this.head; + for (let i = 0; i < this.count && c !== undefined; i++) { + if (i === index) { + let prev = c.prev; + prev && (prev.next = node); + node.prev = prev; + node.next = c; + c.prev = node; + break; + } + c = c?.next; + } + } + this.count++; + return true; + } + return false; + } + + removeAt(index: number) { + if (index >= 0 && index < this.count) { + let c = this.head; + if (index == 0) { + this.head = c?.next; + if (this.count === 1) { + this.tail = undefined; + } else { + this.head && (this.head.prev = undefined); + } + } else if (index === this.count - 1) { + c = this.tail; + this.tail = c?.prev; + if (this.tail) { + this.tail.next = undefined; + } + } else { + c = this.getElementAt(index) as DoublyNode | undefined; + const p = c?.prev; + if (p) { + p.next = c?.next?.next; + } + if (c && c.next) { + c.next.prev = p; + } + } + this.count--; + return c?.val; + } + return undefined; + } +} diff --git a/code/linked-list/book/doubly-linked-list/doubly-node.ts b/code/linked-list/book/doubly-linked-list/doubly-node.ts new file mode 100644 index 0000000..5d939b9 --- /dev/null +++ b/code/linked-list/book/doubly-linked-list/doubly-node.ts @@ -0,0 +1,14 @@ +import { LinkedNode } from "../LinkedNode"; + +/** + * 双向链表 node 节点 + */ +export default class DoublyNode extends LinkedNode { + prev: DoublyNode | undefined; + next: DoublyNode | undefined; + + constructor(element: T, next: DoublyNode | undefined = undefined, prev: DoublyNode| undefined = undefined) { + super(element, next) + this.prev = prev; + } +} diff --git a/code/linked-list/test/doubly-linked-list.test.ts b/code/linked-list/test/doubly-linked-list.test.ts new file mode 100644 index 0000000..a4699e0 --- /dev/null +++ b/code/linked-list/test/doubly-linked-list.test.ts @@ -0,0 +1,17 @@ +import DoublyLinkedList from "../book/doubly-linked-list/doubly-linked-list"; + +describe('doubly linked list', () => { + it('should create empty doubly linked list', () => { + const linkedList = new DoublyLinkedList(); + expect(linkedList.toString()).toBe(''); + }); + + it('test insert doubly linked list', () => { + const linkedList = new DoublyLinkedList(); + linkedList.push(2); + linkedList.push(5); + linkedList.push(7); + linkedList.insert(1, 1) + expect(linkedList.toString()).toBe("2,1,5,7"); + }); +}); diff --git a/docs/zh/guide/book/chap6.mdx b/docs/zh/guide/book/chap6.mdx index d1aa345..7b5e9db 100644 --- a/docs/zh/guide/book/chap6.mdx +++ b/docs/zh/guide/book/chap6.mdx @@ -214,3 +214,7 @@ export default class LinkedList implements LinkedListInterface { } ``` + +## 双向链表 + +与单向链表相比较而言,双向链表在链表节点内部多了一个指针指向前一个节点。也就是说一个节点有`next`指针和`prev`指针。在遍历上,可以正向遍历也可以反向遍历。