1. 为什么要引入双链表?
那我们就先来说一说单链表有一些缺点:其一:查找的方向只能是一个方向,也就是next遍历;其二:单链表不能实现自我删除,需要靠辅助节点。
而双链表的出现就是为了解决上述两个问题
2.双链表是什么样的?
大家看图可以清晰发现,双链表比单链表多了一个pre:这个就是用来指向前一个节点的。那么就可以清晰感到为什么不需要辅助接点就可以删除了
简单分析一下: 因为本身节点直接把它前一个结点的next指向本身节点next(后一个节点),而本身节点的下一个节点(next)的pre(前一个节点)指向本身节点的pre(前一个结点)就删除本身这个节点啦。
接着我们来使用java代码实现试一试:
package com.pengmeng.linkedList;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
//先创建节点 面试题 Interview questions
DoubleHeroNode h1 = new DoubleHeroNode(1, "宋江", "及时雨");
DoubleHeroNode h2 = new DoubleHeroNode(2, "卢俊义", "玉麒麟");
DoubleHeroNode h3 = new DoubleHeroNode(3, "吴用", "智多星");
DoubleHeroNode h4 = new DoubleHeroNode(4, "林冲", "豹子头");
DoubleLinkedList dlt = new DoubleLinkedList();
dlt.add(h1);
dlt.add(h2);
dlt.add(h3);
dlt.add(h4);
//遍历
dlt.list();
//修改
System.out.println("修改4号节点的内容");
DoubleHeroNode h6 = new DoubleHeroNode(4, "你挂", "阿达");
dlt.update(h6);
dlt.list();
//查找
System.out.println("查找4号节点的内容");
dlt.select(4);
//删除
System.out.println("删除4号节点的内容");
dlt.delete(4);
dlt.list();
}
}
//双向链表的管理类
class DoubleLinkedList{
//初始化一个头节点,头节点不要动,动了就不找不到列表了(没有具体的数子)
private DoubleHeroNode head = new DoubleHeroNode(0,"","");
//返回头节点
public DoubleHeroNode getHead() {
return head;
}
// 显示双向链表 思路:遍历
public void list(){
//0 判断链表是否为空,也就是head.next == null head是头节点
if (head.next == null){
System.out.println("链表为空");
return;
}
//1 同样需要一个辅助节点(temp)遍历,因为头节点不可以用
DoubleHeroNode temp = head.next;
//2 遍历
while (true){
//判断是否到最后了,也就是 next域 = null 也就是temp
if (temp == null){
break;
}
// 输出节点的信息
System.out.println(temp);
// 将下一个节点后移
temp = temp.next;
}
}
//有顺序的添加操作
public void addByOrder(DoubleHeroNode heroNode){
// 需要一个辅助节点temp,因为头节点不能动,且是单链表,temp是位于添加操作的前一个节点
DoubleHeroNode temp = head;
boolean flag = false;//标志编号是否存在
while (true){
if (temp.next == null){//判断temp是不是最后一个节点
break;
}
if (temp.next.no > heroNode.no){//位置找到了,就在temp后面插入
flag = false;
break;
}else if (temp.next.no == heroNode.no){//说明节点存在了
flag = true;
break;
}
temp = temp.next;//后移一位,相当遍历
}
if (flag){
//编号存在,不能添加
System.out.printf("插入英雄的编号%d存在,不能加入\n",heroNode.no);
}else {
// 可以添加,插入链表中
temp.next = heroNode;
heroNode.pre = temp;
}
}
//添加节点
public void add(DoubleHeroNode heroNode){
//1 需要一个辅助节点(temp)遍历,因为头节点不可以用
DoubleHeroNode temp = head;
//2 遍历
while (true){
// 当 next域是null的时候就是最后一个
if (temp.next == null){
break;
}
//没有找到,就接着移动查找
temp = temp.next;
}
// 当退出while循环的时候,temp就是指向链表的最后(把新的节点添加进去)
temp.next = heroNode;
heroNode.pre = temp;
}
//修改链表信息,但是编号no不能修改
public void update(DoubleHeroNode newHeroNode){
//首先判断链表是否为空
if (head.next == null){
//链表为空不能修改
System.out.println("链表为空不能修改");
}
//辅助接点
DoubleHeroNode temp = head.next;
boolean flag = false;//用于判断节点是否存在
while (true){
if (temp.no == newHeroNode.no){
//找到了这个节点
flag = true;
break;
}
if (temp.next == null){
//到了最后一个节点啦,用于终止循环的
break;
}
temp = temp.next;
}
if (flag){
//找到了节点,赋值修改
temp.name = newHeroNode.name;
temp.nickName = newHeroNode.nickName;
}else {
//没找到节点
System.out.printf("%d 的英雄编号不存在,不能修改,请先完成添加操作\n",newHeroNode.no);
}
}
// 链表的删除操作
public void delete(int no){
//首先判断链表是否为空
if (head.next == null){
//链表为空不能修改
System.out.println("链表为空不能修改");
}
// 找到一个辅助接点
DoubleHeroNode temp = head.next;
// 用于判断节点是否存在
boolean flag = false;
while (true){
if (temp == null){
//找到尾部节点啦
break;
}
if (temp.no == no){
//找到删除节点的前一个结点啦
flag = true;
break;
}
temp = temp.next;//后移一位,用于遍历
}
if (flag){
//找到当前删除节点了
temp.pre.next = temp.next;
if (temp.next != null) {
temp.next.pre = temp.pre;
}
}else {
//没有当前编号no的节点
System.out.printf("没有找到编号为%d的节点,请先完成list遍历操作",no);
}
}
//查询操作
public void select(int no){
DoubleHeroNode temp = head.next;
boolean flag = false;
while (true){
if (temp == null){
// 到位尾部节点
break;
}
if (temp.no == no){
//找到当前节点啦
flag = true;
break;
}
temp = temp.next;//后移
}
if (flag){
//找到了,直接输出temp
System.out.println(temp);
}else {
System.out.printf("没有找到编号为%d的节点,请先完成add操作\n",no);
}
}
}
//双向链表的节点
class DoubleHeroNode {
public int no;//编号
public String name;//姓名
public String nickName;//别称
public DoubleHeroNode next;//指向下一个节点(域)
public DoubleHeroNode pre;//指向前一个节点
//创建构造方法,初始化参数
public DoubleHeroNode(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "DoubleHeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
…结果展示
最后我们来也可以回顾一下所有所学的单链表认识:大家可以看看我以前的文章
- 单链表的初步认识 这篇文章主要讲解一些链表的基本认识,以及简单的代码模拟。
- 单链表的顺序添加 这篇文章主要讲解第一篇文章所提出的问题进行解答,以及思维。
- 单链表的增删改查 这篇文章主要用代码实现链表的增删改查。
-
有关单链表的面试题 新浪、腾讯、百度的面试题,用代码来解析!
这四篇文章,需要你好好在自己的电脑上跑一遍,基本上你对单链表的认识就不仅仅局限在理论上了,在优化自己的代码的时候也知道如何下手了! - 如果这篇文章能帮助你,码字不易,请动动小指给我点个赞,谢谢!。