上一篇文章已经讲过什么是数据结构以及数据结构的栈,队列等等,这篇文章就直接说单链表,双链表了,就不在赘述了
链表和数组一样, 可以用于存储一系列的元素, 但是链表和数组的实现机制完全不同
链表相比较于数组的优点
但是链表也有一些缺点
链表的常用方法有以下这些
append(element)
:向列表尾部添加一个新的项insert(position, element)
:向列表的特定位置插入一个新的项remove(element)
:从列表中移除一项indexOf(element)
:返回元素在列表中的索引。如果列表中没有该元素则返回-1
removeAt(position)
:从列表的特定位置移除一项isEmpty()
:如果链表中不包含任何元素,返回true
,如果链表长度大于0则返回false
size()
:返回链表包含的元素个数。与数组的length
属性类似toString()
:由于列表项使用了Node
类,就需要重写继承自JavaScript对象默认的toString
方法,让其只输出元素的值接下来我们就来封装一下链表常用的方法或函数吧
<script>
class Lnode {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkList {
constructor() {
this.head = null;
this.len = 0;
}
append(ele) {
// 创建一个新的节点
let newnode = new Lnode(ele);
if (this.head == null) {
this.head = newnode;
} else {
let current = this.head;
while(current.next != null) {
current = current.next;
}
current.next = newnode;
}
this.len++
}
}
let list = new LinkList();[''[p'[
console.log(list);
list.append(0);
list.append(1);
list.append(7);
console.log(list);
</script>
这就已经封装好了,需要向链表末尾添加元素的时候,直接调用就好了
insert(指定为位置,插入的元素)
Lnode LinkList 我就不在写了,都是一样的,下面的封装我会直接用
insert(position, ele) {
if (position < 0 || position > this.len || !Number.isInteger(position)) {
return false;
}
let newnode = new Lnode(ele);
// 在头部位置插入
if (position == 0) {
// 如果链表没有,head=null,那就直接创建节点
if (this.head == null) {
this.head == newnode;
} else {
// 如果head有,那么让head等于head的下一个节点
newnode.next = this.head;
// 让创建的节点等于head就可以了
this.head = newnode;
}
this.len++;
// 尾部插入
} else if (position == this.len) {
// 直接在尾部加一个节点上去就好
this.append(ele);
} else {
// 任意位置插入
// 让插入元素等于头结点
let current = this.head;
// 下标设为0
let index = 0;
// 前一个节点,position-1的位置节点
while (index < position - 1) {
// 插入元素的下一个节点等于插入元素
current = current.next;
// 下标加一
index++
}
// current就是前一个节点
// 让插入元素的下一个节点等于现在元素的下一个节点
newnode.next = current.next;
// 现在元素等于插入元素的下一个节点
current.next = newnode;
// 插入过后长度加一
this.len++
}
let list = new LinkList();
console.log(list);
list.append(0);
list.append(1);
list.append(7);
console.log(list);
list.insert(1,100)
list.insert(2,200)
list.insert(4,300)
list.insert(0,"new")
console.log(list); }
removeAt(position) {
// 先判断位置是否合法,不合法就直接返回false
if (position < 0 || position > this.len || !Number.isInteger(position)) {
return false;
}
// 判断链接里是否有值,没有就不需要移除,直接返回
if (this.head == null) {
return
} else {
if (position == 0) {
//头移除,直接让head的下一个节点等于head就移除了
this.head = this.head.next
} else {
// 其他位置移除
// 让移除位置的等于头节点
let current = this.head,
// 设置下标为0
index = 0;
// 如果下标小于位置减一的话
while (index < position - 1) {
// 就让移除元素的下一个元素等于移除元素
current = current.next;
// 并且下标加一
index++
}
// 让移除元素的下下一个等于下一个即可
current.next = current.next.next;
}
// 移除过后长度减一
this.len--;
}
}
let list = new LinkList();
console.log(list);
list.append(0);
list.append(1);
list.append(7);
console.log(list);
list.insert(1,100)
list.insert(2,200)
list.insert(4,300)
list.insert(0.1,"new")
console.log(list);
list.removeAt(0)
list.removeAt(2)
console.log(list);
下标只是为了方便理解,链表是没有下标的哦
indexOf(ele) {
let current = this.head,
index = 0;
while (index < this.len) {
if (current.data == ele) {
return index
} else {
// 继续找
current = current.next;
index++;
}
}
// 遍历完都没找到就返回-1
return -1;
}
console.log(list.indexOf("new"));
console.log(list.indexOf(100));
console.log(list.indexOf(300));
toString() {
let current = this.head,
index = 0,
res = "";
while (index < this.len) {
res += "-" + current.data;
current = current.next;
index++;
}
return res.slice(1)
}
console.log(list.toString());
所以这个时候就有了双向链表
但是双向链表依旧有一些缺点
双向链接常用的方法和单向链表差不多,这里就不在赘述了,就直接封装双向链表的方法了
<script>
class Dnode {
constructor(data) {
this.prev = null;
this.data = data;
this.next = null;
}
}
class DoubleLinkList {
constructor() {
this.head = null;
this.tail = null;
this.len = 0
}
append(ele){
let newnode = new Dnode(ele);
if(this.len == 0){
this.head = newnode;
this.tail = newnode;
} else {
newnode.prev = this.tail;
this.tail.next = newnode;
this.tail = newnode
}
this.len++
}
}
let dlist = new DoubleLinkList();
dlist.append("new");
dlist.append(100);
dlist.append(200);
dlist.append(300);
console.log(dlist);
</script>
Dnode DoubleLinkList 我也不重复写啦,后面一样的直接用
insert(position, ele) {
if (position < 0 || position > this.len || !Number.isInteger(position)) {
return false;
}
let newnode = new Dnode(ele);
if (position == 0) {
if (this.len == 0) {
this.head = newnode;
this.tail = newnode;
} else {
newnode.next = this.head;
this.head.prev = newnode;
this.head = newnode
}
this.len++
} else if (position == this.len) {
this.append(ele);
} else {
let current = this.head,
index = 0;
while (index < position - 1) {
current = current.next
index++
}
newnode.prev = current;
newnode.next = current.next;
current.next = newnode;
current.next.prev = newnode;
this.len++
}
}
let dlist = new DoubleLinkList();
dlist.append("new");
dlist.append(100);
dlist.append(200);
dlist.append(300);
console.log(dlist);
dlist.insert(1,"hello")
dlist.insert(2,"word")
console.log(dlist);
removeAt(position) {
if (position < 0 || position > this.len - 1 || !Number.isInteger(position)) {
return false
}
if (this.len == 0) {
return
} else {
if (position == 0) {
if (this.len == 1) {
this.head = null;
this.tail = null;
} else {
this.head = this.head.next;
this.head.prev = null;
}
} else if (position == this.len - 1) {
this.tail = this.tail.prev;
this.tail.next = null;
} else {
let current = this.head,
index = 0;
while (index < position - 1) {
current = current.next;
index++
}
current.next = current.next.next;
current.next.prev = current
}
this.len--
}
}
dlist.removeAt(0)
console.log(dlist);
indexOf(ele) {
let current = this.head,
index = 0;
while (index < this.len) {
if (current.data == ele) {
return index
} else {
current = current.next;
index++;
}
}
return -1
}
console.log(dlist.indexOf("word"));
console.log(dlist);
// 正向遍历
toAfterString() {
let current = this.head,
index = 0,
res = "";
while (index < this.len) {
res += "-" + current.data;
current = current.next;
index++;
}
return res.slice(1)
}
// 反向遍历
toBeforeString() {
let current = this.tail,
index = this.len - 1,
res = "";
while (index >= 0) {
res += "-" + current.data;
current = current.prev;
index--;
}
return res.slice(1)
}
单向链表和双向链表的常用方法就已经封装完啦