一、概念
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
图解:
二、双向链表的相关操作
1. 思路:
- 遍历:和单链表一样的思路,只是可以向前遍历,也可以向后遍历;
- 添加:temp.next = newNode,newNode.pre = temp ( temp表示链表的最后一个结点);
- 修改:思路和单链表一样;
- 删除:找到要删除的结点temp,
temp.pre.next = temp.next,
temp.next.pre = temp.pre (这个式子要求temp不是最后一个节点,否则temp.next.pre会报空指针异常)。
2. 代码实现:
定义一个双向链表:
class HeroNode2 {
public int no;
public String name;
public String nickName;
public HeroNode2 pre; //前驱
public HeroNode2 next; //后继
public HeroNode2(int no, String name, String nickName) {
this.no = no;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
双向链表管理类:
/**
* 定义双向链表管理类DoubleLinkedList,管理HeroNode2
*/
class DoubleLinkedList {
//先初始化一个头结点,不存放具体的数据
private HeroNode2 head = new HeroNode2(0,"","");
public HeroNode2 getHead() {
return head;
}
/**
* 遍历链表.
*/
public void list() {
//链表为空
if (head.next == null) {
System.out.println("链表为空!");
return;
}
//因为头结点不能动,所以我们需要一个辅助变量指向头结点的下一个节点
HeroNode2 temp = head.next;
while (true) {
//判断是否遍历到了链表最后
//这里不能是temp.next == null
if (temp == null) {
break;
}
//输出节点信息
System.out.println(temp);
temp = temp.next;
}
}
/**
* 向单链表中添加节点(添加到最后).
* @param heroNode
*/
public void add(HeroNode2 heroNode) {
//因为头结点不能动,所以我们需要一个辅助变量指向头结点
HeroNode2 temp = head;
//变量链表,找到最后一个节点
while (true) {
//指向了链表的最后一个节点
if (temp.next == null) {
break;
}
//指针后移
temp = temp.next;
}
temp.next = heroNode;
heroNode.pre = temp;
}
/**
* 修改链表的节点信息(根据no查找所要修改的节点)
* @param heroNode
*/
public void update(HeroNode2 heroNode) {
if (head.next == null) {
System.out.println("链表为空,不能修改");
return;
}
//因为头结点不能动,所以我们需要一个辅助变量指向头结点的下一个节点
HeroNode2 temp = head.next;
//标志是否找到所要修改的节点
boolean flag = false;
while (true) {
//遍历到了链表最后
if (temp == null) {
break;
}
//找到所要修改的节点
if (temp.no == heroNode.no) {
flag = true;
break;
}
temp = temp.next;
}
//根据flag判断是否修改节点
if (flag) {
temp.name = heroNode.name;
temp.nickName = heroNode.nickName;
} else {
System.out.printf("未找到编号为 %d 的节点\n", heroNode.no);
}
}
/**
* 删除指定的链表节点
* @param no
*/
public void delete(int no) {
if (head.next == null) {
System.out.println("链表为空,不能删除");
return;
}
//因为头结点不能动,所以我们需要一个辅助变量指向头结点
HeroNode2 temp = head.next;
//标志是否找到所要删除节点的上一个节点
boolean flag = false;
while (true) {
//遍历到了链表最后
if (temp == null) {
break;
}
//找到所要删除节点的上一个节点
if (temp.no == no) {
flag = true;//表示找到了所要删除节点的上一个节点temp
break;
}
temp = temp.next;
}
//根据flag表示是否要删除节点 到了这里temp是所要删除节点
if (flag) {
temp.pre.next = temp.next;
if (temp.next != null) { //表示temp不为最后一个结点
temp.next.pre = temp.pre;
}
} else {
System.out.printf("未找到编号为 %d 的节点\n", no);
}
}
}
3. 测试
测试类:
/**
* @auther 半命i 2019/12/19
* @description
*/
public class DoubleLinkedListDemo {
public static void main(String[] args) {
//结点
HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
HeroNode2 hero3 = new HeroNode2(5, "吴用", "智多星");
HeroNode2 hero4 = new HeroNode2(8, "林冲", "豹子头");
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
//加入
doubleLinkedList.add(hero1);
doubleLinkedList.add(hero2);
doubleLinkedList.add(hero3);
doubleLinkedList.add(hero4);
System.out.println("=============遍历================");
doubleLinkedList.list();
System.out.println();
System.out.println("=============修改================");
HeroNode2 hero5 = new HeroNode2(5,"小吴","小聪明");
doubleLinkedList.update(hero5);
doubleLinkedList.list();
System.out.println();
System.out.println("=============删除================");
doubleLinkedList.delete(8);
doubleLinkedList.list();
}
}
运行结果: