使用TS实现各种链表

297 阅读2分钟

使用TypeScript实现链表

  • 使用泛型实现了数据结构的统一性
  • 通过修饰符控制类中属性的访问权限
  • 封装LinkedList类实现链表中的各种方法
  • push()向链表末尾添加元素
  • getNodeAt()获取索引对应的节点
  • removeAt()移除对应索引的节点
  • indexOf()获取元素对应的第一个索引(表中有重复元素时只能获取第一个)
  • remove()移除对应元素的节点(表中有重复元素时只能移除第一个)
  • insert()在输入的索引位置插入节点
  • getHead()获取头节点
  • size()获取表长
  • isEmpty()判断表是否为空
  • clear()清空链表

单链表

export interface nodeType<T> {
  element?: T
  next: nodeType<T> | null
}

export class Node<T> implements nodeType<T> {
  element: T

  next: nodeType<T> | null = null

  constructor( element: T ) {
    this.element = element
  }
}

export interface linkListType<T> {
  push( element: T ): void

  getNodeAt( index: number ): nodeType<T> | undefined

  removeAt( index: number ): T | undefined

  // removeAt2( index: number ): T | undefined

  indexOf( element: T ): number | undefined

  remove( element: T ): boolean

  insert( element: T, index: number ): boolean

  getHead(): nodeType<T> | null

  size(): number

  isEmpty(): boolean

  clear(): void
}

export class LinkedList<T> implements linkListType<T> {

  protected head: nodeType<T> | null = null

  protected count: number = 0

  push( element: T ): void {
    const node = new Node<T>( element )
    if ( !this.head ) {
      this.head = node
    } else {
      let current = this.head
      while ( current.next ) {
        current = current.next
      }
      current.next = node
    }
    this.count++
  }

  getNodeAt( index: number ): nodeType<T> | undefined {
    let node = this.head || undefined
    if ( index >= 0 && index < this.count ) {
      for ( let i = 0; i < index; i++ ) {
        if ( node?.next )
          node = node.next
      }
      return node
    }
  }
  
  //通过遍历寻找,效率低
  // removeAt( index: number ): T | undefined {
  //   let res: T | undefined = undefined
  //   if ( index >= 0 && index <= this.count ) {
  //     if ( index === 0 ) {
  //       let current = this.head
  //       if ( current ) {
  //         this.head = current.next
  //         res = current.element
  //         this.count--
  //       }
  //     } else {
  //       let previous: nodeType<T> | null = null
  //       let current = this.head
  //       for ( let i = 0; i < index; i++ ) {
  //         if ( current ) {
  //           previous = current
  //           current = current.next
  //         }
  //       }
  //       if ( previous && current ) {
  //         previous.next = current.next
  //         res = current.element
  //         this.count--
  //       }
  //     }
  //   }
  //   return res
  // }

  removeAt( index: number ): T | undefined {
    let res: T | undefined = undefined
    if ( index >= 0 && index < this.count ) {
      if ( index === 0 ) {
        let current = this.head
        if ( current ) {
          this.head = current.next
          res = current.element
          this.count--
        }
      } else {
        let previous = this.getNodeAt( index - 1 )
        let current = this.getNodeAt( index )
        if ( previous && current ) {
          previous.next = current.next
          res = current.element
          this.count--
        }
      }
    }
    return res
  }

  indexOf( element: T ): number | undefined {
    let index: number | undefined = undefined
    for ( let i = 0; i < this.count; i++ ) {
      if ( this.getNodeAt( i )?.element === element ) index = i
    }
    return index
  }

  remove( element: T ): boolean {
    let bool = false
    const index = this.indexOf( element )
    if ( index !== undefined && this.removeAt( index ) ) bool = true
    return bool
  }

  insert( element: T, index: number ): boolean {
    const node = new Node<T>( element )
    let bool = false
    if ( index >= 0 && index < this.count ) {
      let current = this.head
      if ( index === 0 && current ) {
        this.head = node
        node.next = current
        bool = true
        this.count++
      } else {
        let previous = this.getNodeAt( index - 1 )
        let current = this.getNodeAt( index )
        if ( previous && current ) {
          previous.next = node
          node.next = current
          bool = true
          this.count++
        }
      }
    }
    return bool
  }

  getHead(): nodeType<T> | null {
    return this.head
  }

  size(): number {
    return this.count
  }

  isEmpty(): boolean {
    return this.count === 0
  }

  clear(): void {
    this.head = null
    this.count = 0
  }
}

双向链表

  • 通过继承LinkedList类实现双向链表
import { LinkedList, linkListType, Node, nodeType } from './linkedList'

interface doublyNodeType<T> extends nodeType<T> {
  prev: doublyNodeType<T> | null
  next: doublyNodeType<T> | null
}

class DoublyNode<T> extends Node<T> implements doublyNodeType<T> {
  prev: doublyNodeType<T> | null = null

  next: doublyNodeType<T> | null = null

  constructor( element: T ) {
    super( element )
  }
}

interface doublyLinkListType<T> extends linkListType<T> {
  getNodeAt( index: number ): doublyNodeType<T> | undefined

  getHead(): doublyNodeType<T> | null

  getTail(): doublyNodeType<T> | null
}

class DoublyLinkedList<T> extends LinkedList<T> implements doublyLinkListType<T> {

  protected tail: doublyNodeType<T> | null = null
  protected head: doublyNodeType<T> | null = null

  push( element: T ): void {
    const node = new DoublyNode<T>( element )
    if ( !this.head && !this.tail ) {
      this.head = node
      this.tail = node
      this.count++
    } else {
      if ( this.tail ) {
        this.tail.next = node
        node.prev = this.tail
        this.tail = node
        this.count++
      }
    }
  }

  getNodeAt( index: number ): doublyNodeType<T> | undefined {
    let node = this.head || undefined
    if ( index >= 0 && index < this.count ) {
      for ( let i = 0; i < index; i++ ) {
        if ( node?.next )
          node = node.next
      }
      return node
    }
  }

  removeAt( index: number ): T | undefined {
    let res: T | undefined = undefined
    const current = this.getNodeAt( index )
    if ( index >= 0 && index < this.count && current ) {
      if ( index === 0 && current.next ) {
        current.next.prev = null
        this.head = current.next
        this.count--
        res = current.element
      } else if ( index === 0 && !current.next && !current.prev ) {
        this.head = null
        this.tail = null
        this.count--
        res = current.element
      } else if ( index !== 0 && index === this.count - 1 && current.prev ) {
        current.prev.next = null
        this.tail = current.prev
        this.count--
        res = current.element
      } else if ( index !== 0 && current.prev && current.next ) {
        current.prev.next = current.next
        current.next.prev = current.prev
        this.count--
        res = current.element
      }
    }
    return res
  }

  indexOf( element: T ): number | undefined {
    return super.indexOf( element )
  }

  remove( element: T ): boolean {
    let bool = false
    const index = this.indexOf( element )
    if ( index !== undefined && this.removeAt( index ) ) bool = true
    return bool
  }

  insert( element: T, index: number ): boolean {
    let bool = false
    const node = new DoublyNode<T>( element )
    if ( index >= 0 && index < this.count ) {
      if ( index === 0 && this.tail && this.head ) {
        this.head.prev = node
        node.next = this.head
        this.head = node
        this.count++
        bool = true
      } else {
        const previous = this.getNodeAt( index - 1 )
        const current = this.getNodeAt( index )
        if ( previous && current ) {
          previous.next = node
          node.next = current
          current.prev = node
          node.prev = previous
          this.count++
        }
      }
    }
    return bool
  }

  getHead(): doublyNodeType<T> | null {
    return this.head
  }

  getTail(): doublyNodeType<T> | null {
    return this.tail
  }

  size(): number {
    return super.size()
  }

  isEmpty(): boolean {
    return super.isEmpty()
  }

  clear(): void {
    super.clear()
    this.tail = null
  }
}

循环链表

  • 通过继承LinkedList类实现双向链表
import { LinkedList, linkListType, Node, nodeType } from './linkedList'

export class CircularLinkedList<T> extends LinkedList<T> implements linkListType<T> {

  push( element: T ) {
    const node = new Node( element )
    const current = this.getNodeAt( this.size() - 1 )
    if ( !this.head ) {
      this.head = node
      node.next = this.head
      this.count++
    } else if ( current ) {
      current.next = node
      node.next = this.head
      this.count++
    }
  }

  getNodeAt( index: number ): nodeType<T> | undefined {
    return super.getNodeAt( index )
  }

  removeAt( index: number ): T | undefined {
    const res = super.removeAt( index )
    const tail = this.getNodeAt( this.size() - 1 )
    if ( index === 0 && tail ) tail.next = this.head
    return res
  }

  indexOf( element: T ): number | undefined {
    return super.indexOf( element )
  }

  remove( element: T ): boolean {
    let bool = false
    const index = this.indexOf( element )
    if ( index === 0 ) {
      const tail = this.getNodeAt( this.size() - 1 )
      const current = this.head
      if ( index === 0 && tail && this.head && this.count !== 1 && current ) {
        this.head = current.next
        tail.next = this.head
      } else if ( index === 0 && tail && this.head && this.count === 1 ) {
        this.head = null
      }
      this.count--
      bool = true
    } else {
      bool = super.remove( element )
    }
    return bool
  }

  insert( element: T, index: number ): boolean {
    let res = super.insert( element, index )
    if ( index === 0 ) {
      const tail = this.getNodeAt( this.size() - 1 )
      if ( tail ) tail.next = this.head
    }
    return res
  }

  getHead(): nodeType<T> | null {
    return super.getHead()
  }

  size(): number {
    return super.size()
  }

  isEmpty(): boolean {
    return super.isEmpty()
  }

  clear() {
    super.clear()
  }
}