特点:
1、链表的特点,在内存中不必是连续的空间
2、链表的每一个元素储存都是由元素本身的节点和一个指向下一个元素的引用组成;【其实在c中被称为指针,但js中并未有指针的这一概念,但其可以使用this来替代】
(图片来自网络)
JS中链表与数组的区别
链表查找任何一个元素时,都需要从头开始访问;而数组则可以通过下标值去进行快速查找
疑惑
既然在JS中数组的效率比链表快这么多,那么为什么我们还需要学习数据结构的链表呢?
答:我认为,只是我认为奥,我们只是使用JS的话,并不想更加深层次的去了解它的底层原理的话,这一章节似乎对于我们前端可有可无,直接掌握数组的各种API它不香吗?
但是如果我们想要去更加深层次的去了解我们这门语言得话,那数据结构就显的重要的多了,而且不论是哪一门语言,它的数据结构都是大差不差的,学习了JS关于数据结构的,对于你日后学习其他的数据结构就会容易许多,我相信,既然你是使用JS来写数据结构的话,对于除JS其他的语言您并未有过了解,或者了解的并不是很深刻;
并且,当你和其他同事或同学谈及数据结构的时候您不觉得很有范吗?至少,别人谈论的时候,你只能在一旁听着,挺难受的;
关于链表的基本结构
class LinkList {
head: any | { body: any, next: any };
body: any;
next: any;
length: number;
constructor() {
this.head = null;
this.length = 0;
}
// Node节点
Node(data: any): { body: any, next: any } {
this.body = data;
//body用于储存主数据
this.next = null;
next用于储存下一个节点
return { body: this.body, next: this.next }
}
//方法区
//添加方法
append(){}
}
//插入方法
insert(){
}
//查找节点:根据元素位置
getNode(position){
}
//查找节点
indexOf(data){
}
//更新元素
uadata(positiion,data){
}
//删除元素:根据下标值删除
removeAt(position){
}
//删除元素:根据元素
remove(data){
}
}
let list = new LinkList();
如上代码所看,我们定义了head,相当于一个链表的头部,而此时我们在这个链表中什么都没有添加,所以此时的head为空[null];
然后定义了一个length;主要用于计算链表的长度;
紧接着我们由定义了一个方法Node,主要是用于生成一个合格的节点,这个合格的节点中包含主体[body],和一个指针[next],用于指向下一个节点,然后我们在其中定义了许多的方法,而这些方法,我都会在下面进行一一的实现;
关于添加方法append()
append(data: any) {
//1、先使data变为一个合格的节点
let newNode = this.Node(data);
if (this.head === null) {
this.head = newNode
} else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
this.length += 1
}
1、这里,我们先使传入的data变成一个合格的Node节点
2、判断head是否为一个null,如果为null就将此时节点赋值给head,注意,此时Node的next是指向空的,准确的来定义,只要是最后一个节点,next必然指向空
3、但如果不为空:我们先定义一个current,它的最终作用,是指向最后的节点,但由于此时的我们不太清除,此时我们到底有几个节点,所以暂且将它指向head,然后用一个while循环来进行判断,因为节点里面有一个next元素,我们也说过,next是用于指向下一个节点的,而最后一个节点的next必然为null,所以我们只需要判断,current.next中的值是否为null我们就能判断出此时的节点是否为最后一个节点;当它是最后一个节点,我们就将最后一个节点的next指向newNode即可,如果不是,则表示此时的current.next必然指向下一个节点,我们只需要将下一个节点调出来即可;然后并使链表的长度加1;
关于插入insert()方法
insert(position, data) {
// 1、对position进行一个越界判断
// 位置不能小于零且不能大于链表的最大长度
if (position < 0 || position >this.length) {
return false;
}
let newNode = this.Node(data);
if (position === 0) {
newNode.next = this.head;
this.head = newNode;
} else {
let i = 0;
let current = this.head;//寻找到需要插入的节点
let previous = null//寻找到插入节点的上一个节点
while (i++ < position) {
previous = current
current = current.next
}
newNode.next = current;
previous.next = newNode;
}
this.length += 1;
return true;
}
1、要做好插入,首先要传入两个中重要的参数:位置与要插入的数值;
2、接着我们应该做好越界判断;这不仅仅是插入要做好越界判断,只要关于位置的我们都应该做好越界判断,主要是放止别人乱传;【接下来的越界判断我会一带而过】
3、创建节点
4、我们需要判断:位置是否为0:为零时则让新创建的节点的next指向原先的头元素,接着在将头节点变为刚刚的新节点;
5、此时我们在讨论位置不为0时:设置三个变量:i【用于判断当前的位置】current:用于保存当前的元素:pervious:用于保存当前元素的上一个元素;
然后用while循环,直到找到应该插入的位置:此时让pervious【指向当前节点的上一个节点】的next指向新建的节点;并让新建节点的next指向当前节点【current】;
关于根据位置查找节点getNode
getNode(position) {
// 越界判断;
if (position < 0 && position >= this.length) {
return null
}
let i = 0;
let current = this.head;
while (i++ < position) {
current = current.next;
}
return current.data;
}
代码逻辑简单,就不多做说明了,不过需看清越界判断的position限制;
关于根据元素查找indexof()
indexOf(data) {
let current = this.head;
let index = 0;
while (current) {
//判断current.data是否等于data,等于data的话返回index
if (current.data = data) {
return index
}
//当current.data不等于data的时候,使index+1
index += 1;
current = current.next;
}
//没有的话返回-1
return -1
}
关于修改方法 updata()
updata(position, data) {
// 越界判断
if (position < 0 && position >= this.length) {
return null
}
let i = 0;
let current = this.head;
while (i++ !== position) {
current = current.next;
}
current.data = data;
return this.head;
}
删除方法:根据下标值查找删除 removeAt()
removeAt(position) {
// 越界判断
if (position < 0 && position >= this.length) {
return null
}
let current = this.head;
let previous = null;
if (position === 0) {
this.head = current.next;
}
let i = 0;
while (i++ !== position) {
previous = current;
current = current.next;
}
previous.next = current.next;
//删除长度-1;
this.length -= 1;
return true;
}
关于删除元素:根据元素删除remove()
remove(data) {
// 先用indexof方法查找下标值
let index = this.indexOf(data)
// 然后根据下标值方法进行删除
this.removeAt(index)
}