双向链表
双向链表概念:
既可以从头遍历到尾,又可以从尾遍历到头。也就是说链表连接的过程是双向的,它的实现原理是:一个节点既有向前连接的引用,也有一个向后连接的引用。
双向链表的缺点:
- 每次在插入或删除某个节点时,都需要处理四个引用,而不是两个,实现起来会困难些;
- 相对于单向链表,所占内存空间更大一些;
- 但是,相对于双向链表的便利性而言,这些缺点微不足道。
双向链表的结构:
-
双向链表不仅有head指针指向第一个节点,而且有tail指针指向最后一个节点;
-
每一个节点由三部分组成:item储存数据、prev指向前一个节点、next指向后一个节点;
-
双向链表的第一个节点的prev指向null;
-
双向链表的最后一个节点的next指向null;
双向链表常见的操作(方法):
- append(element):向链表尾部添加一个新的项;
- inset(position,element):向链表的特定位置插入一个新的项;
- get(element):获取对应位置的元素;
- indexOf(element):返回元素在链表中的索引,如果链表中没有元素就返回-1;
- update(position,element):修改某个位置的元素;
- removeAt(position):从链表的特定位置移除一项;
- isEmpty():如果链表中不包含任何元素,返回trun,如果链表长度大于0则返回false;
- size():返回链表包含的元素个数,与数组的length属性类似;
- toString():由于链表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值;
- forwardString():返回正向遍历节点字符串形式;
- backwordString():返回反向遍历的节点的字符串形式;
// 双向链表节点
export class DoublyLnode{
constructor(data){
this.data = data
this.pre = null
this.next = null
}
}
// 封装双向链表
export class DoublyLinkedList{
constructor(){
this.head =null // 头指针
this.tail = null // 尾指针
this.length = 0
}
// 向尾部添加一项
append(element){
const newNode = new DoublyLnode(element)
if(this.length == 0){ //判断是不是要插入第一个
this.head = newNode
this.tail = newNode
}else{
newNode.pre = this.tail
this.tail.next = newNode
this.tail = newNode
}
}
// 将链表转为字符串的形式
toString(){
return this.backwardString()
}
forwardToString(){ // 从后往前遍历
const current = this.tail
const res =''
while(current){
res +=current.data +' '
current = current.pre
}
return res
}
backwardString(){ // 从前往后遍历
const current = this.head
const res =''
while(current){
res +=current.data +' '
current = current.next
}
return res
}
// 向链表的特定位置插入一个新的项
insert(position,element){
if(position <0 || position >=this.length) return null
const newNode = new DoublyLnode(element)
if(position == 0){
if( this.head === null){
this.head = newNode;
this.tail = newNode;
}else{
newNode.next = this.head
this.head.pre = newNode
this.head = newNode
}
}else if(position == this.length){
newNode.pre = this.tail
this.tail.next = newNode
this.tail = newNode
}else{
const index = 0
const current = this.head
while(index < position){
current = current.next
index++
}
newNode.next = current
current.pre.next = current
newNode.pre = current.pre
current.pre = newNode
}
this.length++
return ture
}
// 获取对应位置的元素
get(position){
if(position <0 || position >=this.length) return null
const index = 0
const current = this.head
while(index < position){
current = current.next
index++
}
return current.data
}
// 根据元素值,返回位置
indexOf(data){
const index = 0
const current = this.head
while(current){
if(current.data == data){
return index
}
current = current.next
index++
}
return -1
}
// 删除指定位置的节点
removeAt(position){
if(position <0 || position >=this.length) return null
let current = this.head;
if(position == 0){
if(this.length === 1){
this.head = null
this.tail = null
}else{
this.head = this.head.next
this.head.pre = null
}
}else if(position === this.length -1){
current = this.tail;
this.tail.pre.next = null
this.tail = this.tail.pre
return
}else{
let index =0
while(index < position){
current = current.next
index++
}
current.pre.next = current.next
current.next.pre = current.pre
return current.data
}
this.length--
}
// 修改指定位置的节点
update(position, data){
if(position <0 || position >=this.length) return null
const res = this.removeAt(position)
this.insert(position,data)
return res
}
// 删除指定 data 所在的节点
remove(data){
return this.removeAt(this.indexOf(data))
}
// 判断链表是否为空
isEmpty(){
if(this.length === 0){
return ture
}else{
return false
}
}
// 获取链表的长度
size(){
return this.length
}
}