链表
链表的概念
链表是一种物理存储单元上非连续、非顺序的存储结构。由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
头指针:是指向链表中第一个结点的指针
首元结点:是指链表中存储的第一个数据元素的结点
头结点:是在链表的首元结点之前附设的一个结点
链表的特点
- 不支持随机访问:访问链表只能通过头指针(即储存空间的首地址)进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,访问储存的所有数据
- 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
单链表
如下图:
链表的定义
根据面向对象的理论,在JAVA里规范的链表该这样定义
代码如下:
/**
* 在算法中最常用的链表定义方式
*/
public class Node {
public int val;
public Node next;
public Node(int val,Node next) {
this.val = val;
this.next = next;
}
}
遍历链表
对于单链表来说,一定都是从头节点开始逐个向后访问
如下图:
代码如下:
/**
* 遍历单链表方法1
**/
public void loop1(Consumer<Integer> consumer) {
Node cur = null;
cur = head;
while ( cur != null) {
consumer.accept(cur.var);
cur = cur.next; //将当前节点更新为下一节点
}
}
/**
* 遍历单链表方法2
**/
public void loop2(Consumer<Integer> consumer) {
Node cur = null;
for (cur = head; cur != null; cur = cur.next) {
consumer.accept(cur.var);
}
}
/**
* 遍历单链表方法3
**/
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node cur = head;
@Override
public boolean hasNext() { //是否有下一个元素
return cur != null;
}
@Override
public Integer next() { //返回当前值,指向下一个节点
int v = cur.var;
cur = cur.next;
return v;
}
};
}
链表插入
单链表插入需要考虑3中情况:分别是头部、中部、尾部
表头插入
创建一个新结点,将新节点的指针域指向head(表头节点) 即newNode.next=head。之后我们遍历就需要从newNode开始遍历,但是我们更习惯从head开始,所以我们在更新一下表头节点即head=newNode就行了。
如下图:
/**
* 表头插入
* @param val 待插入的值
**/
public void addFirst(int val) {
head = new Node(var, head);
}
表中间插入
在链表中间插入时,我们需要新遍历找到要插入的位置,找到它的前驱节点和后驱节点并插入。
例如下图中,我们需要在a2节点和a3节点中插入,给newNode接上两条线,为此我们需要得到a3前驱节点a2的指针域,将a2的指针域赋值给newNode即newNode.next = node(a2).next,然后再将a2的指针域连接到newNode上即node(a2).next=newNode就完成了,由于每个节点只有一个指针,a2连接a3的线将自动断开。
切记一定要先执行newNode.next = a2.next 然后再执行a2.next = newNode , 如果先执行a2.next = newNode 的话,那么就会找不到newNode的后继结点,也就是会造成结点的丢失。
代码如下:
/**
* 按索引找节点
**/
private Node findNode(int index) {
int i = 0; //索引值从0开始
for (Node cur = head; cur != null; cur = cur.next, i++) {
if(i == index){
return cur;
}
}
return null;
}
//异常
private IllegalArgumentException Illegalindex(int index) {
return new IllegalArgumentException(String.format("索引值 [%d] 不合法%n", index));
}
public void insert(int index,int val) {
if (index == 0) {
addFirst(val);
return;
}
Node prev = findNode(index - 1); //上一节点
if (prev == null) { //找不到
throw Illegalindex(index);
}
Node newNode = new Node(val, prev.next);
prev.next = newNode;
}
}
表尾插入
首先我们需要遍历找到最后一个节点,再将最后一个节点an的指针域连接newNode就行了即node(an).next=newNode。
如下图:
代码如下:
/**
* 遍历找到最后一个节点
**/
private Node findLast() {
if (head == null) { //判断是否为空链表
return null;
}
Node cur = null;
cur = head;
while (cur.next != null) {
cur = cur.next; //更新当前节点为下一节点
}
return cur;
}
/**
* 表尾插入
**/
public void addLast(int val) {
Node last = findLast(); //赋值为最后一个节点
if (last == null) {
addFirst(val); //使用表头插入的逻辑
return;
}
last.next = new Node(val,null);
}
链表删除
删除同样分为:删除表头节点、删除中间节点和尾部节点
表头删除
表头删除,只需要将头节点(head)向后移动一次,原来的节点将被jvm回收。
代码如下:
/**
* 删除头结点
**/
public void removeFirst() {
if (head == null) {
throw Illegalindex(0);
}
head = head.next;
}
中间节点删除
删除中间节点时,也要用cur.next来比较,例如将节点a3删除,需要将a2的指针域更新为a4的数据域即cur.next=cur.next.next
如下图:
代码如下:
/**
* 按索引删除结点
**/
public void remove(int index) {
if (index == 0) {
removeFirst();
return;
}
Node prev = findNode(index - 1); //上一节点
if (prev == null) {
throw Illegalindex(0);
}
Node remove = prev.next; // 被删节点
if (remove == null) {
throw Illegalindex(0);
}
prev.next=remove.next;
}
表尾删除
表尾删除,将尾节点的前驱节点的指针域变为空就行即cur.next=null
如下图:
完整代码:
import java.util.Iterator;
import java.util.function.Consumer;
/**
* @ClassName SinglyLinkedList
* @Description TODO
* @Author lxy23
* @Date 2023/7/20 14:08
* @Version 1.0
*/
public class SinglyLinkedList implements Iterable<Integer> { //整体
private Node head = null; //表头
//定义节点类
private static class Node {
int val; //数据域:存放数据
Node next; //指针域:指向下一个节点
public Node(int val, Node next){
this.val = val;
this.next = next;
}
}
/**
* 表头插入
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 14:36
* @param val 待插入的值
**/
public void addFirst(int val) {
//创建应该新节点newNode.next=head head=newNode
head = new Node(val, head);
}
/**
* 遍历单链表方法1
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 14:44
* @return
**/
public void loop1(Consumer<Integer> consumer) {
Node cur = null;
cur = head;
while ( cur != null) {
consumer.accept(cur.val);
cur = cur.next; //将当前节点更新为下一节点
}
}
/**
* 遍历单链表方法2
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 14:44
* @return
**/
public void loop2(Consumer<Integer> consumer) {
Node cur = null;
for (cur = head; cur != null; cur = cur.next) {
consumer.accept(cur.val);
}
}
/**
* 遍历单链表方法3
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 14:44
* @return
**/
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
Node cur = head;
@Override
public boolean hasNext() { //是否有下一个元素
return cur != null;
}
@Override
public Integer next() { //返回当前值,指向下一个节点
int v = cur.val;
cur = cur.next;
return v;
}
};
}
/**
* 遍历找到最后一个节点
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 15:55
* @return unit3_链表就这些题.单链表.SinglyLinkedList.Node
**/
private Node findLast() {
if (head == null) { //判断是否为空链表
return null;
}
Node cur = null;
cur = head;
while (cur.next != null) {
cur = cur.next; //更新当前节点为下一节点
}
return cur;
}
/**
* 表尾插入
* @author: lxy23
* @description:
* @Date: 2023/7/20 16:44
* @param val
**/
public void addLast(int val) {
Node last = findLast(); //赋值为最后一个节点
if (last == null) {
addFirst(val); //使用表头插入的逻辑
return;
}
last.next = new Node(val,null);
}
/**
* 找到需要插入位置的节点d的方法
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 17:13
* @param index
* @return unit3_链表就这些题.单链表.SinglyLinkedList.Node
**/
private Node findNode(int index) {
int i = 0; //索引值从0开始
for (Node cur = head; cur != null; cur = cur.next, i++) {
if(i == index){
return cur;
}
}
return null;
}
/**
* 取得该索引值位置节点的值
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 17:37
* @param index
* @return int
**/
public int get(int index) {
Node cur = findNode(index);
if (cur == null) { //没找到抛出异常
throw Illegalindex(index);
}
return cur.val;
}
private IllegalArgumentException Illegalindex(int index) {
return new IllegalArgumentException(String.format("索引值 [%d] 不合法%n", index));
}
public void insert(int index,int val) {
if (index == 0) {
addFirst(val);
return;
}
Node prev = findNode(index - 1); //上一节点
if (prev == null) { //找不到
throw Illegalindex(index);
}
Node newNode = new Node(val, prev.next);
prev.next = newNode;
}
/**
* 删除头结点
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 18:05
**/
public void removeFirst() {
if (head == null) {
throw Illegalindex(0);
}
head = head.next;
}
/**
* 按索引删除结点
* @author: lxy23
* @description: TODO
* @Date: 2023/7/20 18:05
**/
public void remove(int index) {
if (index == 0) {
removeFirst();
return;
}
Node prev = findNode(index - 1); //上一节点
if (prev == null) {
throw Illegalindex(0);
}
Node remove = prev.next; // 被删节点
if (remove == null) {
throw Illegalindex(0);
}
prev.next=remove.next;
}
}