四-中,Java实现双链表 0.8
文章目录
四-中, 双链表( Double Linked List)
单链表结点中只有一个指向其后继的指针,这使得单链表只能从头结点依次顺序地向后遍历。若要访问某个结点的前驱结点(插入、删除操作时),只能从头开始遍历,访问后继结点的时间复杂度为O(1),访问前驱结点的时间复杂度为O(n)。
- 为了克服单链表的上述缺点,引入了双链表,
双链表结点中有两个指针pre(或prior)和next,分别指向其前驱结点和后继结点
,如下图所示。
4.1 双链表的常用方法主要代码
双链表仅仅是在单链表的结点中增加了一个指向其前驱的pre指针,因此,在双链表中执行
按值查找和按位查找的操作和单链表相同
。但双链表在插入和删除操作的实现上,和单链表有着较大的不同
。这是因为“链”变化时也需要对pre(或prior)指针做出修改,其关键在于保证在修改的过程中不断链。此外,双链表可以很方便地找到其前驱结点,因此,插入、删除结点算法的时间复杂度仅为O(1)。
双链表解决了访问前驱结点的问题;
4.1.2 双链表的普通插入,顺序插入, 定点插入(插入到指定结点后)和遍历输出
因为双链表有两个指针域,所以我们在一定要同时考虑两个指针域的引用的赋值!
- 双链表的普通插入:
按照结点添加顺序. 每需要添加一个结点,就要从头结点遍历到链表的尾部,然后把新的结点连接到尾部即可;
- 主要实现代码:
///1. 双链表的普通插入(我们一定要照顾两条指针域!!!)
public void add(Node node) {
//临时变量temp
Node temp = head;
while (true) {
if (temp.next == null)
break;
temp = temp.next;
}
//决定性语句
temp.next = node;
node.pre = temp;
}
- 双链表的顺序插入和遍历输出
双链表的顺序插入是指链表按照某种规则顺序的组成链表,新结点的插入需要跟已经在链表中的结点一一比较,然后再插入到合适的位置上
这里跟单链表不同的是,我们需要特别注意,如果插入的位置在双链表的末尾,可能会出现空指针的情况,为了避免空指针,我们需要根据情况对可能会出现空指针的语句多加上一重判断!
顺序插入和打印链表的代码如下:
//结点类
static class Node{
Node next;
Node pre;
int num;
///构造器和toString方法
public Node(int num){
this.num = num;
}
public String toString(){
return "num= "+num;
}
}
/// 头结点
Node head =new Node(0);
///结点的顺序插入
/*
结点顺序插入的三种情况:
1. temp.next.num == node.num 如果不允许重复结点,则不插入链表中
2. temp.next.num > node.num 找到了! 插入到temp的后面即可(注意有无next的区别噢)
3. temp.next.num < node.num 还没找到, 继续找吧
*/
public void addByOrder(Node node){
//临时变量
Node temp = head;
boolean flag =false; //是否插入结点的标志
while(true){
if(temp.next == null){
flag =true;
break;
}
if(temp.next.num == node.num)
break;
if(temp.next.num > node.num){
flag = true;
break;
}
temp =temp.next;
}
if(flag){
node.next = temp.next;
node.pre = temp;
temp.next = node;
if(node.next != null)
node.next.pre = node; 如果node在结点最后添加,node.next为空,所以我们需要加上一层判断
}
}
///4.双链表的打印
public void list() {
//临时变量
Node temp = head;
while (true) {
if (temp.next == null)
break;
temp = temp.next;
System.out.println(temp);
}
}
- 把结点插入到双链表的指定结点后, 具体实现可参考2中结点顺序插入到链表
4.1.3 双链表的查找,修改和删除
- 双链表的查找和修改
- 双链表的删除:
如上图所示,我们需要删除链表中的2号结点, 在Java的具体实现事,我们需要让temp正好指向要删除的结点, 因为这样的话我们可以很方便的获取到删除结点的前驱结点!
- 主要代码如下:
///掌管双链表结点删除的两句代码
temp.pre.next = temp.next;
temp.next.pre = temp.pre; ②
在删除双链表的结点时,我们要特别注意待删除结点的位置情况,如果要删除的结点正好处于双链表的最后一个, 那么在上面的删除代码第二句(②),
temp.next是个null, null.pre就产生了空指针异常!
- 所以我们如果在删除或者说插入遇到了这种类似的情况, 都需要对 第二句加上一个判断, if( temp.next != null)
public void del(Node node) {
//临时变量
Node temp = head;
while (true) {
if (temp.next == null) {
System.out.println("未找到待删除的结点!");
break;
}
temp = temp.next;
///删除操作:
if (temp.num == node.num) {
temp.pre.next = temp.next;
if( temp.next != null) 在处理最后一个结点时,千万不要忘记这句话哦!
temp.next.pre = temp.pre;
break;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)