这篇主要回顾下,单链表和双链表增删查改的实现,链表操作一个基本点就是先连结再拆除。
后文用例都是将一个学生作为节点,包含姓名和分数两项属性,进行相关功能练习,完整代码可访问。https://gitee.com/flying-morning/data-structure/tree/master/src/com/datastructure/list
class StudentNode {
public String name; //姓名
public int score; //分数
public StudentNode next; //指向后一个节点
public StudentNode pre; //指向前一个节点,双链表的时候才有
public StudentNode(String name,int score) {
this.name = name;
this.score = score;
next = null;
pre = null;
}
@Override
public String toString() {
return "name: " + this.name + ", score: " + this.score;
}
}
1 单链表
head 节点:不存放具体数据,作用就是表示单链表头
1. 1 增加节点
在末尾插入节点其实没什么好说的,从头节点一直开始遍历,直到找到最后一个节点,然后将尾节点 next 指针指向新节点。
尾节点:temp.next == null
public void add(StudentNode student) {
StudentNode temp = head;
//next == null 就是尾节点
while (temp.next != null) {
temp = temp.next;
}
temp.next = student;
}
如果是在中间插入节点,比如说按照分数从高到低的顺序。因为单链表只有指向后一个节点的指针,所以我们需要找到插入位置的前一个节点,这样才能进行链表连接操作。
比如我们要插入的节点 a3 分数高于 a2,所以要插到 a2 之前,那么 a1 这个节点就是我们需要找到的,有如下两步操作:
- a3.next = a1.next,将新节点 a3 的 next 指向 a2,而 a2 = a1.next
- a1.next = a3,将 a1 的 next 指向 a3.
这两步顺序不能反,否则会丢失 a2 节点的访问
public void addByScore(StudentNode studentNode) {
StudentNode temp = head;
while (temp.next != null) {
//目标节点的分数大于后一个节点
if(temp.next.score < studentNode.score) {
break;
}
temp = temp.next;
}
//节点插入,先将目标节点的next指向temp.next
//再将 temp.next 指向目标节点
studentNode.next = temp.next;
temp.next = studentNode;
}
1. 2 删除节点
删除节点大概三步:
- delNode = a1.next,暂存 a3,不暂存的话,后面就找不到这个节点了。
- a1.next = a1.next.next,让 a1 的 next 指向 a2
- delNode.next = null,将 a3 的 next 置为 null,完全断开
public void del(String name) {
StudentNode temp = head;
while (temp.next != null) {
if (temp.next.name.equals(name)) {
break;
}
temp = temp.next;
}
if (temp.next != null) {
StudentNode delNode = temp.next;
temp.next = temp.next.next;
delNode.next = null;
System.out.println(name + "已被删除");
}else {
System.out.println(name + "不在链表中");
}
}
1.3 修改和查找
这两个其实没什么好说的,同增加和删除不同,这里因为不需处理跟前后节点的关系,所以只需要当前节点就行,我们在遍历的时候就让 StudentNode temp = head.next;
,不再记录前置节点的信息了。
2 双链表
2.1 增加节点
在末尾增加,和单链表类似,只是多了将新增节点的 pre 指向之前的尾节点的操作。
public void add(StudentNode studentNode) {
StudentNode temp = head;
while (temp.next != null) {
temp = temp.next;
}
temp.next = studentNode;
studentNode.pre = temp;
}
按分数插在中间,比单链表要简单,步骤如下
- a3.next = a2; a2.pre = a2.pre,这一步就处理了好了 a3 节点 pre 和 next 的值
- a2.pre.next = a3,将 a1 的 next 指向 a3;a2.pre = a3,将 a2 的 pre 指向 a3
代码中的 temp 就想当于 a2
public void addByScore(StudentNode studentNode) {
StudentNode temp = head.next;
while (temp != null) {
// 目标节点的分数大于当前节点
if(temp.score < studentNode.score) {
break;
}
temp = temp.next;
}
//节点插入
studentNode.next = temp;
studentNode.pre = temp.pre;
temp.pre.next = studentNode;
temp.pre = studentNode;
}
2.2 删除节点
删除 a3 节点,主要步骤如下:
- 先连结:a3.pre.next = a3.next; a3.next.pre = a3.pre,借助 a3 的信息将 a1、a2关联
- 断开a3:a3.next = null; a3.pre = null
public void del(String name) {
StudentNode temp = head.next;
while (temp != null) {
if (temp.name.equals(name)) {
break;
}
temp = temp.next;
}
if(temp != null) {
temp.pre.next = temp.next;
if (temp.next != null) {
temp.next.pre = temp.pre;
temp.next = null;
}
temp.pre = null;
System.out.println(name + "已被删除");
}else {
System.out.println(name + "不在链表中");
}
}
3 单向环形链表
环形链表的一个特点就是,尾节点的 next 指向第一个节点,形成一个环状的结构。我们定义第一个添加的节点为 first ,且只有一个节点时 first.next = first
。
这里主要围绕约瑟夫问题展开,每个节点只有 num 这一个属性,表示序号,方便问题求解。
class NumNode {
int num; //节点序号
NumNode next;
NumNode(int num) {
this.num = num;
this.next = null;
}
}
节点的操作在前面应该已经很熟悉了,这里主要讲讲约瑟夫问题怎么解。
对于如下的环形链表,first 指向 a1,