大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
一、什么是链表
链表是一种数据结构,跟数组不同,链表不需要连续的内存空间,而是通过指针将零散的内存块连接起来。
因此,链表的查找需要通过节点按顺序遍历,而增加与删除通过只需要操作指针指向,这也造成了相比数组,链表的查找性能消耗大而增加和删除消耗小的特点。
链表由节点组成,一般每个节点最少包含用于储存数据的data区和用于指向下一个节点的指针next,有的链表可能会有头结点或尾结点用于存储链表长度等信息。
二、链表的简单实现
先来实现一个简单的节点类:
/**
* @Author:huang
* @Date:2020-06-20 10:19
* @Description:节点类
*/
public class Node {
//节点序号
int num;
//数据
Object data;
//下一个节点
Node next;
public Node(int num, Object data) {
this.num = num;
this.data = data;
}
public int getNum() {
return num;
}
public Object getData() {
return data;
}
public void setNum(int num) {
this.num = num;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "Node{" +
"num=" + num +
", data=" + data +
'}';
}
}
1.插入节点
1.1不按照排序的插入
/**
* @Author:huang
* @Date:2020-06-20 10:19
* @Description:单链表
*/
public class SingleLinkList {
//默认初始化一个头节点
private Node head = new Node(0, "我是头结点");
/**
* 添加节点到链表
* @param node
*/
public void add(Node node) {
Node temp = head;
//遍历链表
while (true) {
if (temp.next == null) {
break;
}
//不是尾节点就继续遍历下一个节点
temp = temp.next;
}
//将尾节点指向即将插入的新节点
temp.next = node;
}
}
1.2按照排序插入
要想按顺序插入节点,需要确定节点的位置,也就是确定新节点的大小,以图为例:
遍历链表,我们把遍历到的节点叫A,A之前的节点叫A-1,A之后的节点叫A+1,要插入的节点叫B,有以下几种情况:
- 当A的序号大于B时,就把B插到A前,也就是原本的A和A-1之间
- 当A的序号等于B时,抛出异常禁止插入
- 当A的序号小于B时,就跳过这个节点,继续遍历,然后对比A+1和B的大小
- 当A就是链表最后一个节点时,A还是小于B,那就直接让B插到A后,变成链表的尾节点
按这个思路,我们在原来的类里新增一个方法:
/**
* 按顺序添加节点到链表
* @param node 要插入的节点
*/
public void addByOrder(Node node) {
Node temp = head;
//遍历链表
while (true) {
//如果链表到底了就直接插入
if (temp.next == null) {
temp.next = node;
return;
}else if (temp.next.num > node.num){
//如果当前节点比当要插入的节点大,就把新节点插到当前节点之前
node.next = temp.next;
temp.next = node;
return;
}else if (temp.next.num == node.num){
//如果有节点序号和要插入的节点重复就抛异常
throw new RuntimeException("插入节点与已有节点序号重复!");
}
//如果当前节点比当要插入的节点小,就继续遍历
temp = temp.next;
}
}
2.查找节点
/**
* 展示链表全部节点
*/
public void show() {
//判断链表是否为空
if (head.next == null) {
throw new RuntimeException("链表为空!");
}
Node temp = head.next;
//遍历链表
while (true) {
if (temp == null) {
break;
}
System.out.println(temp.toString());
temp = temp.next;
}
}
/**
* 根据序号获取节点
* @param num 要获取的节点序号
* @return
*/
public Node get(int num){
//判断链表是否为空
if (head.next == null) {
throw new RuntimeException("链表为空!");
}
Node temp = head.next;
//遍历链表
while (true) {
if (temp == null) {
throw new RuntimeException("编号为" + num + "的节点不存在!");
}
//如果是要获取的节点
if (temp.num == num) {
return temp;
}
temp = temp.next;
}
}
3.修改节点
修改节点与按顺序插入逻辑相似,需要先遍历并找到要修改的节点,然后用新节点去替换旧节点,或者干脆直接修改节点内的信息
/**
* 修改节点
* @param node 要更新的节点
*/
public void update(Node node) {
Node temp = head;
//判断链表是否为空
if (temp.next == null) {
throw new RuntimeException("链表为空!");
}
//获取要更新的节点序号
int nodeNum = node.num;
//遍历链表
while (true) {
//如果已经遍历完链表
if (temp == null) {
throw new RuntimeException("编号为" + temp.num + "的节点不存在!");
}
//如果找到了该节点
if (temp.num == nodeNum) {
temp.data = node.data;
return;
}
//继续遍历下一节点
temp = temp.next;
}
}
4.删除节点
因为在单向链表中,节点无法知道自己前一个节点的情况,以图为例:
如果我们想要删除节点A,那么就要找到A的前一个节点A-1,根据是否存在A后的节点A+1有以下几种情况:
-
A节点就是链表尾节点,此时只需让A-1节点的next指向null即可
nodeA-1.next = null
-
A节点后有A+1,此时让A-1节点指向A+1节点
nodeA-1.next = nodeA-1.next.next
按这个思路,在类里新加一个方法:
/**
* 删除节点
* @param num 要删除的节点编号
*/
public void delete(int num) {
Node temp = head;
//判断链表是否为空
if (temp.next == null) {
throw new RuntimeException("链表为空!");
}
//遍历链表
while (true) {
//如果链表到底了
if (temp.next == null) {
throw new RuntimeException("编号为" + num + "的节点不存在!");
}
//如果找到了待删除节点的前一个节点
if (temp.next.num == num) {
//判断待删除节点是否为尾节点
if (temp.next.next == null){
temp.next = null;
}else {
temp.next = temp.next.next;
}
return;
}
//继续遍历下一节点
temp = temp.next;
}
}
三、完整实现代码
/**
* @Author:huang
* @Date:2020-06-20 10:19
* @Description:单链表
*/
public class SingleLinkList {
private Node head = new Node(0, "我是头结点");
/**
* 添加节点到链表
* @param node 要插入的节点
*/
public void add(Node node) {
Node temp = head;
//遍历链表
while (true) {
if (temp.next == null) {
break;
}
//不是尾节点就继续遍历下一个节点
temp = temp.next;
}
//将尾节点指向即将插入的新节点
temp.next = node;
}
/**
* 展示链表
*/
public void show() {
//判断链表是否为空
if (head.next == null) {
throw new RuntimeException("链表为空!");
}
Node temp = head.next;
//遍历链表
while (true) {
if (temp == null) {
break;
}
System.out.println(temp.toString());
temp = temp.next;
}
}
/**
* 根据序号获取节点
* @param num 要获取的节点序号
* @return
*/
public Node get(int num){
//判断链表是否为空
if (head.next == null) {
throw new RuntimeException("链表为空!");
}
Node temp = head.next;
//遍历链表
while (true) {
if (temp == null) {
throw new RuntimeException("编号为" + num + "的节点不存在!");
}
if (temp.num == num) {
return temp;
}
temp = temp.next;
}
}
/**
* 按顺序添加节点到链表
* @param node 要插入的节点
*/
public void addByOrder(Node node) {
Node temp = head;
//遍历链表
while (true) {
//如果链表到底了就直接插入
if (temp.next == null) {
temp.next = node;
return;
}
else if (temp.next.num > node.num){
//如果后一节点比当新节点大,就插入当前节点
node.next = temp.next;
temp.next = node;
return;
}else if (temp.next.num == node.num){
//如果后一节点等于新节点,抛异常
throw new RuntimeException("插入节点与已有节点序号重复!");
}
//如果后一节点比当前节点小,就继续遍历
temp = temp.next;
}
}
/**
* 修改节点
* @param node 要更新的节点
*/
public void update(Node node) {
Node temp = head;
//判断链表是否为空
if (temp.next == null) {
throw new RuntimeException("链表为空!");
}
//获取要更新的节点序号
int nodeNum = node.num;
//遍历链表
while (true) {
//如果已经遍历完链表
if (temp == null) {
throw new RuntimeException("编号为" + temp.num + "的节点不存在!");
}
//如果找到了该节点
if (temp.num == nodeNum) {
temp.data = node.data;
return;
}
//继续遍历下一节点
temp = temp.next;
}
}
/**
* 删除节点
* @param num 要删除的节点编号
*/
public void delete(int num) {
Node temp = head;
//判断链表是否为空
if (temp.next == null) {
throw new RuntimeException("链表为空!");
}
//遍历链表
while (true) {
//如果链表到底了
if (temp.next == null) {
throw new RuntimeException("编号为" + num + "的节点不存在!");
}
//如果找到了待删除节点的前一个节点
if (temp.next.num == num) {
//判断待删除节点是否为尾节点
if (temp.next.next == null){
temp.next = null;
}else {
temp.next = temp.next.next;
}
return;
}
//继续遍历下一节点
temp = temp.next;
}
}
}
四、环形链表
循环链表是一种特殊单链表,他跟单链表的区别在于尾节点的尾指针指向了链表的头结点,也就是相当于将链表连接成了一个环形。
环形链表可以用于处理能描述为环形的数据,典型的比如约瑟夫问题。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170818.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...