大家好,今天给大家分享一下单链表;单链表,顾名思义,一个一个的表由一根链条连接在一起;说到单链表,我们肯定会想到数组,同时脑海中还会浮现一个问题---两个不都能实现增删改查嘛,好好的数组不用却非要用链表。就这一问题呢,我来说一下两者的区别吧,这个问题也是面试的高频问点,所以大家一定要记牢:
数组存在的缺点:
1.数组的创建需要申请一片连续的空间,也就是一整块内存,并且大小是固定的。所以当数组的容量不能满足需求时需要进行扩容处理
2.在数组的开口或中间位置插入数据时成本很高,需要进行大量元素的移动
链表的优点:
1.链表中的内存不必是连续的空间,可以充分利用计算机的内存实现灵活的内存动态管理
2.链表不必在创建时就确定大小,并且大小可以无限的延伸下去
3.链表在插入和删除数据时时间复杂度可以达到O(1),效率很高
链表的缺点:
1.链表访问任何一个位置的元素时,都需要从头开始访问
2.无法通过下标值直接访问元素,需要从头开始一个个访问
3.虽然可以轻松地到达下一个节点,但是回到前一个节点是很难的
对于初学者来说 ,不理解上面的总结很正常,可以先略过,让我先带你解释一下链表的原理并去实现它的功能之后你再回头看上面的总结,相信你一定会恍然大悟的,哈哈哈哈!!!!
前面说到链表,其实生活中也有许多链表的影子,比如火车,火车的整体由一个车头和许多车厢组成,和链表的结构特别像
下面来看一看链表的结构:
有没有觉得链表的结构和火车很像呢,有了这样的图,那我们就一步一步的来实现链表吧
首先,创建一个单链表的构造函数:
// 构造一个单链表函数
function LinkedList() {
this.head = null // 单链表的头结点
this.length = 0 // 单链表的长度
// 封装一个节点类来存放数据项和下一个数据位置的项
function Node(data) {
this.data = data
this.next = null
}
}
这样,一个简单的链表就封装好了;接下来就来一个一个实现链表的功能吧
1. insert插入数据
// 单链表的插入操作
LinkedList.prototype.insert = function (position, data) {
var newNode = new Node(data)
// 越界判断
if (position < 0 || position > this.length) return false
// 特殊情况:当插入到第0个位置时
if (position === 0) {
newNode.next = this.head
this.head = newNode
} else {
var index = 0
var pre = this.head
while (++index < position) {
pre = pre.next
}
newNode.next = pre.next
pre.next = newNode
}
this.length += 1
return true
}
流程图解:
2. append追加操作
// 单链表的追加操作
LinkedList.prototype.append = function (data) {
var newNode = new Node(data)
if (this.length === 0) {
this.head = newNode
} else {
var current = this.head
while (current.next) {
current = current.next
}
current.next = newNode
}
this.length += 1
}
流程图解:
3. get根据索引获取单链表中的某个元素
// 根据索引获取单链表中的某个元素
LinkedList.prototype.get = function (position) {
// 越界判断
if (position < 0 || position >= this.length) return null
var index = 0
var current = this.head
while (current) {
if (index === position) {
return current.data
}
current = current.next
index++
}
}
4. indexOf获取单链表中某个元素的索引
// 获取单链表中某个元素的索引
LinkedList.prototype.indexOf = function (data) {
var current = this.head
var index = 0
while (current) {
if (current.data === data) {
return index
}
index++
current = current.next
}
return -1
}
5. update修改单链表中某个元素的值
// 修改单链表中某个元素的值
LinkedList.prototype.update = function (position, element) {
if (position < 0 || position >= this.length) return false
var index = 0
var current = this.head
while (index++ < position) {
current = current.next
}
current.data = element
return true
}
6. removeAt根据索引删除单链表中某个元素
LinkedList.prototype.removeAt = function (position) {
// 越界判断
if (position < 0 || position >= this.length) return false
if (position === 0) {
this.head = this.head.next
} else {
var index = 0
var pre = this.head
while (++index < position) {
pre = pre.next
}
pre.next = pre.next.next
}
this.length -= 1
return true
}
7. remove删除单链表中某个元素
// 删除单链表中某个元素
LinkedList.prototype.remove = function (data) {
var index = this.indexOf(data) // 调用indexOf方法获取data对应的索引
return this.removeAt(index) //删除索引对应的元素
}
8. isEmpty判断单链表是否为空
// 判断单链表是否为空
LinkedList.prototype.isEmpty = function () {
return this.length === 0
}
9. toString 遍历单链表
// 遍历单链表
LinkedList.prototype.toString = function () {
var strResult = ''
var current = this.head
while (current) {
strResult += current.data + ' '
current = current.next
}
return strResult
}
测试代码:
let link = new LinkedList() // 创建链表实例
link.append('Mike')
link.append('Kobe')
link.append('John')
link.append('Abo')
link.append('Lisa')
link.insert(0, 'kangkang')
console.log(link.toString()); //kangkang Mike Kobe John Abo Lisa
console.log(link.removeAt(5)); //true
console.log(link.remove('Abo')); //true
console.log(link.toString()); //kangkang Mike Kobe John
console.log(link.isEmpty()); //false
console.log(link.get(0)); //kangkang
console.log(link.indexOf('Kobe')); //2
以上就是单链表的创建及各功能的实现过程。看完的小伙伴再回到顶部看一下数组和链表的区别吧,此时是不是已经恍然大悟了呢?
“了解真相,你才能获得真正的自由”,只有我们自己搞懂了原理我们才能自如地实现我们想要的功能。