
🌸雨落在了我的手上:个人主页
🐟个人仓库:Gitee仓库
❄️个人专栏:<<JaveSe>> <<C语言>> <<C语言数据结构>>**
🔥🔥🔥 人生格言:无人扶我青云志,我自踏雪至山巅
🎬 博主简介:

目录
内容大纲:
本文介绍了双链表的基本实现方法,重点讲解了双链表的增删改查操作。首先定义了双链表节点类DLikedNode和双链表类MyDLikedList,包含前驱指针prev和后继指针next。文章详细演示了10种核心操作:头插、尾插、指定位置插入、值查找、下标查找、头删、尾删、指定位置删除、值删除以及修改节点。其中特别解析了中间插入和删除的操作原理,通过修改前后节点的指针关系实现数据操作。所有方法都考虑了空链表、单节点等特殊情况,并实现了双向打印功能验证链表结构。双链表的实现与单链表类似,主要区别在于多维护了一个前驱指针。
一:双链表的前期准备
前面的文章我们都已经了解了线性表,而线性表又包括了顺序表和链表,然后链表又包括了单链表和双链表,前面我们都已经写过了,这里我们再写一个双链表,本质也是增删改查,和单链表几乎一模一样,只是多了一个prev罢了!!!
这里我们创建一个类DLikedNode,用来代表我们的节点
java
//代表节点
class DLikedNode{
public int val;
public DLikedNode prev;
public DLikedNode next;
public DLikedNode(int val) {
this.val = val;
this.prev = null;
this.next = null;
}
}
再创建一个类MyDLikedList,表示我们整个双链表,还有一个头节点,方便我们书写代
java
public class MyDLikedList {
//代表头节点
private DLikedNode head;
增
删
改
查
....
}
书写一个toString方法,来方便我们打印内容,这里我们还格外加了一个反向打印内容,方便我们验证双链表
java
//toString方法,并且可以方向打印结果
@Override
public String toString(){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
//这里单独定义一个tail尾指向,方便我们后续的反向打印
DLikedNode tail=null;
//进入循环,开始打印内容
for (DLikedNode cur=head;cur!=null;cur=cur.next){
//一开始让cur和tail指向开头,循环完之后,tail和cur就都指向最后一个了
tail=cur;
stringBuilder.append(cur.val);
if (cur.next!=null){
stringBuilder.append(",");
}
}
stringBuilder.append("] | [");
//此时cur和tail都指向尾部
//进入循环,开始反向打印
for (DLikedNode cur=tail;cur!=null;cur=cur.prev){
stringBuilder.append(cur.val);
if (cur.prev!=null){
stringBuilder.append(",");
}
}
stringBuilder.append("]");
return stringBuilder.toString();
}
获取有效元素的方法
java
//获取有效元素
public int size(){
int size=0;
for (DLikedNode cur=head;cur!=null;cur=cur.next){
size++;
}
return size;
}
二:双链表的实现
1.头插
java
public void addFirst(int val){
DLikedNode newNode = new DLikedNode(val);
//空情况
if (head==null){
head=newNode;
return;
}
//非空情况
newNode.next=head;
head.prev=newNode;
head=newNode;
}
2.尾插
java
//2.尾插
public void addLast(int val){
DLikedNode newNode=new DLikedNode(val);
//空情况
if (head==null){
head=newNode;
return;
}
//非空情况
DLikedNode cur=head;
while (cur.next!=null){
cur=cur.next;
}
//此时cur指向最后一个节点
cur.next=newNode;
newNode.prev=cur;
}
3.指定下标插入
java
//3.指定下标插入
public void add(int index,int val){
DLikedNode newNode = new DLikedNode(val);
//避免重复调用size
int size=size();
//异常情况,下标不正确
if (index<0||index>size){
throw new IndexOutOfBoundsException("下标越界");
}
//下标为0,即头插
if (index==0){
addFirst(val);
return;
}
//下标为size,即尾插
if (index==size){
addLast(val);
return;
}
//一般情况
//先找到要插入位置的前一个节点
DLikedNode cur =head;
for (int i =0;i<index-1;i++){
cur=cur.next;
}
//此时cur已经指向前一个节点
//我们再定义一个next
DLikedNode next = cur.next;
//核心代码
cur.next=newNode;
newNode.prev=cur;
newNode.next=next;
next.prev=newNode;
}
4.通过值查找
java
//4.值查找
public boolean contains(int value){
for (DLikedNode cur=head;cur!=null;cur=cur.next){
if (cur.val==value){
return true;
}
}
//此时没有找到
return false;
}
5.查找并且返回其对应下标
java
//5.查找,返回下标
public int indexOf(int value){
int index = 0;
for (DLikedNode cur =head;cur!=null;cur=cur.next){
if (cur.val==value){
return index;
}
index++;
}
//此时没有找到,返回-1
return -1;
}
6.头删
java
//6.头删
public void removeFirst(){
//空情况
if (head==null){
return;
}
//只有一个节点的情况
if (head.next==null){
head=null;
return;
}
//一般情况
head=head.next;
head.prev=null;
}
7.尾删
java
//7.尾删
public void removeLast(){
//空情况
if (head==null){
return;
}
//只有一个节点情况
if (head.next==null){
head=null;
return;
}
//一般情况
//要先找到要删除节点的前一节点
DLikedNode tail=head;
while (tail.next!=null){
tail=tail.next;
}
DLikedNode prev= tail.prev;
//此时已经指向前一节点
prev.next=null;
}
8.通过下标删除
java
//8.通过下标删除
public void remove(int index){
int size =size();
//异常情况
if (index<0||index>=size){
throw new IndexOutOfBoundsException("下标越界");
}
//下标为0情况,相当于头删
if (index==0){
removeFirst();
return;
}
//下标为size-1的情况,相当于尾删
if (index==size-1){
removeLast();
return;
}
//一般情况
//要先找到要删除节点的前一节点
DLikedNode prev =head;
for (int i=0 ;i<index-1;i++){
prev=prev.next;
}
//此时prev指向前一个节点
//定义两个变量,方便书写
DLikedNode toDelete = prev.next;
DLikedNode next = toDelete.next;
//核心代码
prev.next=next;
next.prev=prev;
}
9.指定值删除
java
//9.指定值删除
public void removeByValue(int value){
//为空情况
if (head==null){
return;
}
//一个节点的情况
if (head.val==value){
removeFirst();
return;
}
//一般情况
//先找到需要删除的节点
DLikedNode cur=head;
for (;cur!=null;cur=cur.next){
if (cur.val==value){
break;
}
}
//如果没有找到要删除的节点,则进入
if (cur==null){
System.out.println("链表不存在该元素:"+value);
return;
}
//如果上述没有进入,则此时要删除的值已经找到,这里定义两个变量
DLikedNode prev= cur.prev;
DLikedNode next =cur.next;
//核心代码
if (prev!=null){
prev.next=next;
}
if (next!=null){
next.prev=prev;
}
}
10.修改节点
java
//10.修改节点和获取节点元素
public void Set(int index, int value) {
// 空链表直接返回
if (head == null){
throw new IndexOutOfBoundsException("链表为空,无法修改");
}
//非空情况
//先获取下标位置,则通过遍历链表,找到我们要修改的下标位置
DLikedNode cur = head;
int i = 0;
while (i < index) {
cur = cur.next;
i++;
}
//如果没有找到可以修改的下标位置,则进入
if (cur == null) {
throw new IndexOutOfBoundsException("索引越界: " + index + ",链表长度为 " + i);
}
//修改节点
cur.val = value;
}
三:代码解析
这里我们就挑选几个来进行解析,本质都是差不多的
1.中间插入

以上这个图就是中间插入的图形画法
2.中间删除

其他的本质差不多,都是通过修改节点的指向到达我们需要的效果的
以上就是我们的全部内容了!!!!