模拟双向链表详解
说明
- 双向链表通过两个指针依次将各个节点连接起来
- 使用next指针指向后一个节点,通过pre指针指向前一个节点
- 可以很好的解决删除节点时找到自身节点删除的问题,单向链表在删除某个节点时需要找到这个节点的前一个节点,而双向链表则不用
- 每一个节点实质为内存中的一个节点实例对象,将每个实例对象的引用(地址)互相连接起来,看起来像一条链子一样,称为链表
- 一个节点除了存储本身的数据域外,还要存储其他节点的地址引用
节点
//节点类
//则每一个Hero实例就是一个节点
class HeroNode2 {
//编号
public int no;
//姓名
public String name;
//昵称
public String nickedName;
//指针域,存储的实则是一个地址,指向下一个节点
public HeroNode2 next;
//指针域,定义指向前一个节点的指针,存储的也是前一个节点的地址
public HeroNode2 pre;
public HeroNode2() {
}
public HeroNode2(int no, String name, String nickedName) {
this.no = no;
this.name = name;
this.nickedName = nickedName;
}
//显示节点信息
@Override
public String toString() {
return "Hero{" +
"no=" + no +
", name='" + name + '\'' +
", nickedName='" + nickedName + '}';
}
}
双链表类模拟
//模拟双向链表
class DoubleLinkedList {
//创建一个头节点,不存放任何数据
public HeroNode2 head = new HeroNode2();
//按照英雄编号添加节点
//向链表中添加元素,按照英雄的编号大小添加
//思路:
//1.难点在于寻找新节点要插入的位置
//2.考虑将新节点插入到temp 和 temp.next之间
public void addByOrder(HeroNode2 hero) {
//因为头节点不能动,因此还是需要一个辅助变量temp指向head
HeroNode2 temp = head;
//定义一个表示位flag标识节点是否能添加成功,默认为false
boolean flag = false;
while (true) {
//如果已经遍历到最后一个节点,则将新节点添加到temp的后面
if (temp.next == null) {
break;
}
//如果要添加的节点的标号大于temp,小于temp.next的编号,则找到temp
if (temp.next.no > hero.no) {
break;
//如果要添加的节点的编号已经存在,则不能添加
} else if (temp.next.no == hero.no) {
flag = true;
break;
}
//temp后移
temp = temp.next;
}
if (flag) {
//如果flag为真,则表示要加入的节点编号已经存在
System.out.println("该节点已经存在~~");
} else {
//循环结束后则已经找到新节点要插入的位置
if (temp.next != null) {
temp.next.pre = hero;
}
hero.next = temp.next;
temp.next = hero;
hero.pre = temp;
}
}
//遍历双向链表
//显示链表
public void list() {
//先判断链表是否为空
if (head.next == null) {
System.out.println("链表为空~~");
return;
}
//因为头节点不能动,因此还是需要一个辅助遍历temp(类似一个指针,每次指向下一个节点)
//temp指向第一个节点
HeroNode2 temp = head.next;
//如果链表不为空,则遍历链表
while (true) {
//如果链表为空,则结束遍历
if (temp == null) {
break;
}
//输出链表内容
System.out.println(temp);
//temp后移指向下一个节点
temp = temp.next;
}
}
//向链表中添加数据,添加到链表的最后边
//编写向双链表中添加节点的方法,不考虑插入元素的位置,直接插入链表的最后
public void add(HeroNode2 hero) {
//因为head头节点不能动,所以需要创建一个临时变量
HeroNode2 temp = head;
//先遍历链表找到链表的最后,然后将这个节点添加
while (true) {
if (temp.next == null) {
break;
}
//如果不是最后一个节点,则temp指向的节点后移
temp = temp.next;
}
//循环结束后找到链表的最后一个元素
//将这个节点添加到链表
temp.next = hero;
hero.pre = temp;
}
//修改节点
//编写修改链表中节点的方法
public void update(HeroNode2 newHero) {
//判断链表是否为空
if (head.next == null) {
System.out.println("链表是空的~~");
return;
}
//因为头节点不能动,因此创建一个辅助变量temp
HeroNode2 temp = head.next;
//创建一个标志位flag判断是否找到要修改的节点
boolean flag = false;
while (true) {
//如果已经遍历完链表,还没有找到,直接返回
if (temp == null) {
break;
}
//如果找到需要修改的节点
if (temp.no == newHero.no) {
flag = true;
break;
}
//指针后移
temp = temp.next;
}
//如果找到需要修改的节点
if (flag) {
temp.name = newHero.name;
temp.nickedName = newHero.nickedName;
} else {
//如果没有找到
System.out.println("要修改的节点不存在~~");
}
}
//删除指定的节点
//编写方法删除链表中的节点(根据节点的编号)
public void delete(int no) {
//判断链表是否为空
if (head.next == null){
System.out.println("链表为空,不能删除~~");
return;
}
//因为头节点不能动,定义辅助变量temp
HeroNode2 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 {
//没找到要删除的节点
System.out.println("要删除的节点不存在~~");
}
}
}
测试类
public static void main(String[] args) {
//测试,创建四个节点
HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
//创建一个双向链表
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
//向双向链表中添加元素
// doubleLinkedList.add(hero1);
// doubleLinkedList.add(hero2);
// doubleLinkedList.add(hero3);
// doubleLinkedList.add(hero4);
//按照英雄编号向链表中添加元素
doubleLinkedList.addByOrder(hero1);
doubleLinkedList.addByOrder(hero3);
doubleLinkedList.addByOrder(hero4);
doubleLinkedList.addByOrder(hero2);
//显示双向链表元素
doubleLinkedList.list();
HeroNode2 newHero = new HeroNode2(4, "小林", "豹子头~~");
//修改链表中的节点
System.out.println("修改节点~~");
doubleLinkedList.update(newHero);
doubleLinkedList.list();
//删除某个节点
System.out.println("删除节点~~");
doubleLinkedList.delete(4);
doubleLinkedList.list();
}
}