链表

115 阅读1分钟

1. 链表数据结构

  • 链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。

  • 相对于传统的数组,链表的一个好处在于,==添加或移除元素的时候不需要移动其他元素==。然而,链表需要使用指针,因此实现链表时需要额外注意。在数组中,我们可以直接访问任何位置的任何元素,而要想访问链表中间的一个元素,则需要从==起点(表头)开始迭代链表直到找到所需的元素==。

    function defaultEquals(a, b) {
      return a === b;
    }
    
    class Node {
      constructor(element) {
        this.element = element;
        this.next = undefined;
      }
    }
    
    class LinkedList {
      constructor(equalsFn = defaultEquals) {
        this.conut = 0;
        this.head = undefined;
        this.equalsFn = equalsFn;
      }
      push(element) {
        //向链表尾部添加一个元素
        const node = new Node(element);
        let current;
        if (this.head == null) {
          this.head = node;
        } else {
          current = this.head;
          while (current.next != null) {
            current = current.next;
          }
          current.next = node;
        }
        this.conut++;
      }
      getElementAt(index) {
        if (index >= 0 && index <= this.count) {
          let node = this.head;
          for (let i = 0; i < index && node != null; i++) {
            node = node.next;
          }
          return node;
        }
        return undefined;
      }
      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
            }
            //将previous与current的下一项连接起来:跳过current,从而移除它
            previous.next = current.next
          }
          this.count--
          return current
        }
        return undefined
      }
      remove(element) {
        const index = this.indexOf(element);
        return this.removeAt(index);
      }
      insert(element, index) {
        if (index >= 0 && index <= this.count) {
          const node = new Node(element)
          if (index === 0) {//在第一个位置添加
            const current = this.head
            node.next = current
            this.head = node
          } else {
            const previous = this.getElementAt(index - 1)
            const current = previous.next
            node.next = current
            previous.next = node
          }
          this.count++
          return true
        }
        return false
      }
      indexOf(element) {
        //返回一个元素的位置
        let current = this.head;
        for (let i = 0; i < this.size() && current != null; i++) {
          if (this.equalsFn(element, current.element)) {
            return i;
          }
          current = current.next;
        }
        return -1;
      }
      isEmpty() {
        return this.size() === 0;
      }
      size() {
        return this.count;
      }
      getHead() {
        return this.head;
      }
      clear() {
        this.head = undefined;
        this.count = 0;
      }
      toString() {
        if (this.head == null) {
          return '';
        }
        let objString = `${this.head.element}`;
        let current = this.head.next;
        for (let i = 1; i < this.size() && current != null; i++) {
          objString = `${objString},${current.element}`;
          current = current.next;
        }
        return objString;
      }
    }
    

2. 双向链表

  • 双向链表和普通链表的区别在于,在普通链表中,一个节点只有链向下一个节点的链接;而在双向链表中,链接是双向的:一个链向上一个元素,一个链向下一个元素。
//双向链表
class DoublyNode extends Node{
  constructor(element, next, prev) {
    super(element, next)
    this.prev = prev
  }
}

class DoublyLinkedList extends LinkedList{
  constructor(equalsFn = defaultEquals) {
    super(equalsFn)
    this.tail = undefined
  }
  insert(element, index) {
    if (index >= 0 && index <= this.count) {
      const node = new DoublyNode(element)
      let current = this.head
      if (index === 0) {//在第一项中插入
        if (this.head == null) {
          this.head = node
          this.tail = node
        } else {
          node.next = this.head
          current.prev = node
          this.head = node
        }
      } else if (index === this.count) {//在最后一项插入
        current = this.tail
        current.next = node
        node.prev = current
        this.tail = node
      } else {
        const previous = this.getElementAt(index - 1)
        current = previous.next
        node.next = current
        previous.next = node
        current.prev = node
        node.prev = previous
      }
      this.count++
      return true
    }
    return false
  }
  removeAt(index) {
    if (index >= 0 && index < this.count) {
      let current = this.head
      if (index === 0) {//移除第一项
        this.head = current.next
        //r如果只有一项,更新tail
        if (this.conut === 1) {
          this.tail = undefined
        } else {
          this.head.prev = undefined
        }
      } else if (index === this.count - 1) {//最后一项
        current = this.tail
        this.tail = current.prev
        this.tail.next = undefined
      } else {
        current = this.getElementAt(index)
        const previous = current.prev
        previous.next = current.next
        current.next.prev = previous
      }
      this.count--
      return current.element
    }
    return undefined
  }
}

3. 循环链表

  • 循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别就是==最后一个元素指向下一个元素不是引用undefined,而是指向第一个元素==。
//循环链表
class CircularLinkedList extends LinkedList{
  constructor(equalsFn = defaultEquals) {
    super(equalsFn)
  }
  insert(element, index) {
    if (index >= 0 && index <= this.count) {
      const node = new Node(element)
      let current = this.head
      if (index === 0) {
        if (this.head == null) {
          this.node = node
          node.next = this.head
        } else {
          node.next = current
          current = this.getElementAt(this.size())
          this.head = node
          current.next = this.head
        }
      } else {
        const previous = this.getElementAt(index-1)
        node.next = previous.next
        previous.next = node
      }
      this.count++
      return true
    }
    return false
  }
  removeAt(index) {
    if (index >= 0 & index < this.count) {
      if (index === 0) {
        if (this.size() === 1) {
          this.head = undefined
        } else {
          const removed = this.head
          current = this.getElementAt(this.size())
          this.head = this.head.next
          current.next = this.head
          current = removed
        }
      } else {
        const previous = this.getElementAt(index - 1)
        current = previous.next
        previous.next = current.next
      }
      this.count--
      return current.element
    }
    return undefined
  }
}

4. 有序链表

  • 有序链表是指==保持元素有序==的链表结构
//有序链表
const Compare = {
  LESS_THAN: -1,
  BIGGER_THAN: 1
}
function defaultCompare(a, b) {
  if (a === b) {
    return 0
  }
  return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN
}

class SortLinkedList extends LinkedList{
  constructor(equalsFn = defaultEquals, compareFn = defaultCompare) {
    super(equalsFn)
    this.compareFn = compareFn
  }
  insert(element, index = 0) {
    if (this.isEmpty()) {
      return super.insert(element, 0)
    }
    const pos = this.getIndexNextSortedElement(element)
    return super.insert(element, pos)
  }
  getIndexNextSortedElement(element) {
    let current = this.head
    let i = 0
    for (; i < this.size() && current; i++){
      const comp = this.compareFn(element, current.element)
      if (comp === Compare.LESS_THAN) {
        return i
      }
      current = current.next
    }
    return i
  }

}