双向链表简介
对于双向链表来说,它的每个节点都有两个部分组成:pre域、data域和next域
- pre域存储的是上一个节点的内存地址;
- data域存储当前节点的数据;
- next域存储的是下一个节点的内存地址。如下图:
链表也可分为带头结点的链表和不带头结点的链表
- 带头结点的链表的头节点的data域是空的,主要是利用头结点的next域,类似于不坐乘客的火车头;
- 不带头结点的链表的每个节点都用到了pre域、data域和next域(当然了,最后一个节点的next域是空的,但并不代表以后就用不到呀)。
下面将模拟一个带头结点的双向链表来说明链表的增删改查。
需求
模拟一个带头结点的双向链表存储西游记中的人物信息;
需要存储的人物信息有编号、姓名和头衔;
可以实现增、删、改、查等功能;
其中添加人物信息的功能要实现按输入顺序添加和按照编号大小顺序添加两种方式。
西游人物实体类
package com.jpc.linkedlist.three;
/**
* 双向链表的节点类
* 该类的next属性指向下一个节点
* 该类的pre属性指向上一个节点
* 创建一个有参构造函数用于在创建时传入数据
* @author 景鹏冲
*/
public class PersonNode {
private int id;
private String name;
private String title;
private PersonNode pre;
private PersonNode next;
public PersonNode() {
}
public PersonNode(int id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
//此处省略get/set方法
@Override
public String toString() {
return "[id=" + id + ", name=" + name + ", title=" + title + "]";
}
}
思路
增:按照输入顺序添加
1、先创建一个头节点head,表示单向链表的头
2、每次添加对象节点,直接将该对象的地址赋值给链表最后节点的next属性,并且将链表最后节点的地址赋值给新节点的pre属性
增:按照编号大小顺序添加
1、先创建一个指针节点temp,初始化为链表头节点
2、每次添加对象节点,遍历链表,将待插入的新节点的id与链表的每个节点的后一个节点的id比较,如果是升序添加则找到当前节点下一个节点的id大于新节点的id时停止遍历
3、将当前指针节点temp所指向的节点的下一个节点的内存地址赋值给新节点的next属性
4、将新节点的内存地址赋值给temp所指向的节点的next属性
5、将temp的内存地址赋值给新节点的pre属性
6、将新节点的内存地址赋值给temp所指向的节点的下一个节点的pre属性
删:根据编号删除
1、定义布尔类型的标记flag,赋值为false
2、创建一个指针节点temp,初始化为链表头节点
3、用temp遍历链表,每次下移一位:temp = temp.next
4、如果temp的next为空就停止遍历;如果temp的id与输入的id相同,将flag变为true,停止遍历
5、如果flag为true,则将当前temp所指向的节点的下一个节点的内存地址赋值给它前一个节点的next属性:temp.pre.next = temp.next
6、如果要删除的节点是最后一个,那么为了防止出现空指向异常,所以要判断temp.next是否为空,如果不为空,将temp的前一个节点的内存地址赋值给temp所指向的节点的下一个节点的pre属性:temp.next.pre = temp.pre
改:根据编号修改姓名和头衔
1、定义布尔类型的标记flag,赋值为false
2、创建一个指针节点temp,初始化为链表头节点的下一个节点
3、用temp遍历链表,每次下移一位:temp = temp.next
4、如果temp为空就停止遍历;如果temp的id与新节点的id相同,将flag变为true,停止遍历
5、如果flag为true,则将新节点的name和title赋值给当前temp所指向节点的name和title
查:根据编号查找
1、定义布尔类型的标记flag,赋值为false
2、创建一个指针节点temp,初始化为链表头节点
3、用temp遍历链表,每次下移一位:temp = temp.next
4、如果temp为空就停止遍历;如果temp的id与新节点的id相同,将flag变为true,停止遍历
5、如果flag为true,则将temp输出
查:遍历输出链表
1、如果头节点的next为空,就提示链表为空并结束方法
2、如果头节点的next不为空,创建一个指针节点temp,初始化为链表头节点
3、用while循环输出temp,每次下移:temp = temp.next
代码实现
package com.jpc.linkedlist.three;
import com.jpc.linkedlist.three.PersonNode;
public class DoubleLinkedList {
private PersonNode head;
//初始化头节点
public DoubleLinkedList() {
head = new PersonNode();
}
//按照默认顺序添加节点数据
public void add(PersonNode personNode) {
PersonNode temp = this.head;
while(temp.getNext() != null) {
temp = temp.getNext();
}
temp.setNext(personNode);
personNode.setPre(temp);
}
//按照编号大小添加节点数据
public void addOderBy(PersonNode personNode) {
boolean flag = true;
PersonNode temp = this.head;
while(temp.getNext() != null) {
if(temp.getNext().getId() > personNode.getId()) {
break;
}
if(temp.getNext().getId() == personNode.getId()) {
flag = false;
break;
}
temp = temp.getNext();
}
if(flag) {
personNode.setNext(temp.getNext());
temp.setNext(personNode);
personNode.setPre(temp);
temp.getNext().setPre(personNode);
}else {
System.out.println("编号为" + personNode.getId() + "的人物已经存在,不能添加!");
}
}
//根据id删除节点数据
public void delete(int id) {
boolean flag = false;
PersonNode temp = this.head.getNext();
while(temp != null) {
if(temp.getId() == id) {
flag = true;
break;
}
temp = temp.getNext();
}
if(flag) {
temp.getPre().setNext(temp.getNext());
if(temp.getNext() != null) {
temp.getNext().setPre(temp.getPre());
}
}else {
System.out.println("您要删除的" + id + "号人物不存在!");
}
}
//根据id修改节点数据
public void update(PersonNode personNode) {
boolean flag = false;
PersonNode temp = this.head.getNext();
while(temp != null) {
if(temp.getId() == personNode.getId()) {
flag = true;
break;
}
temp = temp.getNext();
}
if(flag) {
temp.setName(personNode.getName());
temp.setTitle(personNode.getTitle());
}else {
System.out.println("您要修改的" + personNode.getId() + "号人物不存在!");
}
}
//根据id查询节点数据
public void select(int id) {
boolean flag = false;
PersonNode temp = this.head.getNext();
while(temp != null) {
if(temp.getId() == id) {
flag = true;
break;
}
temp = temp.getNext();
}
if(flag) {
System.out.println(temp);
}else {
System.out.println("您要查找的" + id + "号人物不存在!");
}
}
//遍历双向链表
public void list() {
PersonNode temp = this.head.getNext();
while(temp != null) {
System.out.println(temp);
temp = temp.getNext();
}
}
//获取双向链表的长度
public int length() {
int sum = 0;
PersonNode temp = this.head.getNext();
if(temp == null) {
return sum;
}
while(temp != null) {
sum ++;
temp = temp.getNext();
}
return sum;
}
}