1、何为链表
顾名思义,所谓链表
- 首先它得是个表,用来存储数据
- 其次它是用链条把这些数据链接起来
所以链表就是存储着有序的元素集合,链表中的每一个元素由元素本身的节点 和 指向下一个元素的指针 组成
它的第一个节点我们称之为头节点,最后一个节点为尾节点,并且尾节点的next 指针指向null(为空)
2、链表 VS 数组
数组我们是再熟悉不过了,顺序存储,按照索引来访问
所以,如果说数组在内存中是顺序存储的话,那么链表在内存中就是随机存储,然后通过链条来把这些零散的节点连接起来
所以在存储上:
- 数组在内存中是占用了连续完整的存储空间
- 链表则是零散分,充分利用碎片空间
基于它们的结构特点,数组的优势在于:能够快速定位,访问比较迅速;而链表每次都需要从头部节点开始查找
而链表的优势是能够灵活地进行插入和删除操作,每次只需要找到要插入的节点位置,修改 next 指针的指向即可;而数组进行删除或者插入操作的时候,其后面的元素都需要变动。
如:一排座椅坐着一些人,每个人就是紧挨着,这时他们的boss来了,要坐C位,那么C位后面的人都得往后挪一个位置,那太也麻烦了。设想一下,如果是一个庞大的数据,需要频繁进行增删改查的操作,用数组那效率得有多低的,估计修改个数据都得半天
所以,如果需要频繁进行插入和删除元素的,用链表更合适些;对于读取多的,操作少的场景来说,那数组更合适些
3、创建链表
链表分给单向链表、双向链表、循环链表 和 环形链表
上面所示的就会是单向链表
双向链表
循环链表
今天我们就先来讨论一下单向链表,我们用类来实现一下链表,如果对类不熟悉的话,可以看下 面向对象 这篇文章,了解个一二,理解下面就某得问题了
首先我们先创建一个节点对象,当我们需要创建节点的时候,就实例一个节点对象
class Node {
constructor(element) {
this.element = element;
this.next = undefined;
}
}
链表的几个主要的功能(增删改查):
- 向链表尾部添加节点
- 删除节点
- 在链表任意位置添加节点
- 修改链表节点
- 遍历打印链表的节点值
其结构如下:
// 创建一个链表,先定义其表头、节点个数
class LinkList {
constructor() {
this.count = 0; // 节点个数
this.head = null; // 表头
}
// 根据位置查找节点
getElement(index) {}
// 向链表尾部添加元素
pull(element) {}
// 根据位置从链表中移除元素
removeAt(index) {}
// 在任意位置插入元素 元素+位置
insert(element, index) {}
// 遍历显示当链表
display() {}
// 返回一个元素的位置
indexof(element) {}
// 根据节点从链表中移除元素
remove(element) {}
// 将对象转换为字符串
toString() {}
}
根据位置查找节点,将它疯转成一个方法,后面会经常调用
getElement(index) {
if (index >= 0 && index < this.count) {
let curret = this.head;
for (let i = 0; i < index && curret != null; i++) {
curret= curret.next
}
return curret
}
return undefined
}
向链表尾部添加元素
pull(element) {
// 创建节点
let node = new Node(element);
let current;
// 如果链表为空,直接令添加的节点为头节点
if (this.head === null) {
this.head = node;
} else {
// 查找链表的最后一个元素
current = this.getElement(this.count - 1)
current.next = node
}
this.count++;
}
从链表中删除一个元素
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head
// 如果删除的是头部节点
if (index === 0) {
// this代表链表,其头部this.head等于下一个值
this.head = current.next
} else {
// 获取要删除元素的前一个元素
let previous = this.getElement(index - 1);
// 删除的元素
current = this.getElement(index)
// 删除元素
previous.next = current.next
};
// 删除一个元素记得要将节点个数-1
this.count--;
return current.element;
}
return undefined
}
在任意位置插入元素
insert(element, index) {
let node = new Node(element);
if (index >= 0 && index <= this.count) {
let current = this.head
// 如果是插入在第一个
if (index === 0) {
node.next = current
this.head = node
} else {
// 获取插入的前一个节点
let previous = this.getElement(index - 1);
// 让插入节点指向下一个节点
node.next = previous.next
// 前一个节点指向下一个节点
previous.next = node
}
this.count++;
return true
}
return undefined
}
修改链表节点
modify(index, newElemnet) {
if (index >= 0 && index < this.count) {
// 如果修改的是第一个值
if (index === 0) {
this.head.element = newElemnet
} else {
let node = this.getElement(index);
node.element = newElemnet
}
return newElemnet
}
return undefined
}
遍历显示链表
// 方法一 for循环
display() {
if (this.head === null) {
return undefined
}
var result = ''
for (let i = 0; i < this.count; i++) {
let current = this.getElement(i)
result += current.element
if (i < this.count - 1) {
result += '->'
}
}
return result
}
// 方法二 while循环
display() {
if (this.head === null) {
return undefined
}
var result = ''
let current = this.head
while (current) {
result += current.element;
current = current.next;
if (current) {
result += '->'
}
}
return result
}
根据节点从链表中删除元素
// 返回一个元素的位置
indexOf(element) {
for (let i = 0; i < this.count; i++) {
let current = this.getElement(i);
if (element === current.element) {
return i
}
}
return -1
}
// 从链表中删除元素
remove(element) {
// 获取该元素的节点
let index = this.indexOf(element)
// 调用根据节点位置删除节点的函数
return this.removeAt(index)
}
将对象转换为字符串
toString() {
if (this.head === null) {
return ''
}
let objString = this.head.element;
let current = this.head.next;
for (let i = 1; i < this.count; i++) {
// 拼接节点的值
objString += ',' + current.element
current = current.next
}
return objString
}
单链表的功能基本实现了,整个一下
// 创建节点
class Node {
constructor(element) {
this.element = element;
this.next = undefined;
}
}
// 创建一个链表
class LinkList {
constructor() {
this.count = 0; // 链表个数
this.head = null; // 表头
}
getElement(index) {
if (index >= 0 && index < this.count) {
let node = this.head;
for (let i = 0; i < index && node != null; i++) {
node = node.next
}
return node
}
return undefined
}
// 向链表尾部添加元素
pull(element) {
// 创建节点
let node = new Node(element);
let current;
if (this.head === null) {
this.head = node;
} else {
// 查找链表的最后一个元素
current = this.getElement(this.count - 1)
current.next = node
}
this.count++;
}
// 从链表中删除一个元素
removeAt(index) {
if (index >= 0 && index < this.count) {
let current = this.head
if (index === 0) {
// this代表链表,其头部this.head等于下一个值
this.head = current.next
} else {
// 获取要删除元素的前一个元素
let previous = this.getElement(index - 1);
// 删除的元素
current = this.getElement(index)
// 删除元素
previous.next = current.next
};
this.count--;
return current.element;
}
return undefined
}
// 在任意位置插入元素
insert(element, index) {
let node = new Node(element);
if (index >= 0 && index <= this.count) {
let current = this.head
if (index === 0) {
node.next = current
this.head = node
} else {
let previous = this.getElement(index - 1);
node.next = previous.next
previous.next = node
}
this.count++;
return true
}
return undefined
}
// 修改链表节点
modify(index, newElemnet) {
if (index >= 0 && index < this.count) {
if (index === 0) {
this.head.element = newElemnet
} else {
let node = this.getElement(index);
node.element = newElemnet
}
return newElemnet
}
return undefined
}
// 遍历显示链表
display() {
if (this.head === null) {
return undefined
}
var result = ''
let current = this.head
while (current) {
result += current.element;
current = current.next;
if (current) {
result += '->'
}
}
return result
}
// 返回一个元素的位置
indexOf(element) {
for (let i = 0; i < this.count; i++) {
let current = this.getElement(i);
if (element === current.element) {
return i
}
// current = current.next
}
return -1
}
// 从链表中删除元素
remove(element) {
let index = this.indexOf(element)
return this.removeAt(index)
}
// 判断链表是否为空
isEmpty() {
return this.head === null
}
// 返回链表长度
size() {
return this.count;
}
// 返回尾部节点
getLast() {
return this.getElement(this.count - 1)
}
// 清空链表
clear() {
this.head = null
this.count = 0
return true
}
// 将对象转换为字符串
toString() {
if (this.head === null) {
return ''
}
let objString = this.head.element;
let current = this.head.next;
for (let i = 1; i < this.count; i++) {
objString += ',' + current.element
current = current.next
}
return objString
}
}
// 实例化链表对象
let l = new LinkList();
// 尾部添加节点
l.pull(1)
l.pull(2)
l.pull(3)
l.pull(4)
// 插入节点
l.insert(5, 0)
l.insert(6, 5)
// 打印整个链表(以目录的形式)
console.dir(l, {
depth: 100
});
// 删除位置为0的节点 返回该值
console.log(l.removeAt(0)); // 5
// 删除值为1的节点 返回该值
console.log(l.remove(1)); // 1
// 遍历该节点
console.log(l.display()); // 2->3->4->6
//最后一个节点
console.log(l.getLast()); // Node { element: 6, next: undefined }
// 修改节点
console.log(l.modify(2, 100)); // 100
console.log(l.display()); // 2->3->100->6
// 转化为字符串
console.log(l.toString()); // 2,3,100,6
拿走不客气哈!