链表通常指的是单向链表,他包含多个节点,每个节点都有一个纸箱下一个几点的next指针。表中最后一个节点指向NULL,表示链表的结束。
下面是一个基于Java 声明的链表节点
public class Node<T extends Serializable> {
// 节点数据
private T data;
// 指向的Next 节点
private Node<T> next;
}
- 遍历链表
- 在链表中插入一个元素
- 在链表中删除一个元素
1.1 链表的遍历
假设表头节点是指向链表的第一个节点,完成链表的遍历只需要执行以下步骤。
- 沿指针遍历
- 显示当前指针所指向的内容
- 将当前节点指向 Next 节点
- 当 Next 节点指向NULL的时候遍历结束
public static <T extends Serializable> void linkedListForeach(Node<T> headNode) {
Node<T> currentNode = headNode;
// 4. 遍历终止条件,Next指向NULL
while (currentNode != null) {
// 2.输出当前节点的数据
System.out.println(currentNode.getData() + "->");
// 3. 将当前节点指向Next 节点
currentNode = currentNode.getNext();
}
}
根据同样的逻辑,也可以根据此实现链表长度的计算。
private static <T extends Serializable> int length(Node<T> headNode) {
Node<T> currentNode = headNode;
int count = 0;
while (currentNode != null) {
count++;
currentNode = currentNode.getNext();
}
return count;
}
1.2 链表插入元素
链表中插入元素分为三种,分别是在链表头部插入元素,在链表中间插入元素以及在链表尾部插入元素。需要注意的这里的链表的位置指的在p位置插入元素,在插入完成之后,链表的p位置指向的是新的节点。
1.2.1 在链表的头部插入新的节点
其逻辑步骤是:
- 首先将新的节点的指针指向表头节点
- 将表头节点指向新的节点
1.2.2 在链表尾部插入节点
在链表尾部插入新的节点也非常简单,需要将新的节点的NEXT 指针指向NULL,然后将原链表的尾指针执行新节点,即可。
1.2.3 在链表中间插入节点
- 首先定位到需要插入的节点位置,断开其 Next 节点,注意需要保存 Next 节点的引用
- 将当前节点的 Next 指针执行新的节点
- 将新节点的 Next 指针指向 Next 节点
1.2.4 源码示例
// 时间复杂度 O(n) 空间复杂度 O(1)
private static <T extends Serializable> Node<T> insert(
Node<T> headNode, Node<T> insertNode, int position) {
// 判断长度,插入的位置是否合法
int length = length(headNode);
if (position <= 0 || position > length) {
throw new IllegalArgumentException();
}
// 如果是链表头部,直接指向Next节点
if (position == 1) {
insertNode.setNext(headNode);
return insertNode;
}
// 如果不是头部,那么获取到当前插入位置的节点
Node<T> currentNode = findByPosition(headNode, position - 1);
if (currentNode == null) {
return headNode;
}
// 保存当前插入位置节点的Next引用
Node<T> next = currentNode.getNext();
// 当前节点的Next 指向新的节点
currentNode.setNext(insertNode);
// 新的节点的Next 指向Next节点
insertNode.setNext(next);
return headNode;
}
1.3 链表删除元素
同插入类似,链表删除也分为三种情况,分别是在删除链表头部元素,删除链表中间元素以及在删除链表尾部元素。
1.3.1 删除链表头部节点
删除头部节点元素非常简单,只需要将原头部节点的NEXT指向NULL,然后将表头指针指向原头结点的Next节点即可。
1.3.2 删除链表尾部节点
删除尾部节点也非常简单,只要将尾部节点的前驱的Next节点设置为NULL,即可
1.3.3 删除链表中间的节点
删除链表的中间节点的步骤是:
- 定位当前节点为需要删除节点的前驱位置,当前节点的后驱就是即将删除的节点
- 将删除节点的Next 指向NULL
- 将当前节点的Next指针执行将要删除的节点的Next节点。
1.3.4 代码示例
// 时间复杂度 O(n) 空间复杂度 O(1)
private static <T extends Serializable> Node<T> remove(Node<T> headNode, int position) {
int length = length(headNode);
if (position <= 0 || position > length) {
throw new IllegalArgumentException();
}
// 如果是删除头部节点,直接返回头部节点的Next
if (position == 1) {
headNode.setNext(null);
return headNode.getNext();
}
// 定位到删除节点的前驱节点,以及即将删除的节点
Node<T> preNode = findByPosition(headNode, position - 1);
Node<T> currentNode = findByPosition(headNode, position);
// 如果前驱节点存在,则将前驱节点的Next设置为删除节点的Next
if (preNode != null) {
Node<T> next = Objects.isNull(currentNode) ? null : currentNode.getNext();
preNode.setNext(next);
}
return headNode;
}