单向链表简介
链表是链式存储的有序的数据结构,但是它在内存中的地址却不一定是连续的,因此它的优点是可以利用内存中的碎片空间
对于单向链表来说,它的每个节点都有两个部分组成:data域和next域
- data域存储当前节点的数据;
- next域存储的是下一个节点的内存地址。如下图:
链表也可分为带头结点的链表和不带头结点的链表 - 带头结点的链表的头节点的data域是空的,主要是利用头结点的next域,类似于不坐乘客的火车头;
- 不带头结点的链表的每个节点都用到了data域和next域(当然了,最后一个节点的next域是空的,但并不代表以后就用不到呀)。
下面将模拟一个带头结点的链表来说明链表的增删改查。
需求
模拟一个带头结点的单向链表存储西游记中的人物信息;
需要存储的人物信息有编号、姓名和头衔;
可以实现增、删、改、查等功能;
其中添加人物信息的功能要实现按输入顺序添加和按照编号大小顺序添加两种方式。
西游人物实体类
package com.jpc.linkedlist.two;
/**
* 西游人物实体类
* id:编号
* name:姓名
* title:头衔
* next:下一个人物对象数据的内存地址
* @author jpc
*/
public class PersonNode {
private int id;
private String name;
private String title;
private PersonNode next;
public PersonNode() {
}
public PersonNode(int id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
//get/set/toString方法此处不再展示。。。
}
思路
增:按照输入顺序添加
1、先创建一个头节点head,表示单向链表的头
2、每次添加对象节点,直接将该对象的地址赋值给链表最后节点的next属性
增:按照编号大小顺序添加
1、先创建一个指针节点temp,初始化为链表头节点
2、每次添加对象节点,遍历链表,将待插入的新节点的id与链表的每个节点的后一个节点的id比较,如果是升序添加则找到当前节点下一个节点的id大于新节点的id时停止遍历
3、将当前指针节点temp所指向的节点的下一个节点的内存地址赋值给新节点的next属性
4、将新节点的内存地址赋值给temp所指向的节点的next属性
删:根据编号删除
1、定义布尔类型的标记flag,赋值为false
2、创建一个指针节点temp,初始化为链表头节点
3、用temp遍历链表,每次下移一位:temp = temp.next
4、如果temp的next为空就停止遍历;如果temp的下一个节点的id与输入的id相同,将flag变为true,停止遍历
5、如果flag为true,则将当前temp所指向的节点的下下一个节点的内存地址赋值给它前一个节点:temp.next = temp.next.next
改:根据编号修改姓名和头衔
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.two;
public class LinkedList {
private PersonNode head;
//初始化头节点
public LinkedList() {
head = new PersonNode();
}
//按照添加顺序添加数据节点
public void append(PersonNode node) {
PersonNode temp = this.head;
while(temp.getNext() != null) {
temp = temp.getNext();
}
temp.setNext(node);
}
//按照id大小升序添加数据
public void addOderBy(PersonNode node) {
PersonNode temp = this.head;
boolean flag = false;
temp = this.head;
while(true) {
if(temp.getNext() == null) {
break;
}
if(temp.getNext().getId() == node.getId()) {
flag = true;
break;
}else if(temp.getNext().getId() > node.getId()) {
break;
}
temp = temp.getNext();
}
if(flag) {
System.out.println(node + "id重复,不能添加!");
}else {
node.setNext(temp.getNext());
temp.setNext(node);
}
}
//删除节点数据
public void delete(int id) {
PersonNode temp = this.head;
boolean flag = false;
while(true) {
if(temp.getNext() == null) {
break;
}
if(temp.getNext().getId() == id) {
flag = true;
break;
}
temp = temp.getNext();
}
if(flag) {
temp.setNext(temp.getNext().getNext());
}else {
System.out.println("要删除的人员" + id + "不存在!");
}
}
//根据编号修改人物信息
public void update(PersonNode newPersonNode) {
boolean flag = false;
PersonNode temp = this.head;
while(true) {
if(temp == null) {
break;
}
if(temp.getId() == newPersonNode.getId()) {
flag = true;
break;
}
temp = temp.getNext();
}
if(flag) {
temp.setName(newPersonNode.getName());
temp.setTitle(newPersonNode.getTitle());
}else {
System.out.println("编号为" + newPersonNode.getId() + "的人物没有找到,无法修改!");
}
}
//根据编号查找数据
public void select(int id) {
boolean flag = false;
PersonNode temp = this.head;
while(true) {
if(temp == null) {
break;
}
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;
if(temp.getNext() == null) {
System.out.println("链表为空!");
return;
}
while(temp.getNext() != null) {
System.out.println(temp.getNext());
temp = temp.getNext();
}
}
}