本文总结一下链表常用方法的实现。

准备工作:由于链表由一个个节点组成,且节点在内存中的存储不连续,所以我们需要封装一个内部的Node类,包含两个属性:节点值data和下一个节点的引用地址next。此外在链表类中还需要两个属性:head用来指向第一个节点,length表示当前链表的长度。

      //  内部的类:节点类
      function Node(data) {
        this.data = data
        this.next = null
      }
      //  指向链表中第一项的地址
      this.head = null
      //  链表长度
      this.length = 0

1、append方法:在链表末尾插入一个新节点。

该方法的实现比较简单:创建新节点—若是插入的第一个元素,则修改this.head—若不是第一个元素,则从head开始一步一步走到末尾的节点,让其next指向新节点。最后不要忘记修改length。

LinkedList.prototype.append = function (data) {
        let newNode = new Node(data)
        if (this.length === 0) {
          this.head = newNode
        } else {
          let current = this.head
          while (current.next) {
            current = current.next
          }
          current.next = newNode
        }
        this.length += 1
      }

2、toString方法:将各个节点值以空格为分隔符,以字符串形式输出。

这个方法实现起来也很简单,就是节点遍历+字符串拼接。

LinkedList.prototype.toString = function () {
        let current = this.head
        let listString = ""
        while (current) {
          listString += current.data + " "
          current = current.next
        }
        //  终止循环时,current指向null
        return listString
}

需要注意的一点是,在遍历节点时,若对所有节点执行的操作相同,则以current作为终止条件,退出循环时current指向null;若需要对最后一个节点执行单独的操作,则以current.next作为终止条件,这样退出循环时current指向最后一个节点。

3、get方法:根据索引获取对应节点的值。

这个方法就是读操作,只需要设置一个指针变量,让其逐步前进,当其与传入的索引相等时,返回指针所在节点的值即可。

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
}

可见,循环退出时,指针Index恰好指向正确的节点。此外,这里对传入的索引进行了边界判断,如果超出了当前链表的索引范围,则读取到的值为null。

4、indexOf方法:根据值获取对应的索引。

设置一个变量动态存储索引,仍然是从头开始遍历,每比较一个节点将索引值加一,直到找到对应的节点,返回此时的Index。如果遍历结束仍未找到,则返回-1。

LinkedList.prototype.indexOf = function(data){
        let index = 0
        let current = this.head
        while(current){
          if(data === current.data){
            return index
          }else{
            current = current.next
            index++
          }
        }
        return -1
}

5、upDate方法:根据索引修改对应节点的值。

这个方法与get方法差不多,无非是在找到对应节点后,不返回而是修改其值。

LinkedList.prototype.update = function(position, newData) {
        if(position < 0 || position >= this.length)  return false
        let index = 0;
        let current = this.head
        while( index++ < position){
          current = current.next
        }
        current.data = newData
        return true
}

6、insert方法:在指定索引处插入新元素。

这个方法需要分类讨论。指定的索引是否越界?新元素是否插入在头部?如果不在,那么需要两个指针:curr,指向指定索引原来的元素;prev,指向curr前一个元素。每次循环,将prev指向curr。到达正确的索引后,将prev的next指向新节点,将新节点的next执行curr。最后不要忘记修改长度。

LinkedList.prototype.insert = function(position, data){
        if(position < 0 || position > this.length)  return false
        let newNode = new Node(data)
        if(position == 0){
          newNode.next = this.head
          this.head = newNode
        }else{
          let current = this.head
          let previous = null
          let index = 0
          while(index++ < position){
            previous = current
            current = current.next
          }
          previous.next = newNode
          newNode.next = current
        }
        this.length += 1
        return true
}

7、removeAt方法:删除指定索引处的元素。

这个方法实现思路几乎与上一个相同,只需要比较一下删除和插入的区别,修改一些细节即可。记得修改length的长度。

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 previous = null
          let index = 0
          while(index++ < position){
            previous = current
            current = current.next
          }
          previous.next = current.next
        }
        this.length -= 1
        return current.data
}

值得注意的是:因为需要将删除的数据返回,即要在else语句块外面使用current,所以把current的定义放在外部。

8、remove方法:删除指定值。

没什么好说的,先根据值获取索引(indexOf方法),然后根据索引删除元素(removeAt)。

LinkedList.prototype.remove = function(data){
        let position = this.indexOf(data)
        this.removeAt(position)
}

9、isEmpty方法:返回链表是否为空。

等价于长度是否为0。

LinkedList.prototype.isEmpty = function(){
        return this.length === 0
}

10、size方法:返回链表长度。

等价于长度。

LinkedList.prototype.size = function(){
        return this.length
}

 

posted on 2021-06-22 19:26  springxxxx  阅读(65)  评论(0编辑  收藏  举报