Java数据结构:单向链表
单链表逻辑结构图:
- 链表以节点的方式来存储
- 每个节点包含 data 域,next 域:指向下一个节点
- 链表的各个节点不一定连续存储
- 链表分带头节点和不带头结点的,根据实际的需求来确定
代码实现
节点类:StudentNode
package com.syj.linkedlist;
/**
* 链表节点 StudentNode,每一个StudentNode对象就是一个节点
*/
public class StudentNode {
private int id; // 学生id
private String name; // 学生姓名
private StudentNode nextStudent; // 指向下一个学生
public StudentNode() {
}
public StudentNode(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public StudentNode getNextStudent() {
return nextStudent;
}
public void setNextStudent(StudentNode nextStudent) {
this.nextStudent = nextStudent;
}
@Override
public String toString() {
return "StudentNode{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
链表类SingleLinkedList
package com.syj.linkedlist;
/**
* 定义 SingleLinkedList 类表示学生链表,用来管理学生
* 该类包含的功能有:
* addNodeAscById 添加节点时,会按照节点的 id 属性升序排序插入节点
* addNodeTail 添加节点在单向链表的尾部
* addNodeHead 从链表头部插入节点
* deleteNodeById 根据 id 删除节点
* updateNameById 根据 id 修改学生节点中的姓名
* printLinkedList 输出整个链表
*/
public class SingleLinkedList {
// 先初始化一个头节点,头节点不要动,不存放具体数据
private StudentNode headNode = new StudentNode(0, "");
/**
* 添加节点时,会按照节点的 id 属性升序排序插入节点
* 思路:
* 1、创建一个辅助节点(tempNode)来遍历链表
* 2、若辅助接点的下一个节点的 id 大于新节点的 id,则在辅助节点和辅助节点的下一个节点之间插入新节点
* @param newStudentNode 要插入的新节点
*/
public void addNodeAscById(StudentNode newStudentNode) {
// 头节点不能动,所以定义一个辅助变量来帮助我们找到插入新节点的位置
StudentNode tempNode = headNode;
while (true) {
if (tempNode.getNextStudent() == null) {
// 已经到达了链表尾部还没有退出循环,说明中间没有位置插入,直接在尾部插入新节点
tempNode.setNextStudent(newStudentNode);
break;
}
if (tempNode.getNextStudent().getId() == newStudentNode.getId()) break; // 链表中已经存在该 id 的节点了
if (tempNode.getNextStudent().getId() > newStudentNode.getId()) {
// tempNode 节点的下一个节点的 id 大于新节点的 id,那么插入的位置就在 tempNode 节点和 tempNode.nextStudent() 节点之间
// 插入节点
newStudentNode.setNextStudent(tempNode.getNextStudent());
tempNode.setNextStudent(newStudentNode);
break;
}
tempNode = tempNode.getNextStudent(); // tempNode 后移一位
}
}
/**
* 添加节点在单向链表的尾部
* 思路:
* 1、找到当前链表的最后节点
* 2、将最后这个节点的 nextStudent 指向新的节点
* 其实如果要在链表尾部插入节点,最好给链表添加一个尾指针
* @param newStudentNode 要添加的新节点
*/
public void addNodeTail(StudentNode newStudentNode) {
// 由于头节点不能动,所以定义一个辅助遍历的节点
StudentNode tempNode = headNode;
// 遍历链表,找到最后一个节点
while (true) {
if (tempNode.getNextStudent() == null) break;
// 将 tempNode 后移
tempNode = tempNode.getNextStudent();
}
// 当退出 while 循环时,tempNode 就指向了链表的最后
// 将最后这个节点的 nextStudent 指向新的节点
tempNode.setNextStudent(newStudentNode);
}
/**
* 从链表头部插入节点
* 如果链表带头指针,最好使用头插法
* @param newStudentNode
*/
public void addNodeHead(StudentNode newStudentNode) {
// 先判断链表是否为空
if (headNode.getNextStudent() == null) {
// 如果链表为空,则直接在头结点后面添加
headNode.setNextStudent(newStudentNode);
} else {
// 链表不为空
// 先将头节点的 nextStudent 赋值给新节点的 nextStudent
newStudentNode.setNextStudent(headNode.getNextStudent());
// 再将新节点赋值给头节点的 nextStudent
headNode.setNextStudent(newStudentNode);
}
}
/**
* 根据 id 删除节点
* 思路:
* 1、定义一个辅助节点(tempNode)遍历链表,找到要删除节点的前一个节点
* 2、删除节点:tempNode.setNextStudent(tempNode.getNextStudent().getNextStudent())
* 被删除的节点将不会有其它引用再指向它,将被垃圾回收机制回收
* @param id 要删除的节点的 id
*/
public void deleteNodeById(int id) {
StudentNode tempNode = headNode;
while (true) {
if (tempNode.getNextStudent() == null) {
// 已经到链表最后了,还没有找到,说明链表中不存在要找的节点
System.out.println("要删除的节点不存在");
break;
}
if (tempNode.getNextStudent().getId() == id) {
// 找到要删除的节点了
tempNode.setNextStudent(tempNode.getNextStudent().getNextStudent());
break;
}
tempNode = tempNode.getNextStudent();
}
}
/**
* 根据 id 修改学生节点中的姓名
* 思路:遍历链表,找到对应 id 的节点,然后修改姓名
* @param newStudentNode 修改过姓名后的新节点
*/
public void updateNameById(StudentNode newStudentNode) {
// 判断链表是否为空
if (headNode.getNextStudent() == null) {
System.out.println("链表为空");
return;
}
// 根据新节点的 id 找到链表中对应的节点
StudentNode tempNode = headNode;
while (true) {
if (tempNode.getNextStudent() == null) {
// 已经到链表尾部了,还没有找到对应 id 的节点,说明链表中没有该 id 的节点
System.out.println("不存在id为:" + newStudentNode.getId() + "的学生");
break;
}
if (tempNode.getId() == newStudentNode.getId()) {
// 找到了,修改姓名
tempNode.setName(newStudentNode.getName());
break;
}
tempNode = tempNode.getNextStudent(); // tempNode 后移一位
}
}
/**
* 输出整个链表
*/
public void printLinkedList() {
// 先判断链表是否为空
if (headNode.getNextStudent() == null) {
System.out.println("链表为空");
} else {
// 因为头结点不能动,所以我们定义一个辅助变量来遍历
StudentNode tempNode = headNode.getNextStudent();
while (true) {
// 判断链表是否到最后
if (tempNode == null) break;
// 输出节点信息
System.out.println(tempNode);
// 将 tempNode 后移
tempNode = tempNode.getNextStudent();
}
}
}
}
测试单向链表
package com.syj.linkedlist;
public class SingleLinkedListDemo {
// 测试
public static void main(String[] args) {
// 创建节点
StudentNode student1 = new StudentNode(1, "小白");
StudentNode student2 = new StudentNode(2, "小黑");
StudentNode student3 = new StudentNode(3, "小红");
// 创建链表
SingleLinkedList studentLinkedList = new SingleLinkedList();
// 从尾部添加节点
System.out.println("===========从尾部添加节点============");
studentLinkedList.addNodeTail(student1);
studentLinkedList.addNodeTail(student2);
studentLinkedList.addNodeTail(student3);
studentLinkedList.printLinkedList();
studentLinkedList.deleteNodeById(3);
studentLinkedList.deleteNodeById(2);
studentLinkedList.deleteNodeById(1);
// 从头部添加节点
System.out.println("===========从头部添加节点============");
studentLinkedList.addNodeHead(student1);
studentLinkedList.addNodeHead(student2);
studentLinkedList.addNodeHead(student3);
studentLinkedList.printLinkedList();
studentLinkedList.deleteNodeById(1);
studentLinkedList.deleteNodeById(2);
studentLinkedList.deleteNodeById(3);
// 添加节点,并按照节点的 id 属性升序排序添加节点
System.out.println("===========按照节点的 id 属性升序排序添加节点============");
studentLinkedList.addNodeAscById(student3);
studentLinkedList.addNodeAscById(student2);
studentLinkedList.addNodeAscById(student2); // 重复插入
studentLinkedList.addNodeAscById(student1);
studentLinkedList.printLinkedList();
// 修改链表中 id = 1 的节点的姓名为“小黄”
System.out.println("===========修改节点============");
StudentNode newStudent1 = new StudentNode(1, "小黄");
studentLinkedList.updateNameById(newStudent1);
studentLinkedList.printLinkedList();
// 删除 id = 2 的节点
System.out.println("===========删除节点============");
studentLinkedList.deleteNodeById(2);
studentLinkedList.printLinkedList();
}
}