单向链表
单向链表类似于火车,有一个火车头,火车头会连接一个节点,节点上有乘客,并且这个节点会连接下一个节点,以此类推。
-  链表的火车结构 
  
-  链表的数据结构 
 head 属性指向链表的第一个节点。
 链表中的最后一个节点指向 null。当链表中一个节点也没有的时候,head 直接指向 null。
  
链表的数据结构
-  给火车加上数据后的结构 
  
 链表中的常见操作
-  append(element) 向链表尾部添加一个新的项。 
-  insert(position, element) 向链表的特定位置插入一个新的项。 
-  get(position) 获取对应位置的元素。 
-  indexOf(element) 返回元素在链表中的索引。如果链表中没有该元素就返回-1。 
-  update(position, element) 修改某个位置的元素。 
-  removeAt(position) 从链表的特定位置移除一项。 
-  remove(element) 从链表中移除一项。 
-  isEmpty() 如果链表中不包含任何元素,返回 trun,如果链表长度大于 0 则返回 false。 
-  size() 返回链表包含的元素个数,与数组的 length 属性类似。 
-  toString() 由于链表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString 方法,让其只输出元素的值。 
单向链表的封装
创建单向链表类
先创建单向链表类 LinkedList,添加基本属性,再逐步实现单向链表的常用方法。
  function LinkedList() {
      function NewNode(data) {
        this.data = data
        this.next = null
      }
      this.head = null
      this.length = 0
  }
实现 append() 方法
 LinkedList.prototype.append = function(data){
    let node = new NewNode(data)
    if(this.length === 0){
      this.head = node
    } else {
      let current = this.head
      while(current.next){
        current = current.next
      }
      current.next = node
    }
    this.length += 1
 }
过程图解
- 首先让 currentNode 指向第一个节点。
  
- 通过 while 循环使 currentNode 指向最后一个节点,最后通过 currentNode.next = newNode,让最后一个节点指向新节点 newNode。
  
 代码测试
let linkedList = new LinkedList()
// 测试 append 方法
linkedList.append("A");
linkedList.append("B");
linkedList.append("C");
console.log(linkedList);

 实现 toString() 方法
LinkedList.prototype.toString = function(){
    let str = ''
    let current = this.head
     // 遍历所有的节点,拼接为字符串,直到节点为 null
    while(current){
      str += current.data + ' '
      current = current.next
    }
    return str
}
代码测试
 // 测试 toString 方法
console.log(linkedList.toString()); //--> AA BB CC
实现 insert() 方法
      LinkedList.prototype.insert = function(position, data) {
        // 越界判断
        if(position < 0 || position > this.length) return false;
        const newNode = new Node(data);
        if(position === 0){
          newNode.next = this.head;
          this.head = newNode;
        } else {
          let index = 0;
          let current = this.head;
          let previous = null;
          while(index++ < position){
            previous = current;
            current = current.next;
          }
          newNode.next = current;
          previous.next = newNode;
        }
        this.length += 1;
      }
代码测试
 // 测试 insert 方法
linkedList.insert(0, "123");
linkedList.insert(2, "456");
console.log(linkedList.toString()); //--> 123 AA 456 BB CC
实现 get() 方法
 获取指定位置(position)的 data。
LinkedList.prototype.get = function(position) {
    if(position < 0 || position >= this.length) return null
    let index = 0
    let current = this.head
    while(index++ < position){
      current = current.next
    }
    return current.data
 }
代码测试
 // 测试 getData 方法
console.log(linkedList.get(0)); //--> 123
console.log(linkedList.get(1)); //--> AA
实现 indexOf() 方法
 indexOf(data) 返回指定 data 的 index,如果没有,返回 -1。
 代码实现
LinkedList.prototype.indexOf = function(data) {
    let index = 0
    let current = this.head
    while(current){
      if(current.data === data){
        return index
      }
      current = current.next
      index += 1
    }
    return -1
}
代码测试
 // 测试 indexOf 方法
console.log(linkedList.indexOf("AA")); //--> 1
console.log(linkedList.indexOf("ABC")); //--> -1
实现 update() 方法
 update(position, data) 修改指定位置节点的 data。
 代码实现
  LinkedList.prototype.update = function(position, newData) {
    // 越界判断
    if(position < 0 || position >= this.length) return false;
    let current = this.head;
    let index = 0;
    while(index++ < position){
      current = current.next;        
    }
    current.data = newData;
    return true;
  }
代码测试
 // 测试 update 方法
linkedList.update(0, "12345");
console.log(linkedList.toString()); //--> 12345 AA 456 BB CC
linkedList.update(1, "54321");
console.log(linkedList.toString()); //--> 12345 54321 456 BB CC
实现 removeAt() 方法
 removeAt(position) 删除指定位置的节点。
 代码实现
LinkedList.prototype.removeAt = function(position) {
    // 越界判断
    if(position < 0 || position >= this.length) return null;
    let current = this.head;
    if(position == 0){
      this.head = this.head.next;
    } else {
      let index = 0;
      let previous = null;
      while(index++ < position){
        previous = current;
        current = current.next;
      }
      previous.next = current.next;
    }     
    this.length -= 1;   
    return current.data;
  }
代码测试
 // 测试 removeAt 方法
linkedList.removeAt(3);
console.log(linkedList.toString()); //--> 12345 54321 456 CC
实现 remove() 方法
 remove(data) 删除指定 data 所在的节点。
 代码实现
LinkedList.prototype.remove = function(data) {
    // 获取data在列表中的位置
    const position = this.indexOf(data)
    return this.removeAt(position)
}
代码测试
 // 测试 remove 方法
linkedList.remove("CC");
console.log(linkedList.toString()); //--> 12345 54321 456
实现 isEmpty() 方法
 isEmpty() 判断链表是否为空。
 代码实现
LinkedList.prototype.isEmpty = function() {
  return this.length == 0;
}
代码测试
 // 测试 isEmpty 方法
console.log(linkedList.isEmpty()); //--> false
实现 size() 方法
 size() 获取链表的长度。
 代码实现
LinkedList.prototype.size = function() {
    return this.length;
}
代码测试
 // 测试 size 方法
console.log(linkedList.size()); //--> 3
完整实现
function LinkedList(){
      // 内部类,节点类
      function Node(data){
        this.data = data;
        this.next = null;
      }
      // 属性
      this.head = null;
      this.length = 0;
      LinkedList.prototype.append = function(data) {
        // 创建一个新节点
        const newNode = new Node(data);
        //判断是否添加的第一个节点
        if(this.length == 0){ // 是第一个节点
          this.head = newNode;
        } else { // 不是第一个节点
          let current = this.head;
          while(current.next){ // current.next为null,表示找到了最后一个元素
            current = current.next;
          }
          current.next = newNode;
        }
        this.length += 1;
      }
      LinkedList.prototype.toString = function() {
        let current = this.head;
        let linkString = ''
        while(current){
          linkString += current.data + ' ';
          current = current.next;
        }
        return linkString;
      }
      LinkedList.prototype.insert = function(position, data) {
        // 越界判断
        if(position < 0 || position > this.length) return false;
        const newNode = new Node(data);
        if(position == 0){
          newNode.next = this.head;
          this.head = newNode;
        } else {
          let index = 0;
          let current = this.head;
          let previous = null;
          while(index++ < position){
            previous = current;
            current = current.next;
          }
          newNode.next = current;
          previous.next = newNode;
        }
        this.length += 1;
      }
      LinkedList.prototype.get = function(position) {
        // 越界判断
        if(position < 0 || position >= this.length) return null;
        let current = this.head;
        let index = 0;
          while(index++ < position){
            current = current.next;
          }
          return current.data
      }
      LinkedList.prototype.indexOf = function(data) {
        let current = this.head;
        let index = 0;
        while(current){
          if(current.data === data){
            return index;
          }
          current = current.next;
          index += 1;
        }
        return -1;
      }
      LinkedList.prototype.update = function(position, newData) {
        // 越界判断
        if(position < 0 || position >= this.length) return false;
        let current = this.head;
        let index = 0;
        while(index++ < position){
          current = current.next;        
        }
        current.data = newData;
        return true;
      }
      LinkedList.prototype.removeAt = function(position) {
        // 越界判断
        if(position < 0 || position >= this.length) return null;
        let current = this.head;
        if(position == 0){
          this.head = this.head.next;
        } else {
          let index = 0;
          let previous = null;
          while(index++ < position){
            previous = current;
            current = current.next;
          }
          previous.next = current.next;
        }     
        this.length -= 1;   
        return current.data;
      }
      LinkedList.prototype.remove = function(data) {
        // 获取data在列表中的位置
        const position = this.indexOf(data)
        return this.removeAt(position)
      }
      LinkedList.prototype.isEmpty = function() {
        return this.length == 0;
      }
      LinkedList.prototype.size = function() {
        return this.length;
      }
    }


















