前言
为什么要使用双向链表呢?
单向链表有一个缺点:就是它不可以反向遍历,而双向链表就看可以。首先双向链表是单向链表的一种,
其次它比单向链表多一个属性。pre(指向节点的上一个属性)
package cn.itjy.linkedlist;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
// 测试
System.out.println("双向链表的测试");
HeroNode2 node1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 node2 = new HeroNode2(2, "吴用", "智多星");
HeroNode2 node3 = new HeroNode2(3, "李逵", "黑旋风");
HeroNode2 node4 = new HeroNode2(4, "林冲", "豹子头");
DoubleLinkedList d = new DoubleLinkedList();
d.add(node1);
d.add(node2);
d.add(node3);
d.add(node4);
d.list();
// 修改
HeroNode2 newHeroNode = new HeroNode2(4, "啦啦啦", "国王");
d.update(newHeroNode);
System.out.println("修改后");
d.list();
// 删除
d.delete(3);
System.out.println("删除后");
d.list();
}
}
// 创建一个双向链表的类
class DoubleLinkedList {
// 初始化头节点
private HeroNode2 head2 = new HeroNode2(0, "", "");
// 返回头节点
public HeroNode2 getHead2() {
return head2;
}
// 遍历双向链表的方法
public void list() {
// 判断链表为空
if (head2.next == null) {
System.out.println("链表为空");
return;
}
HeroNode2 temp = head2.next;
while (temp!=null) {
System.out.println(temp);
temp = temp.next;
}
}
// 添加一个节点到双向链表的最后
public void add(HeroNode2 heroNode) {
// 因为head节点不能动,因此我们需要一个辅助变量
HeroNode2 temp = head2;
// 遍历链表
while (true) {
// 找到链表的最后
if (temp.next == null) { //
break;
}
// 如果没有找到最后,将temp后移
temp = temp.next;
}
// 当推出while循环时,temp就指向了链表的最后
// 最后的节点指向新的节点
// 形成一个双向链表
temp.next = heroNode;
heroNode.pre = temp;
}
// 修改一个节点的内容,可以看到双向链表的节点内容修改和单向链表一样
// 只是节点的类型改HeroNode2
public void update(HeroNode2 node2) {
// 判断是否为空
if (head2.next == null) {
System.out.println("链表为空");
return;
}
// 定义一个辅助变量
HeroNode2 temp = head2.next;
boolean flag = false;
while (temp!=null) {
if (temp.no == node2.no) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.name = node2.name;
temp.nickName = node2.nickName;
} else {
System.out.printf("没有找到编号%d节点,不能修改\n", node2.no);
}
}
// 从双向链表中删除一个节点
/**
* 说明:对于双向链表,我们可以直接找到要删除的这个节点 找到后自我删除即可
*/
public void delete(int no) {
// 链表是否为空
if (head2.next == null) {
System.out.println("链表为空,无法删除");
return;
}
HeroNode2 temp = head2.next;
// 找到是否找到待输出节点的前一个节点
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.no == no) {
// 找到待删除节点的前一个节点temp
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.pre.next = temp.next;
// 这里的代码有问题?如果是最后一个节点就不需要执行下面这句话,否则会出现null指针异常。
if (temp.next != null) {
temp.next.pre = temp.pre;
}
} else {
System.out.printf("没有找到编号%d节点,不能删除\n", no);
}
}
}
class HeroNode2 {
public int no;
public String name;
public String nickName;
public HeroNode2 next; // 指向下一个节点 null
public HeroNode2 pre; // 指向前一个节点 null
// 构造器
public HeroNode2(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
// 为了显示方便,我们重写toString
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + ", nickName=" + nickName + "]";
}
}
总结:
|方法|思路 |
list-
1.先判断链表是否为空
2.让辅助节点找到当前节点并打印,然后后移当前节点。以此循环。
3.直到最后一个节点-|
add
1.定义一个辅助节点,
2.因为我们要将新节点插入的链表的最后段,所以开始循环
3.找到最后终止循环,然后让最后的节点等于添加的节点
update
1.先判断链表是否为空
2.定义一个辅助节点(方便我们查找指定位置的节点)
3.判断富足节点的编号是否等于当前节点的编号,如果等于终止循环。否则以此循环。直到最后一个节点
4.然后让找到的编号节点的属性等于传入的属性
delete
1.先判断链表是否为空
2.让辅助节点找到当前节点,
3.然后让当前节点的上一个节点的下一个节点指向当前节点的下一个节点。让当前节点的下一个节点的上一个节点指向当前节点的上一个节点。
这里可以可能有点绕-|
觉得绕的话,把这段话多读几遍。就可以理解了