目录
- 单链表的存储结构
- 单链表:增
- 单链表:删
- 单链表:改
- 单链表:查
- 本文示例代码
单链表的存储结构
简介
- 单链表是一种链式存取的数据结构,是实现线性表方式之一
- 链表都包含:元素(data域)+指针(next域)
public static class LinkNode {
//data域 start --------
int num;
String name;
String nickName;
//data域 end --------
LinkNode next; // next域
//....
}
- 链表的存储地址有可能连续,也有可能非连续
链表的存储结构:
例:单链表存储方式:
head头节点.next -> A.next -> B.next ->C.next -> D.next
也有可能:
head头节点.next -> A.next -> C.next ->B.next -> D.next
链表虽说是线性表的一种,存储结构也是链式的,但是链表的实际存储过程中,有可能并非是连续存储的地址。
本文示例的结构组成
示例代码结构组成
public class LinkList {
// 表示头节点
LinkNode node;
public LinkList() {
node = new LinkNode(0, "", "");
}
/**
* 只有一种情况,add到链表尾部
*/
public void add(LinkNode addnode) {
}
/**
* 按照顺序插入节点,形成一个有序的链表
*/
public void addNum(LinkNode newNode) {
}
/**
* 修改
* @param num 对应链表中元素的num
* 场景:根据此num,修改对应链表中数据的nickName
*/
public void update(int num, LinkNode upNode) {
}
// 删除
public void del(int num) {
}
// 展示(遍历链表打印出来)
public void show() {
}
/**
* 链表内存结构:
* 内存地址 data域 next域
* next引用时指向的地址 当前节点存放的数据 用于指向下一个数据的内存地址
*/
public static class LinkNode {// 链表中的元素
int num; //示例含义: 排名
String name; // 示例含义: 名字
String nickName; //示例含义: 名字(艺名)
LinkNode next; // next域(指针)
public LinkNode(int num, String name, String nickName) {
this.num = num;
this.name = name;
this.nickName = nickName;
}
// .....
}
}
为了方便文章讲解,暂定一种链表
因为文字描述比较绕
这里暂定一种链表:
用A、B、C、D 表示原链表中的4个元素(关系:头节点 -> A.next -> B.next -> C.next- > D)
,E
表示当前要插入的元素
A、B、C、D 、E都是LinkNode
增
增的方式有两种情况:1. 一种是一直添加在链表最后
,2. 另外一种是根据指定的条件进行有序的插入节点
1. 添加在链表最后
核心思路
- 遍历链表找到最后一位元素
- 用最后一位元素的next域指向要插入的数据
- 遍历过程中,通过LinkNode = LinkNode.next 实现链表后移一位
代码实现:
// 头节点 :目的为了方便统一操作,头节点里的next也可以称为头指针
LinkNode node = new LinkNode(0, "", "");
public void add(LinkNode addnode) {
LinkNode n = node;
while (true) {
if (n.next == null) {//说明找到最后一个节点
break;
}
//链表向后移一位,继续查找
n = n.next;
}
//上面的循环会一直找到,n.next为null的情况,说明对应的node是最后一个
// 将最后一个node数据的next指向要添加的node
n.next = addnode;
}
图文分析:
2. 按照顺序节点插入
场景:
这一种插入,会根据用户所期望的某个条件进入节点插入。
例如:根据num(排名)这个字段进行节点插入
如:1 2 3 4 这样的插入,按照数字序号顺序
核心思路
-
遍历链表
- 链表为空,直接结束循环
- 当前链表位置是
A
,用A.next
指向的B
的num
,和当前要插入的E
的num
比较大小,如果B
的num(即:A.next指向的B)
大于插入的E
的num
,则说明当前要插入E
应该插入在B
的前面(后面也有图文解释),结束循环 - 要插入的
E
,在链表中存在,结束循环(本示例不作处理,只做个打印) - 没有满足上述三个条件的情况:通过
LinkNode = LinkNode.next
实现链表后移一位
-
插入链表
- 需要断开
A.next
指向的B
- 让
E.next
指向B
- 然后
A.next
指向E
- 需要断开
E.next = A.next;// A.next 就是B
A.next = E;
代码实现
/**
* 按照顺序插入节点,形成一个有序的链表
*/
public void addNum(LinkNode newNode) {
LinkNode n = node; //头节点
boolean isFind = false;
while (true) {
if (n.next == null) break; // 分析 1-->说明当前没有元素
if (n.next.num > newNode.num) { // 分析 2-->说明当前newNode是在n.next前面
break;
} else if (n.next.num == newNode.num) {
isFind = true;
//分析 3-->要添加的数据已存在
break;
}
//分析4--> 上述三个条件都不满足,向后位移,继续查找遍历
n = n.next;
}
if (isFind) {
System.out.println("要添加的数据已存在" + newNode);
} else {
// 分析5--> 插入链表
newNode.next = n.next;
n.next = newNode;
}
}
图文分析
分析2 -->
分析5 -->
最终实现:
这样最终就实现了按照顺序节点插入。
删
根据元素内的num字段进行删除
思路
-
遍历链表
- 链表为空结束循环
A.next即(B)的num
和当前要删除的元素的num
做对比,相等的话,表示B
就是要删除的元素- 没有满足上述二个条件的情况:通过
LinkNode = LinkNode.next
实现链表后移一位
-
删除元素
- 请看
分析4-->
- 请看
代码实现
public void del(int num) {
LinkNode n = node; // 表示头节点
while (true) {
if (n.next == null) break; //分析1--> 链表为空结束循环
if (n.next.num == num) { // 分析2--> 匹配到要删除的num的对应元素
/**
*分析4--> 例:A、B、C 、D 4个元素,假如C是要删除的元素
* B.next.num(即C.num) == num(要删除的num)
* B.next= B.next.next;//这一步就是删除C的操作
* 上一行解释:B.next即是C,B.next.next即是D,所以这里的意思B.next直接指向了D;
* B.next= B.next.next;相当于: B.next= D
* 这里C就变成一个没有被引用的对象,最后会被GC回收
*/
n.next = n.next.next;
break;
}
n = n.next; // 分析3--> 分析1、2 不满足条件,链表向后位移一位
}
}
图文分析
原链表:
移除C后链表:
C元素就处于游离状态,没有被引用了,最后会被GC回收掉。
改
/**
* @param num 对应链表中元素的num
* 场景:根据此num,修改对应链表中数据的nickName
*/
public void update(int num, LinkNode upNode) {
LinkNode n = node; // 头节点
while (true) {
if (n.next == null) break; // 分析1-->链表为空
if (n.next.num == upNode.num) { // 分析2 -->
n.next.nickName = upNode.nickName; // 分析4 --> 找到要修改的元素,直接修改即可
break;
}
n = n.next;// 分析3 --> 未找到对应num的元素,链表向后位移一位
}
}
查
public LinkNode query(int num) {
LinkNode n = node;
LinkNode returnNode = null;
while (true) {
if (n.next == null) break;
if (n.next.num == num) {
returnNode = n.next; // 找到匹配的赋值给returnNode,最终返回
break;
}
n = n.next;
}
return returnNode;
}
后续会总结一篇针对单链表的面试题。
欢迎大佬们指出不足,加以改进。Thanks!
如果对你有帮助,那就点个👍吧
本文示例代码
public class LinkList {
LinkNode node;
public LinkList() {
node = new LinkNode(0, "", "");
}
/**
* 只有一种情况,add到链表尾部
*/
public void add(LinkNode addnode) {
/**
* 需要遍历整个列表
* */
LinkNode n = node;
while (true) {
if (n.next == null) {
//说明找到最后一个节点
break;
}
// 将下一个Node赋值给当前node,即:链表向后移一位,继续遍历
n = n.next;
}
// 上面的循环会一直找到,n.next为null的情况,说明对应的node是最后一个
// 将最后一个node数据的next指向要添加的node
n.next = addnode;
}
/**
* 按照顺序插入节点,形成一个有序的链表
*/
public void addNum(LinkNode newNode) {
LinkNode n = node;
boolean isFind = false;
while (true) {
if (n.next == null) break;
if (n.next.num > newNode.num) { // 说明当前newNode是在n.next前面
break;
} else if (n.next.num == newNode.num) {
isFind = true;
// 要添加的数据已存在
break;
}
n = n.next;
}
if (isFind) {
System.out.println("要添加的数据已存在" + newNode);
} else {
//插入链表
newNode.next = n.next;
n.next = newNode;
}
}
/**
* @param num 对应链表中元素的num
* 场景:根据此num,修改对应链表中数据的nickName
*/
public void update(int num, LinkNode upNode) {
LinkNode n = node;
while (true) {
if (n.next == null) break;
if (n.next.num == upNode.num) {
n.next.nickName = upNode.nickName;
break;
}
n = n.next;
}
}
public void del(int num) {
LinkNode n = node;
while (true) {
if (n.next == null) break;
if (n.next.num == num) {
n.next = n.next.next;
break;
}
n = n.next;
}
}
public void show() {
if (node.next == null) {
System.out.println("链表为null");
return;
}
// 走到这里,说明链表至少存在一个数据
LinkNode l = node.next;
while (true) {
if (l == null) {
// 说明当前只有一个数据
break;
}
System.out.println(l);
l = l.next;// 链表后移
}
}
public LinkNode query(int num) {
LinkNode n = node;
LinkNode returnNode = null;
while (true) {
if (n.next == null) break;
if (n.next.num == num) {
returnNode = n.next;
break;
}
n = n.next;
}
return returnNode;
}
/**
* 链表内存结构:
* 内存地址 data域 next域
* next引用时指向的地址 当前节点存放的数据 用于指向下一个数据的内存地址
*/
public static class LinkNode {
int num;
String name;
String nickName;
LinkNode next;
public LinkNode(int num, String name, String nickName) {
this.num = num;
this.name = name;
this.nickName = nickName;
}
@Override
public String toString() {
return "LinkNode{" +
"num=" + num +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
}