一认识链表:
要存储多个元素, 另外一个选择就是使用链表
链表的每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(有些语言称为指针或者链接)组成.
相对于数组, 链表有一些优点:
- 内存空间不是比是连续的. 可以充分利用计算机的内存. 实现灵活的内存动态管理.
- 链表不必在创建时就确定大小, 并且大小可以无限的延伸下去.
- 链表在插入和删除数据时, 时间复杂度可以达到O(1). 相对数组效率高很多
相对于数组, 链表有一些缺点:
- 链表访问任何一个位置的元素时, 都需要从头开始访问.(无法跳过第一个元素访问任何一个元素).
- 无法通过下标直接访问元素, 需要从头一个个访问, 直到找到对应的问题.
什么是链表?
-
链表类似于火车: 有一个火车头, 火车头会连接一个节点, 节点上有乘客, 并且这个节点会连接下一个节点, 以此类推.
二. 链表封装
创建链表类:
function Linked() {
// 内部的类:节点类
// 形参设默认值 next = null
function Node(data) {// 封装一个Node类, 用于保存每个节点信息
this.data = data
this.next = null
}
//属性
this.head = null// 链表的第一个节点
this.length = 0// 链表的长度
}
链表常见操作
- 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方法,让其只输出元素的值;
三,链表操作
append方法实现
//追加方法
Linked.prototype.append = function (data) {
//1.创建新的节点
let newNode = new Node(data)
//判断是否添加的是第一个节点
if (this.length === 0) {//2.1是第一个节点
this.head = newNode
}
else {//2.2不是第一个节点
// 找到最后一个节点
let current = this.head
while (current.next) {
current = current.next
}
// 最后节点的next真向新的节点
current.next = newNode
}
//3.length+1
this.length += 1
}
toString方法实现
Linked.prototype.toString = function () {
// 1.定义变量拿到第一个节点
let current = this.head
let nextString = ''
// // 2.循环获取链表中所有的元素
while (current) {
nextString += current.data + ' '
current = current.next
}
// 返回最终结果
return nextString
}
insert方法实现
Linked.prototype.insert = function (seat, data) {
//1.对seat参数进行越界判断
if (seat < 0 || seat > this.length) return false
//2.根据data创建newdata
let newNode = new Node(data)
//3.判断插入的位置是否是第一个
if (seat === 0) {
// 让新节点指向第一个节点
newNode.next = this.head
// 让head指向新节点
this.head = newNode
}
else {
let index = 0;
// 获取4个元素
let current = this.head
let previous = null
//步骤1:通过while循环使变量current指向position位置的后一个节点(注意while循环的写法)
while (index++ < seat) {
//步骤2:在current指向下一个节点之前,让previous指向current当前指向的节点
previous = current
current = current.next
}
newNode.next = current
previous.next = newNode
}
//4.length++1
this.length + 1
return true
}
get方法实现
//4.get方法
Linked.prototype.get = function (seat) {
//查看是否越界
if (seat < 0 || seat >= this.length) return null
//2.获取对应的data
let current = this.head
let index = 0;
while (index++ < seat) {
current = current.next
}
return current.data
}
indexOf方法实现
Linked.prototype.indexOf = function (data) {
//1.定义变量
let current = this.head
let index = 0
//2.开始查找
while (current) {
if (current.data == data) {
return index
}
current = current.next
index += 1
}
//3.找到最后没有找到,返回-1
return -1
}
update方法实现
Linked.prototype.update = function (seat, newdata) {
//越界判断
if (seat < 0 || seat >= this.length) return false
//2.查找正确的节点
//获取第一个
let current = this.head
let index = 0
while (index++ < seat) {
current = current.next
}
//3.将seat位置的后一个节点的data修改成newData
current.data = newdata
return true
}
removeAt方法实现
Linked.prototype.removeAt = function (seat) {
//1.越界判断
if (seat < 0 || seat >= this.length) return false
//2.判断是否删除的是第一个节点
if (seat == 0) {
this.head = this.head.next
}
else {
let index = 0;
let current = this.head
let previous = null
while (index++ < seat) {
previous = current
current = current.next
}
//前一个结点的nexr指向,current的next即可
seat.next = current.next;
}
//length-1
this.length -= 1
return true
}
remove方法实现
Linked.prototype.remove = function (data) {
//1.根据data在列表中的位置
let seat = this.indexOf(data)
//2.根据位置信息 删除节点
return this.removeAt(seat)
}
isEmpty方法实现
Linked.prototype.isEmpty = function () {
return this.length === 0
}
size方法实现
Linked.prototype.size = function () {
return this.length;
}