双向链表分析
单向链表查找的方向只能是一个方向,双向链表是可以向前或者向后查找的
单项链表不能自我删除,但是双向链表是可以实现自我删除的
增删改查的思路分析
-
遍历方法和单链表是一样的,不过双向链表可以向前也可以向后查找
-
添加(默认添加到双向链表的最后)
-
先找到双向链表的最后这个节点
-
temp.next=newHeroNode
-
newHeroNode.pre=temp
-
-
修改思路和单链表的原理一样
-
删除
-
因为是双向链表,我们可以实现自我删除某个节点
-
直接找到要删除的节点,比如temp
-
temp.pre.next=temp.next
-
temp.next.pre=temp.pre
-
代码实现
//定义HeroNode2,每个HeroNode2对象就是一个节点
class HeroNode2 {
public int no;
public String name;
public String nickname;
public HeroNode2 next;// 指向下一个节点
public HeroNode2 pre;// 指向前一个节点
// 构造器
public HeroNode2(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode2{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
// 创建一个双向链表的类
class DoubleLinkedList {
// 先初始化头节点
private HeroNode2 head = new HeroNode2(0, "", "");
// 返回头节点
public HeroNode2 getHead() {
return head;
}
}
添加
// 添加一个节点到双向链表的最后
public void add(HeroNode2 heroNode) {
// 因为head节点不能动 所以我们要声明一个变量帮助遍历
HeroNode2 temp = head;
// 遍历链表 找到最后
while (true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,将cur后移
temp = temp.next;
}
// 当退出while循环时,cur指向最后
//形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
// 插入
public void insert(HeroNode2 heroNode) {
// 头节点不能动,需要辅助变量
// 因为单链表,我们找到的temp是位于添加位置的前一个节点,否则插入不了
HeroNode2 temp = head;
boolean flag = false;// flag标志添加的编号已经存在 无法添加
while (true) {
if (temp.next == null) {// 说明temp已经在链表的最后了
break;
}
if (temp.next.no > heroNode.no) {// 说明已经找到了要插入的位置,就在temp后面插入
break;
} else if (temp.next.no == heroNode.no) {// 说明需要添加的节点点已经存在了
flag = true;// 说明编号存在
break;
}
temp = temp.next;
}
if (flag) {
System.out.println("编号已经存在 不能加入:" + heroNode.no);
} else {
// 插入到链表,temp的后面
heroNode.next = temp.next;
// 防止空指针报错
if (temp.next != null) {
temp.next.pre = heroNode;
}
temp.next = heroNode;
heroNode.pre = temp;
}
}
删除
// 从双向链表中删除一个节点
/*
* 因为是双向链表,我们可以实现自我删除某个节点
* 直接找到要删除的节点,比如temp
* */
public void remove(int no) {
// 判断是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
while (true) {
if (temp == null) {// 已经都链表的最后
break;
}
if (temp.no == no) {// 找到了要删除的节点的前一个节点
temp.pre.next = temp.next;
// 代码有问题
// 如果是最后一个节点,就不需要执行下面这句话,否则会报空指针异常
if (temp.next != null) {
temp.next.pre = temp.pre;
}
break;
}
temp = temp.next;
}
}
修改
// 修改一个节点内容,可以看出双向链表的的修改节点内容和单向链表是一样的
public void update(HeroNode2 newHeroNode) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head.next;
while (true) {
if (temp == null) {
break;// 已经遍历完链表
}
if (temp.no == newHeroNode.no) {
// 找到需要修改的节点
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
}
temp = temp.next;
}
}
遍历
// 显示链表[遍历]
public void show() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 cur = head.next;
while (true) {
if (cur == null) {
break;
}
// 输出节点信息
System.out.println(cur);
// 将cur后移,否则会出现死循环
cur = cur.next;
}
}
测试方法和我们之前测试单链表的方式一样