单向链表
链表和数组一样,可以用于存储一系列的元素,但是链表和数组的实现机制完全不同。链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有的语言称为指针或连接)组成。类似于火车头,一节车厢载着乘客(数据),通过节点连接另一节车厢。
- head属性指向链表的第一个节点;
- 链表中的最后一个节点指向null;
- 当链表中一个节点也没有的时候,head直接指向null;
数组存在的缺点:
- 数组的创建通常需要申请一段连续的内存空间(一整块内存),并且大小是固定的。所以当原数组不能满足容量需求时,需要扩容(一般情况下是申请一个更大的数组,比如2倍,然后将原数组中的元素复制过去)。
- 在数组的开头或中间位置插入数据的成本很高,需要进行大量元素的位移。
链表的优势:
- 链表中的元素在内存中不必是连续的空间,可以充分利用计算机的内存,实现灵活的内存动态管理。
- 链表不必在创建时就确定大小,并且大小可以无限地延伸下去。
- 链表在插入和删除数据时,时间复杂度可以达到O(1),相对数组效率高很多。
链表的缺点:
- 链表访问任何一个位置的元素时,都需要从头开始访问(无法跳过第一个元素访问任何一个元素)。
- 无法通过下标值直接访问元素,需要从头开始一个个访问,直到找到对应的元素。
- 虽然可以轻松地到达下一个节点,但是回到前一个节点是很难的。
链表中的常见操作:
- 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方法,让其只输出元素的值;
// 代码实现
export class LNode { // 声明节点类
constructor(data) {
this.data = data
this.next = null
}
}
export class LinkedList {
constructor() {
this.head = null
this.length = 0
}
// 向列表尾部添加一个元素
append(data) {
const lnode = new LNode(data)
// 判断是不是第一个节点
if (this.length == 0) {
this.head = lnode
} else {
const currentNode = this.head
// 找到最后一个节点,然后进行赋值】
while (currentNode.next) {
currentNode = currentNode.next
}
currentNode.next == lnode
}
this.length++
}
// 实现向一个位置插入一个元素
insert(position, data) {
// 对position进行越界判断,当传入进来的值为负数,或者越界时,返回false
if (position < 0 || position > this.length) return false
const newNode = new LNode(data)
// 判断position是不是为0
if (position == 0) {
newNode.next = this.head
this.head = newNode
} else {
let index = 0
let current = this.head
var pre = null
// 找到插入的位置以及位置的前一个
while (index < position) {
pre = current
current = current.next
index++
}
newNode.next = current
pre.next = newNode
}
this.length++
return true
}
// 获得某一个位置的数据
get(position) {
if (position < 0 || position >= this.length) return null
let current = this.head
let index = 0
while (index < position) {
current = current.next()
index++
}
return current.data
}
// 根据某个位置返回数据元素
indexOf(element) {
let index = 0
let current = this.head
while (current) {
if(current.data == element){
return index
}
current = current.next
index++
}
return -1
}
// 修改某个位置的元素
update(position,element){
if (position < 0 || position >= this.length) return null
let current = this.head
let index = 0
while (index < position) {
current = current.next()
index++
}
current.data = element
return true
}
// 从链表的特定位置移除一项
removeAt(position){
if (position < 0 || position >= this.length) return null
if(position == 0){
this.head = current.next
}else{
let index =0
let pre = null
let current = this.head
while(index < position){
pre = current
current = current.next
index++
}
current = pre.next
pre.next = current.next
}
this.length--
return true
}
// 从链表中移除一项
remove(element){
return this.removeAt(this.indexOf(element))
}
// isEmpty() 判断链表是否为空
isEmpty() {
return this.length === 0;
}
// size() 获取链表的长度
size() {
return this.length;
}
// 实现toString方法
toString() {
const current = this.head
const res = []
while (current.next) {
res.push(current.data)
current = current.next
}
return res.join(' ')
}
}