认识双向链表,双向链表和单链表的区别
单链表
- 只能从头遍历到尾或者从尾遍历到头
- 链表的之间关联是单向的,原理是上一个链表中有一个指向下一个的指针
双链表
- 既可以从头遍历到尾,也可以从尾遍历到头
- 一个节点既有向前的引用,也有一个向后的引用
缺点
- 双向链表再插入和删除某个节点的节点的时候,需处理4个节点的引用,相对比单链表麻烦一些。
封装双向链表![在这里插入图片描述](https://img-blog.csdnimg.cn/5dc32309225d4578955842450a1f88b1.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5bCP6ZOD6ZOb55qE5omT5oCq5LmL6Lev,size_20,color_FFFFFF,t_70,g_se,x_16)
双向链表的结构如上图
1. 创建一个Node类
用来生成节点,表示节点信息。
对比单链表新增了一个前驱指针
//创建一个Node复制类,用来生成节点,表示节点信息
function Node(element) {
this.element = element;
this.next = null; //后继指针
this.prev = null; //前驱指针
}
2. 封装一个双向链表
//封装双链表
function DoubleLink(element) {
this.length = 0;
this.head = null; //初始头结点
this.tail = null; //新增尾结点
//操作的方法(不全)
this.append = append;
}
3. 向尾部追加元素
实现思想:
- 首先根据元素创建节点
- 判断链表是否是空链表:
如果链表是空链表,将头部head
替换为新节点node
,新节点node
的后继指针next
指向尾部tail
链表如果不是空链表,原链表中的尾部节点tail
的后继指针next
指向null,现在将它指向新的节点node
;因为新节点的后继指针next
和前驱指针pre
都是null,所以将新节点node
的前驱指针prev
指向原尾部节点tail
,原尾部节点tail
替换成新节点node
- 最后将长度+1
//尾部追加项
function append(element) {
let node = new Node(element);
if (this.head == null) {
this.head = node;
node.next = tail;
} else {
this.fail.next = node;
node.prev = this.tail;
this.tail = node;
}
this.lenght++;
}
4. 实现转换字符串方法
1. 正向遍历
//1.正向遍历
function forwardString() {
let current = this.head;
let forwardString = "";
while (current) {
forwardString += "," + current.element;
current = current.next;
}
return forwardString.slice(1);
}
2. 反向遍历
//2.反向遍历
function reserveString() {
let current = this.tail;
let reserveString = "";
while (current) {
reserveString += "," + current.element;
current = current.pre;
}
return reserveString.slice(1);
}
3. 实现转换字符串方法
function toString() {
return this.forwardString();
}
5. 向任意位置插入数据
实现方法:
一、首先判断position是否越界
二、元素插入链表第一个位置(position
== 0)又分为两种情况:
- 链表为空,直接让head/tail指向新节点
node
- 链表不为空,原来head的前驱指针
prev
指向新的节点node
,新节点的后继指针next
指向原来head
,原head
替换为新节点node
三、元素插入最后一个位置:将原
tail
的后继指针next
指向新节点node
,新节点node
的前驱指针prev
指向原tail
节点,原tail
替换为新节点node
四、元素插入中间位置,循环找到需要插入的位置,保存插入位置当前的节点和下一个节点信息,开始进行替换
- 新节点node的next/prev要指向前后的节点,也就是previous和current
current
的前驱指针prev
要指向node
,previous
的后继指针next
要指向node
五、最后将链表长度+1
function insert(position, element) {
//1.判断越界问题
if (position < 0 || position > this.lenght) return false;
//2.创建新节点
let node = new Node(element);
//3.判断插入位置
if (position === 0) {
//判断链表是否为空
if (this.head == null) {
this.head = node;
this.tail = node;
} else {
//链表不为空
this.head.prev = node;
node.next = this.head;
this.head = node;
}
} else if (position === this.length) {
//插入位置是最后
this.tail.next = node;
node.prev = this.tail;
this.tail = node;
} else {
//在中间位子插入
let index = 0;
let current = this.head;
let previous = null;
//查找正确的位置
while (index < position) {
previous = current; //保存当前节点
current = current.next; //保存下一个节点
index++;
}
node.next = current;
node.prev = previous;
current.prev = node;
previous.next = node;
}
this.length++;
return true;
}
6. 移除元素
实现思想:
一、根据位置移除元素判断position是否越界
二、判断移出的位置:
- 移除位置是链表的第一个:
- 链表只有一个元素:直接将初始头节点head/尾节点tail设置为null
- 链表有多个元素:将原链表的第二个元素head.next替换为head,现在head的前驱指针prev指向null
二、移除的位置是最后一个:将尾部节点tail替换成原链表的倒数第二个,再将新尾节点的后继指针next指向null
三、移除的位置是中间元素:循环找到中间想要移除的值,保存要删除项的前一个节点和后一个节点,将前一个节点的后继指针指向后一个节点,后一个节点的前驱指针指向前一个节点
四、最后将长度length-1 五、返回删除的元素
function removeAt(position) {
//1.判断position是否越界
if (position === 0 || position >= this.length) return null;
let current = this.head;
//2.判断移除的位置
//移除的位置是链表第一个元素
if (position === 0) {
//链表只有一个元素
if (this.length === 1) {
this.head = null;
this.tail = null;
} else {
current = this.tail;
this.head = this.head.next;
this.head.prev = null;
}
} else if (position === this.length) {
this.tail = this.tail.prev;
this.tail.next = null;
}else{
let index = 0;
let previous = null;
while(index < position){
previous = current;
current = current.next;
index++;
}
previous.next = current.next;
current.next.prev = previous;
}
this.length--
return current.element
}
7. 获取指定元素位置
1.定义变量保存信息
2.通过循环查找和element相等的项
3.如果没有查找到,返回-1
function indexOf(element){
let current = this.head;
let index = 0;
while(current){
if(current.element == current){
return index
}
index++;
current = current.next
}
return -1;
}
8. 根据元素删除
function removeAt(element){
let index = this.indexOf(element);
return this.removeAt(index);
}
以上就是双链表的学习,如有错误,请留言,感谢~