数据结构-链表 JavaScript | 有图 | 有过程 | 有真相

链表结构

配套练习算法地址

1. 背景

在一开始,不知大家用了这么久的数组,你有没有发现数组存在两个明显的缺陷?

  • 一个是数组中所有元素的类型必须一致;

  • 第二个是数组的元素个数必须事先制定并且一旦指定之后不能更改。

于是乎为了解决数组的缺陷,先辈们发明的一些特殊方法来解决:

  • 数组的第一个缺陷靠结构体去解决。结构体允许其中的元素的类型不相同,因此解决了数组的第一个缺陷。所以说结构体是因为数组不能解决某些问题所以才发明的;

结构体是由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的“成员” ,其描述了一块内存区间的大小及解释意义

  • 我们希望数组的大小能够实时扩展。譬如我刚开始定了一个元素个数是10,后来程序运行时觉得不够因此动态扩展为20.普通的数组显然不行,我们可以对数组进行封装以达到这种目的;我们还可以使用一个新的数据结构来解决,这个新的数据结构就是链表(几乎可以这样理解:链表就是一个元素个数可以实时变大/变小的数组)。

2. 什么是链表

顾名思义,链表就是用锁链连接起来的表。这里的表指的是一个一个的节点(一个节点可以比喻成火车的一节车厢),节点中有一些内存可以用来存储数据(所以叫表,表就是数据表); 那怎么能更形象的表达一下链表呢?对于这个问题,我们先来看一张图,如下:

在这里插入图片描述

存在一个火车头(head)和三个车厢(节点)且相连接,

我们给他抽象一下会得到另外一张图,如下:

在这里插入图片描述

由此,我们可以得到一副图形化的链表图:
在这里插入图片描述

它由头指针(Head)和若干个节点(节点包括了数据域(data)和指针域(next)通过指针(next)连接起来的一个表。

3. 链表的优势

  • 要存储多个元素,除开数组之外也可以使用链表
  • 不同于数组,链表中的元素在内存中不必是连续的空间
  • 链表的每个元素由一个存储本身的的节点和一个指向下一个元素的引用组成。

3.1 优点(相对于数组)

  1. 内存空间不必要是连续的。可以充分利用计算机的内存,实现灵活的内存动态管理。
  2. 链表不必在创建时就确认大小,并且大小可以无限延伸下去
  3. 链表在插入和删除时,时间复杂度可以达到O(1),相对数组效率很高

3.1 缺点(相对于数组)

  1. 链表访问任何一个元素时都需要从头开始访问
  2. 无法通过下标直接访问元素

4. 单链表的基本设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FccGiqvF-1660210869909)(E:\Web\test\Notes\数据结构与算法\链表\images\linkedList.png)]

4.1 基本类封装

// 定义节点
class LinkedNode {
  constructor(data) {
    this.data = data
    this.next = null
  }
}

// 定义链表
class LinkedList {
  constructor() {
    // 头节点
    this._head = null
    // 链表大小
    this._size = 0
  }
}

接下来,关于链表的操作我们就是一个一个的往LinkedList类中添加方法。

4.2 链表常用操作

  • append(ele):在表尾插入新节点。
  • insert(position,data):向列表中特定位置插入一个新的项。
  • get(position):通过下标获取对应位置的节点内容。
  • update(position,data):修改指定位置节点内容。
  • indexOf(data):获取元素在链表中的位置,返回值为-1表示不存在该元素;
  • removeAt(position):移除链表中指定位置的项。
  • remove(data):从链表中移除指定项。
  • isEmpty():判断链表是否为空,返回值为true表示链表为空;false不为空
  • size():获取链表大小
  • toString():返回当前链表内容。

4.3 append(ele)

// append(ele):在表尾插入新节点。无返回值。
append(data) {
  // 创建新节点
  const newNode = new LinkedNode(data)
  if (this._size === 0) {
    // 判断为添加第一个节点的时候
    this._head = newNode
  } else {
    // 找到最后一个节点,并将新节点插入到其后
    let currentNode = this._head
    while (currentNode.next) {
      currentNode = currentNode.next
    }
    currentNode.next = newNode
  }

  this._size += 1
}

4.4 insert(position,data)

// insert(position,data):向列表中特定位置插入一个新的项。
insert(position, data) {
  // 1. 越界判断
  if (position < 0 || position > this._size) return false

  // 2. 创建新节点
  const newNode = new LinkedNode(data)

  if (position === 0) {
    // 3. 判断插入头部的情况
    newNode.next = this._head
    this._head = newNode
  } else {
    // 找到需要插入节点的地方并插入
    let index = 0
    let currentNode = this._head
    let previousNode = null

    while (index++ < position) { 
      previousNode = currentNode
      currentNode = currentNode.next
    }
    previousNode.next = newNode
    newNode.next = currentNode
  }

  this._size += 1
  return true
}

4.5 get(position)

// get(position):获取对应位置的节点内容
get(position) {
  if (position < 0 || position >= this._size) return -1
  let index = 0
  let currentNode = this._head
  while (index++ < position) {
    currentNode = currentNode.next
  }
  return currentNode.data
}

4.6 update(position,data)

// update(position,data):修改指定位置节点内容
update(position, data) {
  // 1. 越界判断
  if (position < 0 || position >= this._size || this.isEmpty()) return -1

  if (position === 0) {
    // 2. 更新第一个
    this._head.data = data
  } else {
    // 其他
    let index = 0
    let currentNode = this._head
    while (index++ < position) {
      currentNode = currentNode.next
    }
    currentNode.data = data
  }
}

4.7 indexOf(data)

// indexOf(data):获取元素在链表中的位置
indexOf(data) {
  // 1. 判断链表是否为空
  if (this.isEmpty()) return -1

  if (this._size === 1) {
    // 当只有一个元素的时候
    return this._head.data === data ? 0 : -1
  } else {
    // 其他
    let index = 0
    let currentNode = this._head
    while (currentNode && currentNode.data !== data) {
      currentNode = currentNode.next
      index += 1
    }
    return currentNode ? index : -1
  }
}

4.8 removeAt(position)

// removeAt(position):移除链表中指定位置的项
removeAt(position) {
  // 1. 越界判断 /非空判断 
  if (position < 0 || position >= this._size || this._size === 0) return false;
  if (position === 0) {
    // 2. 链表仅有一个节点
    this._head = this._head.next
  } else {
    // 3. 找到目标节点并删除
    let index = 1
    let currentNode = this._head.next
    let previousNode = this._head
    while (index++ < position) {
      previousNode = currentNode
      currentNode = currentNode.next
    }
    previousNode.next = currentNode.next
  }

  this._size -= 1
}

4.9 remove(data)

// remove(data):从链表中移除指定项
remove(data) {
  if (this.indexOf(data)) {
    this.removeAt(this.indexOf(data))
  }
}

4.10 isEmpty()

// isEmpty():判断链表是否为空
isEmpty() {
  return !this._size
}

4.11 isEmpty()

// size():获取链表大小
size() {
  return this._size
}

4.12 toString()

// 回当前链表的字符串内容
toString() {
  let currentNode = this._head
  let listStr = ""
  while (currentNode) {
    listStr += currentNode.data + " "
    currentNode = currentNode.next
  }

  return listStr === "" ? null : listStr
}

4.13 链表演示

var list = new LinkedList()
console.log("new LinkedList:", list.toString() || null);
list.append("aa")
list.append("bb")
list.append("cc")
console.log("append(toString):", list.toString());
list.insert(0, "1")
list.insert(4, "3")
list.insert(2, "2")
console.log("insert(0,4,2):", list.toString());
console.log("get(0,1,2,3,4,5):", list.get(0) + " ", list.get(1) + " ", list.get(2) + " ", list.get(3) + " ", list.get(4) + " ", list.get(5) + " ");
list.update(1, "Aa")
list.update(3, "Bb")
list.update(4, "Cc")
console.log("update(1,3,4):", list.toString());
list.removeAt(0)
list.removeAt(2)
list.removeAt(3)
console.log("removeAt(0,2,3):", list.toString());
console.log("index(Aa,2,Cc,3,4,5):", list.indexOf('Aa') + " ", list.indexOf('2') + " ", list.indexOf('Cc') + " ", list.indexOf('3') + " ", list.indexOf('4') + " ", list.indexOf('5') + " ");
list.remove('Cc')
list.remove('Cc111')
console.log("remove(Cc,Cc111):", list.toString());
console.log("size():", list.size());
console.log("isEmpty():", list.isEmpty());

打印结果:

new LinkedList: null
append(toString): aa bb cc
insert(0,4,2): 1 aa 2 bb cc 3
get(0,1,2,3,4,5): 1  aa  2  bb  cc  3     
update(1,3,4): 1 Aa 2 Bb Cc 3
removeAt(0,2,3): Aa 2 Cc
index(Aa,2,Cc,3,4,5): 0  1  2  -1  -1  -1 
remove(Cc,Cc111): Aa 2
size(): 2
isEmpty(): false

4.14 完整代码

可通过node执行或者script引用执行

// 定义节点
class LinkedNode {
  constructor(data) {
    this.data = data
    this.next = null
  }
}

// 定义链表
class LinkedList {
  constructor() {
    // 头节点
    this._head = null
    // 链表大小
    this._size = 0
  }

  // append(ele):在表尾插入新节点。无返回值。
  append(data) {
    // 创建新节点
    const newNode = new LinkedNode(data)
    if (this._size === 0) {
      // 判断为添加第一个节点的时候
      this._head = newNode
    } else {
      // 找到最后一个节点,并将新节点插入到其后
      let currentNode = this._head
      while (currentNode.next) {
        currentNode = currentNode.next
      }
      currentNode.next = newNode
    }

    this._size += 1
  }

  // insert(position,data):向列表中特定位置插入一个新的项。
  insert(position, data) {
    // 1. 越界判断
    if (position < 0 || position > this._size) return false

    // 2. 创建新节点
    const newNode = new LinkedNode(data)

    if (position === 0) {
      // 3. 判断插入头部的情况
      newNode.next = this._head
      this._head = newNode
    } else {
      // 找到需要插入节点的地方并插入
      let index = 0
      let currentNode = this._head
      let previousNode = null

      while (index++ < position) { 
        previousNode = currentNode
        currentNode = currentNode.next
      }
      previousNode.next = newNode
      newNode.next = currentNode
    }

    this._size += 1
    return true
  }

  // get(position):获取对应位置的节点内容
  get(position) {
    if (position < 0 || position >= this._size) return -1
    let index = 0
    let currentNode = this._head
    while (index++ < position) {
      currentNode = currentNode.next
    }
    return currentNode.data
  }

  // update(position,data):修改指定位置节点内容
  update(position, data) {
    // 1. 越界判断
    if (position < 0 || position >= this._size || this.isEmpty()) return -1

    if (position === 0) {
      // 2. 更新第一个
      this._head.data = data
    } else {
      // 其他
      let index = 0
      let currentNode = this._head
      while (index++ < position) {
        currentNode = currentNode.next
      }
      currentNode.data = data
    }
  }

  // indexOf(data):获取元素在链表中的位置
  indexOf(data) {
    // 1. 判断链表是否为空
    if (this.isEmpty()) return -1

    if (this._size === 1) {
      // 当只有一个元素的时候
      return this._head.data === data ? 0 : -1
    } else {
      // 其他
      let index = 0
      let currentNode = this._head
      while (currentNode && currentNode.data !== data) {
        currentNode = currentNode.next
        index += 1
      }
      return currentNode ? index : -1
    }
  }

  // removeAt(position):移除链表中指定位置的项
  removeAt(position) {
    // 1. 越界判断 /非空判断 
    if (position < 0 || position >= this._size || this._size === 0) return false;
    if (position === 0) {
      // 2. 链表仅有一个节点
      this._head = this._head.next
    } else {
      // 3. 找到目标节点并删除
      let index = 1
      let currentNode = this._head.next
      let previousNode = this._head
      while (index++ < position) {
        previousNode = currentNode
        currentNode = currentNode.next
      }
      previousNode.next = currentNode.next
    }

    this._size -= 1
  }

  // remove(data):从链表中移除指定项
  remove(data) {
    if (this.indexOf(data)) {
      this.removeAt(this.indexOf(data))
    }
  }

  // isEmpty():判断链表是否为空
  isEmpty() {
    return !this._size
  }

  // size():获取链表大小
  size() {
    return this._size
  }

  // toString():返回当前链表内容。
  toString() {
    let currentNode = this._head
    let listStr = ""
    while (currentNode) {
      listStr += currentNode.data + " "
      currentNode = currentNode.next
    }

    return listStr === "" ? null : listStr
  }
}

5. 双链表的基本设计

在这里插入图片描述

5.1 基本类封装

// 节点类
class LinkedNode {
  constructor(data) {
    this.data = data
    this.next = null
    this.prev = null
  }
}

// 链表
class DoubleLinkedList {
  constructor() {
    this._head = null
    this._tail = null
    this._size = 0
  }
}

接下来,关于链表的操作我们就是一个一个的往LinkedList类中添加方法。

5.2 链表常用操作

  • append(ele):在表尾插入新节点。
  • insert(position,data):向列表中特定位置插入一个新的项。
  • get(position):通过下标获取对应位置的节点内容。
  • update(position,data):修改指定位置节点内容。
  • indexOf(data):获取元素在链表中的位置,返回值为-1表示不存在该元素;
  • removeAt(position):移除链表中指定位置的项。
  • remove(data):从链表中移除指定项。
  • isEmpty():判断链表是否为空,返回值为true表示链表为空;false不为空
  • size():获取链表大小
  • forwordString():返回正向遍历当前链表内容。
  • backwordString():返回反向遍历当前链表内容。

5.3 append(ele)

// append(ele):在表尾插入新节点。无返回值。
append(data) {
  // 1. 创建新节点
  const newNode = new LinkedNode(data)

  // 2. 判断当链表为空的时候
  if (this._size === 0) {
    this._head = newNode
    this._tail = newNode
  } else {
    this._tail.next = newNode
    newNode.prev = this._tail
    this._tail = newNode
  }
  this._size += 1
}

5.4 insert(position,data)

// insert(position,data):向列表中特定位置插入一个新的项。
insert(position, data) {
  // 1. 越界判断
  if (position < 0 || position > this._size) return false;

  // 2. 创建新节点
  const newNode = new LinkedNode(data)

  if (this._size === 0) {
    // 3. 判断列表是否为空
    this._head = newNode
    this._tail = newNode
  } else {
    if (position === 0) {
      // 4. 判断position为 0 的时候
      this._head.prev = newNode
      newNode.next = this._head
      this._head = newNode
    } else if (position === this._size) {
      // 5. 判断插入最后的时候
      this._tail.next = newNode
      newNode.prev = this._tail
      this._tail = newNode
    } else {
      // 6. 找到需要插入节点的地方
      let currentNode = this._head
      let index = 0
      while (index++ < position) {
        currentNode = currentNode.next
      }

      // 7. 插入
      newNode.next = currentNode
      newNode.prev = currentNode.prev
      currentNode.prev.next = newNode
      currentNode.prev = newNode
    }
  }

  this._size += 1
  return true
}

5.5 get(position)

// get(position):获取对应位置的节点内容
get(position) {
  // 1. 越界判断
  if (position < 0 || position >= this._size) return false;

  if (this._size === 0) {
    // 2. 链表为空的时候
    return null
  } else {
    if (position === 0) {
      // 3. 获取第一个节点的时候
      return this._head.data
    } else if (position === this._size - 1) {
      // 4. 获取表尾的时候
      return this._tail.data
    } else {
      // 5. 找到指定位置的节点并返回内容
      let currentNode = this._head.next
      let index = 1
      while (index++ < position) {
        currentNode = currentNode.next
      }
      return currentNode.data
    }
  }
}

5.6 update(position,data)

// update(position,data):修改指定位置节点内容
update(position, data) {
  // 1. 越界判断
  if (position < 0 || position >= this._size) return false;

  if (this._size === 0) {
    // 2. 当列表为空的时候
    return false
  } else {
    if (position === 0) {
      // 3. 更新第一个节点的时候
      this._head.data = data
    } else if (position === this._size - 1) {
      // 4. 更新最后一个节点
      this._tail.data = data
    } else {
      // 5. 找到节点并更新内容
      let currentNode = this._head.next
      let index = 1
      while (index++ < position) {
        currentNode = currentNode.next
      }
      currentNode.data = data
      return true
    }
  }
}

5.7 indexOf(data)

// indexOf(data):获取元素在链表中的位置
indexOf(data) {
  if (this._size === 0) {
    // 1. 当链表为空的时候
    return -1
  } else if (this._size === 1) {
    // 2. 当链表只有一个节点的时候
    return this._head.data === data ? 0 : -1
  } else {
    if (this._head.data === data) {
      // 3. 当查询的节点为第一个节点时
      return 0
    } else if (this._tail.data === data) {
      // 4. 当查询的节点是最后一个节点时
      return this._size - 1
    } else {
      // 5. 查询剩余节点是否含有传入的节点,有则返回下标,无则返回-1
      let currentNode = this._head.next
      let index = 1
      while (currentNode) {
        if (currentNode.data === data) {
          return index
        }

        index += 1
        currentNode = currentNode.next
      }

      return -1;
    }
  }
}

5.8 removeAt(position)

// removeAt(position):移除链表中指定位置的项
removeAt(position) {
  // 1. 越界判断
  if (position < 0 || position >= this._size) return false;

  if (this._size === 0) {
    // 2. 当列表为空的时候
    return false
  } else if (this._size === 1) {
    // 3. 链表仅有一个节点
    this._head = null
    this._tail = null
  } else {
    if (position === 0) {
      // 4. 删除第一个节点的时候
      this._head.next.prev = null
      this._head = this._head.next
    } else if (position === this._size - 1) {
      // 5. 删除最后一个节点
      this._tail.prev.next = null
      this._tail = this._tail.prev
    } else {
      // 6. 找到节点并删除内容
      let currentNode = this._head.next
      let index = 1
      while (index++ < position) {
        currentNode = currentNode.next
      }
      currentNode.next.prev = currentNode.prev
      currentNode.prev.next = currentNode.next
    }
  }

  // 7. 更新size
  this._size -= 1
}

5.9 remove(data)

// remove(data):从链表中移除指定项
remove(data) {
  if (this.indexOf(data)) {
    this.removeAt(this.indexOf(data))
  }
}

5.10 isEmpty()

// isEmpty():判断链表是否为空
isEmpty() {
  return !this._size
}

4.11 isEmpty()

// size():获取链表大小
size() {
  return this._size
}

5.12 forwordString()

// forwordString():返回正向遍历当前链表内容。
forwordString() {
  let currentNode = this._head
  let targetString = ""
  while (currentNode) {
    targetString += currentNode.data + " "
    currentNode = currentNode.next
  }

  return targetString
}

5.13 backwordString()

// backwordString():返回反向遍历当前链表内容。
backwordString() {
  let currentNode = this._tail
  let targetString = ""
  while (currentNode) {
    targetString += currentNode.data + " "
    currentNode = currentNode.prev
  }

  return targetString
}

5.14 链表演示

var list = new LinkedList()
console.log("new LinkedList:", list.toString() || null);
list.append("aa")
list.append("bb")
list.append("cc")
console.log("append(toString):", list.toString());
list.insert(0, "1")
list.insert(4, "3")
list.insert(2, "2")
console.log("insert(0,4,2):", list.toString());
console.log("get(0,1,2,3,4,5):", list.get(0) + " ", list.get(1) + " ", list.get(2) + " ", list.get(3) + " ", list.get(4) + " ", list.get(5) + " ");
list.update(1, "Aa")
list.update(3, "Bb")
list.update(4, "Cc")
console.log("update(1,3,4):", list.toString());
list.removeAt(0)
list.removeAt(2)
list.removeAt(3)
console.log("removeAt(0,2,3):", list.toString());
console.log("index(Aa,2,Cc,3,4,5):", list.indexOf('Aa') + " ", list.indexOf('2') + " ", list.indexOf('Cc') + " ", list.indexOf('3') + " ", list.indexOf('4') + " ", list.indexOf('5') + " ");
list.remove('Cc')
list.remove('Cc111')
console.log("remove(Cc,Cc111):", list.toString());
console.log("size():", list.size());
console.log("isEmpty():", list.isEmpty());

打印结果:

new LinkedList: null
append(toString): aa bb cc
insert(0,4,2): 1 aa 2 bb cc 3
get(0,1,2,3,4,5): 1  aa  2  bb  cc  3     
update(1,3,4): 1 Aa 2 Bb Cc 3
removeAt(0,2,3): Aa 2 Cc
index(Aa,2,Cc,3,4,5): 0  1  2  -1  -1  -1 
remove(Cc,Cc111): Aa 2
size(): 2
isEmpty(): false

5.14 完整代码

可通过node执行或者script引用执行

// 节点类
class LinkedNode {
  constructor(data) {
    this.data = data
    this.next = null
    this.prev = null
  }
}

// 链表
class DoubleLinkedList {
  constructor() {
    this._head = null
    this._tail = null
    this._size = 0
  }

  // append(ele):在表尾插入新节点。无返回值。
  append(data) {
    // 1. 创建新节点
    const newNode = new LinkedNode(data)

    // 2. 判断当链表为空的时候
    if (this._size === 0) {
      this._head = newNode
      this._tail = newNode
    } else {
      this._tail.next = newNode
      newNode.prev = this._tail
      this._tail = newNode
    }
    this._size += 1
  }
  // insert(position,data):向列表中特定位置插入一个新的项。
  insert(position, data) {
    // 1. 越界判断
    if (position < 0 || position > this._size) return false;

    // 2. 创建新节点
    const newNode = new LinkedNode(data)

    if (this._size === 0) {
      // 3. 判断列表是否为空
      this._head = newNode
      this._tail = newNode
    } else {
      if (position === 0) {
        // 4. 判断position为 0 的时候
        this._head.prev = newNode
        newNode.next = this._head
        this._head = newNode
      } else if (position === this._size) {
        // 5. 判断插入最后的时候
        this._tail.next = newNode
        newNode.prev = this._tail
        this._tail = newNode
      } else {
        // 6. 找到需要插入节点的地方
        let currentNode = this._head
        let index = 0
        while (index++ < position) {
          currentNode = currentNode.next
        }

        // 7. 插入
        newNode.next = currentNode
        newNode.prev = currentNode.prev
        currentNode.prev.next = newNode
        currentNode.prev = newNode
      }
    }

    this._size += 1
    return true
  }
  // get(position):获取对应位置的节点内容
  get(position) {
    // 1. 越界判断
    if (position < 0 || position >= this._size) return false;

    if (this._size === 0) {
      // 2. 链表为空的时候
      return null
    } else {
      if (position === 0) {
        // 3. 获取第一个节点的时候
        return this._head.data
      } else if (position === this._size - 1) {
        // 4. 获取表尾的时候
        return this._tail.data
      } else {
        // 5. 找到指定位置的节点并返回内容
        let currentNode = this._head.next
        let index = 1
        while (index++ < position) {
          currentNode = currentNode.next
        }
        return currentNode.data
      }
    }
  }
  // update(position,data):修改指定位置节点内容
  update(position, data) {
    // 1. 越界判断
    if (position < 0 || position >= this._size) return false;

    if (this._size === 0) {
      // 2. 当列表为空的时候
      return false
    } else {
      if (position === 0) {
        // 3. 更新第一个节点的时候
        this._head.data = data
      } else if (position === this._size - 1) {
        // 4. 更新最后一个节点
        this._tail.data = data
      } else {
        // 5. 找到节点并更新内容
        let currentNode = this._head.next
        let index = 1
        while (index++ < position) {
          currentNode = currentNode.next
        }
        currentNode.data = data
        return true
      }
    }
  }
  // removeAt(position):移除链表中指定位置的项
  removeAt(position) {
    // 1. 越界判断
    if (position < 0 || position >= this._size) return false;

    if (this._size === 0) {
      // 2. 当列表为空的时候
      return false
    } else if (this._size === 1) {
      // 3. 链表仅有一个节点
      this._head = null
      this._tail = null
    } else {
      if (position === 0) {
        // 4. 删除第一个节点的时候
        this._head.next.prev = null
        this._head = this._head.next
      } else if (position === this._size - 1) {
        // 5. 删除最后一个节点
        this._tail.prev.next = null
        this._tail = this._tail.prev
      } else {
        // 6. 找到节点并删除内容
        let currentNode = this._head.next
        let index = 1
        while (index++ < position) {
          currentNode = currentNode.next
        }
        currentNode.next.prev = currentNode.prev
        currentNode.prev.next = currentNode.next
      }
    }

    // 7. 更新size
    this._size -= 1
  }
  // indexOf(data):获取元素在链表中的位置
  indexOf(data) {
    if (this._size === 0) {
      // 1. 当链表为空的时候
      return -1
    } else if (this._size === 1) {
      // 2. 当链表只有一个节点的时候
      return this._head.data === data ? 0 : -1
    } else {
      if (this._head.data === data) {
        // 3. 当查询的节点为第一个节点时
        return 0
      } else if (this._tail.data === data) {
        // 4. 当查询的节点是最后一个节点时
        return this._size - 1
      } else {
        // 5. 查询剩余节点是否含有传入的节点,有则返回下标,无则返回-1
        let currentNode = this._head.next
        let index = 1
        while (currentNode) {
          if (currentNode.data === data) {
            return index
          }

          index += 1
          currentNode = currentNode.next
        }

        return -1;
      }
    }
  }
  // remove(data):从链表中移除指定项
  remove(data) {
    if (this.indexOf(data)) {
      this.removeAt(this.indexOf(data))
    }
  }

  // isEmpty():判断链表是否为空
  isEmpty() {
    return !this._size
  }
  // size():获取链表大小
  size() {
    return this._size
  }

  // forwordString():返回正向遍历当前链表内容。
  forwordString() {
    let currentNode = this._head
    let targetString = ""
    while (currentNode) {
      targetString += currentNode.data + " "
      currentNode = currentNode.next
    }

    return targetString
  }
  // backwordString():返回反向遍历当前链表内容。
  backwordString() {
    let currentNode = this._tail
    let targetString = ""
    while (currentNode) {
      targetString += currentNode.data + " "
      currentNode = currentNode.prev
    }

    return targetString
  }
}

6. 补充

循环链表: 在链表的基础上,将尾节点的指针指向头结点,就构成了一个循环链表。环形链表从任意一个节点开始,都可以遍历整个链表。 循环单链表与循环双链表的主要不同之处在于单向与双向的区别。

循环单链表:
在这里插入图片描述

循环双链表:
在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值