数组VS 链表
- 在很多编程语言中,数组的初始化长度都是固定的,如果数组已被数据填满,再要加入新的元素是非常困难的。而且,对于数组的删除和添加操作,通常需要将数组中的其他元素向前或者向后平移,这些操作也是十分繁琐的。其特点:
- 数组是一种线性数据结构
- 数组中存储的是连续的内存空间和相同类型的数据
- 链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一节点的引用讲做链。下面我画了一个简单的链接结构图
- 使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
- 链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
- 链表的起始点的确定比较麻烦,因此很多链表的实现都会在链表的最前面添加一个特殊的节点,称为 头节点,表示链表的头部。
链表又包括 单向链表,双向链表,循环链表
单向链表
class Node {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = new Node('head');
}
display() {
let current = this.head;
const res = [];
while (!(current.next === null)) {
res.push(current.next.data);
current = current.next;
}
return res;
}
find(val) {
let current = this.head;
while (current.next !== null && current.data !== val) {
current = current.next;
}
return current ? current : -1;
}
insert(newVal, target) {
// 在目标节点后面插入值
const newNode = new Node(newVal);
const currentNode = this.find(target);
newNode.next = currentNode.next;
currentNode.next = newNode;
}
findPre(val) {
let current = this.head;
while (current.next !== null && current.next.data !== val) {
current = current.next;
}
return current ? current : -1;
}
remove(val) {
if (val === 'head') return false;
const prevNode = this.findPre(val);
if (prevNode === -1) return false;
if (!(prevNode.next === null)) {
prevNode.next = prevNode.next.next;
return true;
}
}
push(val) {
let current = this.head;
let newNode = new Node(val);
while (current.next !== null) {
current = current.next;
}
current.next = newNode;
}
unshift(val) {
let newNode = new Node(val);
this.insert(newNode, 'head');
}
}
双向链表
双链表以类似的方式工作,但还有一个引用字段,称为“prev”字段。有了这个额外的字段,您就能够知道当前结点的前一个结点。
class doubleLinkNode {
constructor(data) {
this.data = data;
this.next = null;
this.pre=null;
}
}
class DoubleLink {
constructor() {
this.head = new DoubleLinkNode('head');
}
find(val) {
let current = this.head;
while (current.next !== null && current.data !== val) {
current = current.next;
}
return current ? current : -1;
}
insert(newVal, target) {
// 在目标节点后面插入值
const newNode = new DoubleLinkNode(newVal);
const currentNode = this.find(target);
newNode.next = currentNode.next;
newNode.pre = currentNode;
currentNode.next = newNode;
}
remove(val) {
if (val === 'head') return false;
let currentNode = this.find(val);
if (currentNode === -1) return false;
currentNode.previous.next = currentNode.next;
currentNode.next = null;
currentNode.previous = null;
if (currentNode.next !== null) {
currentNode.next.pre = currentNode.pre;
}
return true;
}
display() {
let current = this.head;
const res = [];
while (!(current.next === null)) {
res.push(current.next.data);
current = current.next;
}
}
push(val) {
let current = this.head;
let newNode = new DoubleLinkNode(val);
while (current.next !== null) {
current = current.next;
}
current.next = newNode;
newNode.pre = current;
}
unshift(val) {
let newNode = new DoubleLinkNode(val);
this.insert(newNode, 'head');
}
}
循环链表
循环链表和单链表相似,节点类型都是一样,唯一的区别是,在创建循环链表的时候,让其头节点的 next 属性执行它本身,代码和单向链表类似
head.next = head;