⚡️前言⚡️
本笔记针对无头双向链表的实现来展开,在阅读该笔记时,建议读者结合博主的单链表笔记一起阅读,两者多有相似之处,结合阅读便于理解总结
博客主页: 如风暖阳
欢迎点赞 收藏 ⭐留言评论 私信必回哟
本文由 【如风暖阳】 原创,首发于 CSDN
博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
博客中涉及源码及博主日常练习代码均已上传码云(gitee)
双向链表结构其实与单向链表结构非常相似,只是比单向链表多了prev域用于存储前一个节点的地址,从而实现链表的双向性,见下图
class Node {
public int data;//一个节点存在三个区域
public Node prev;
public Node next;
public Node(int data) {//构造方法用于初始化实例对象
this.data = data;
}
}
public class MyLinkedList {
public Node head;
public Node tail;
public void addFirst(int data);
//1.头插法
public void addLast(int data);
//2.尾插法
public void display();
//3.打印链表
public boolean contains(int key);
//4.查找是否包含关键字key是否在单链表当中
public int size();
//5.求链表长度
public void addIndex(int index,int data);
//6.任意位置插入,第一个数据节点为0号下标
public void remove(int key);
//7.删除第一次出现关键字为key的节点
public void removeAllKey(int key);
//8.删除所有值为key的节点
public void clear();
//9.清空链表
}
以下即为双向链表的各接口的实现
public void addFirst(int data) {
Node node=new Node(data);//将data的值new为一个节点
if (this.head == null) {//第一次插入
this.head=node;//头尾节点都指向该节点
this.tail=node;
}else {
node.next=this.head;//完成连接
this.head.prev=node;//改变域值
this.head=node;//头节点前移
}
}
public void addLast(int data) {
Node node=new Node(data);
if(this.head==null) {
this.head=node;
this.tail=node;
}else {
this.tail.next=node;//续尾
node.prev=this.tail;//变值
this.tail=node;//移尾
}
}
遍历链表完成打印
public void display() {
Node cur=this.head;
while(cur != null) {
System.out.print(cur.data+" ");
cur=cur.next;
}
System.out.println();
}
遍历链表,查找是否包含关键字
public boolean contains(int key) {
Node cur=this.head;
while(cur!=null) {
if(cur.data==key)
return true;
cur=cur.next;
}
return false;
}
遍历求和
public int size() {
int a=0;
Node cur=this.head;
while(cur!=null) {
a++;
cur=cur.next;
}
return a;
}
完成这项任务需要分步进行
1.判断index的合法性
2.是否为头插
3.是否为尾插
4.中间插入
在单链表删除关键字时,还需要设置前后节点完成连接,因为双向链表中存在上一个节点的地址,所以只需要设置一个节点遍历即可完成任务
private void checkIndex(int index) {//判断index位置合法性
if(index<0||index>this.size()) {
throw new RuntimeException("index位置不合法");
}
}
private Node searchIndex(int index) {//查找插入的位置
Node cur=this.head;
int a=0;
while(a!=index) {
a++;
cur=cur.next;
}
return cur;
}
public void addIndex(int index,int data) {
checkIndex(index);
if(index==0) {//头插
this.addFirst(data);
return;
}
if(index==this.size()) {//尾插
this.addLast(data);
return;
}
Node node=new Node(data);//实例化node节点
Node cur=searchIndex(index);//cur存储index位置节点
node.next=cur;//左边四步完成连接过程,先对node中的值改变对原链表无影响
node.prev=cur.prev;
cur.prev.next=node;//然后连接前后
cur.prev=node;
}
public void remove(int key) {
Node cur=this.head;
while(cur!=null) {
if(cur.data==key) {//如果找到关键字key
if(cur==this.head) {//头节点的data为key
this.head=cur.next;//头节点后移完成头节点删除
if(this.head!=null)//防止空指针异常
this.head.prev=null;
}else {//中间找到key
cur.prev.next=cur.next;
if(cur.next!=null)
cur.next.prev=cur.prev;
else//如果cur.next==null,尾节点即为所需删除节点
this.tail=cur.prev;
}
break;//完成删除后跳出循环
}
cur=cur.next;//如果没有进if语句中,cur继续往后遍历
}
}
与7代码相同,只是无需跳出循环,让cur完成遍历,把关键字全部删除即可
public void remove(int key) {
Node cur=this.head;
while(cur!=null) {
if(cur.data==key) {//如果找到关键字key
if(cur==this.head) {//头节点的data为key
this.head=cur.next;//头节点后移完成头节点删除
if(this.head!=null)//防止空指针异常
this.head.prev=null;
}else {//中间找到key
cur.prev.next=cur.next;
if(cur.next!=null)
cur.next.prev=cur.prev;
else//如果cur.next==null,尾节点即为所需删除节点
this.tail=cur.prev;
}
}
cur=cur.next;//如果没有进if语句中,cur继续往后遍历
}
}
在单链表清空链表时,直接将this.head置为空即可(当this.head置为空时,没有对象引用头节点,则其内存被JVM自动收回,后续节点也被收会),而当双向链表清空时,只把this.head置为空时,因为为双向链表,所以第二个节点的prev仍然指向头节点,所以无法完成清空,则需遍历链表完成置空.
public void clear() {//完成遍历,所有都置为空,则内存被收回
while (this.head!=null) {
Node cur=this.head.next;
this.head.prev=null;
this.head.next=null;
this.head=cur;
}
this.tail=null;
}
⚡️最后的话⚡️
总结不易,希望uu们不要吝啬你们的哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正