双向链表
结构
static class ListNode{
private int val;//值
private ListNode prev;//前驱
private ListNode next;//后继
public ListNode(int val){
this.val=val;
}
}
public ListNode head;//双向链表的头节点
public ListNode last;//双向链表的尾节点
基本操作
头部插入
public void insertAtHead(int val) {
// 创建一个新的节点
ListNode newNode = new ListNode(val);
// 如果链表为空,即 last 也是头节点
if (head.next == null) {
head.next = newNode;
newNode.prev = head;
newNode.next = null;
last = newNode;
} else {
// 将新节点的后继指针指向头节点的后继节点
newNode.next = head.next;
// 将新节点的前驱指针指向头节点
newNode.prev = head;
// 将头节点的后继节点的前驱指针指向新节点
head.next.prev = newNode;
// 将头节点的后继指针指向新节点
head.next = newNode;
}
}
头部删除
public void deleteAtHead() {
// 检查链表是否为空(即头节点的下一个节点为 null)
if (head.next == null) {
System.out.println("List is empty. Nothing to delete at head.");
return;
}
// 获取要删除的节点,即头节点之后的第一个数据节点
ListNode nodeToDelete = head.next;
// 将头节点的 next 指向要删除节点的 next
head.next = nodeToDelete.next;
// 判断要删除的节点是否是最后一个数据节点
if (nodeToDelete.next == last) {
// 如果要删除的节点是最后一个数据节点,将 last 指向头节点
last.prev = head;
} else {
// 否则,将要删除的节点的 next 的 prev 指针指向头节点
nodeToDelete.next.prev = head;
}
// 清除要删除的节点的前后链接
nodeToDelete.prev = null;
nodeToDelete.next = null;
}
尾部插入
public void insertAtTail(int val) {
// 创建一个新的数据节点
ListNode newNode = new ListNode(val);
// 获取链表中最后一个数据节点
ListNode lastDataNode = last.prev;
// 将新节点的 prev 指针指向最后一个数据节点
newNode.prev = lastDataNode;
// 将新节点的 next 指针指向尾节点
newNode.next = last;
// 如果链表为空,即 lastDataNode 为 null
if (lastDataNode == null) {
// 链表是空的
head.next = newNode; // 将头节点的 next 指向新节点
} else {
// 链表不为空
lastDataNode.next = newNode; // 将最后一个数据节点的 next 指向新节点
}
// 将尾节点的 prev 指针指向新节点
last.prev = newNode;
}
尾部删除
public void deleteAtTail() {
// 检查链表是否为空
if (last.prev == null) {
System.out.println("List is empty. Nothing to delete at tail.");
return;
}
// 获取要删除的最后一个数据节点
ListNode nodeToDelete = last.prev;
// 将尾节点的 prev 指向要删除节点的前驱节点
last.prev = nodeToDelete.prev;
// 判断要删除节点的前驱是否存在
if (nodeToDelete.prev != null) {
// 如果前驱节点存在,将前驱节点的 next 指向尾节点
nodeToDelete.prev.next = last;
} else {
// 如果前驱节点不存在,说明链表只剩一个节点,更新 head.next
head.next = last;
}
// 清除要删除节点的前后链接
nodeToDelete.prev = null;
nodeToDelete.next = null;
}
指定位置插入
public void insertAtPosition(int val, int position) {
// 检查位置是否为有效位置(从1开始)
if (position < 1) {
System.out.println("Invalid position. Position should be >= 1.");
return;
}
// 创建一个新的数据节点
ListNode newNode = new ListNode(val);
// 从头节点开始进行遍历,找到插入位置
ListNode current = head;
int currentIndex = 0;
while (currentIndex < position - 1 && current.next != last) {
current = current.next;
currentIndex++;
}
// 检查是否已经达到指定位置
if (currentIndex != position - 1) {
System.out.println("Position out of bounds. List has fewer than " + position + " elements.");
return;
}
// 将新节点插入在当前位置
// 新节点的 prev 指向 current
newNode.prev = current;
// 新节点的 next 指向 current 的 next
newNode.next = current.next;
// 将 current 的 next.prev 指向新节点
if (current.next != last) {
current.next.prev = newNode;
} else {
// 如果插入在尾节点之前,更新 last 的 prev 指向新节点
last.prev = newNode;
}
// 将 current 的 next 指向新节点
current.next = newNode;
}
指定位置删除
public void deleteAtPosition(int position) {
// 检查位置是否为有效位置(从 1 开始)
if (position < 1) {
System.out.println("Invalid position. Position should be >= 1.");
return;
}
// 从头节点开始遍历,找到要删除的位置
ListNode current = head.next; // 从第一个数据节点开始
int currentIndex = 0;
while (currentIndex - 1 < position && current != last) {
current = current.next;
currentIndex++;
}
// 检查是否找到要删除的位置
if (current == last || currentIndex != position) {
System.out.println("Position out of bounds. List has fewer than " + position + " elements.");
return;
}
// 删除指定位置的节点
ListNode prevNode = current.prev;
ListNode nextNode = current.next;
// 将 prevNode 的 next 指向 nextNode
prevNode.next = nextNode;
// 将 nextNode 的 prev 指向 prevNode
if (nextNode != last) {
nextNode.prev = prevNode;
} else {
// 如果删除的是最后一个数据节点,更新 last.prev
last.prev = prevNode;
}
// 清除要删除节点的前后链接
current.prev = null;
current.next = null;
}
查找指定数据
public ListNode findNode(int targetValue) {
// 从头节点的下一个节点(第一个数据节点)开始遍历链表
ListNode current = head.next;
// 遍历链表,直到尾节点或找到目标值
while (current != last) {
// 检查当前节点的值是否与目标值匹配
if (current.val == targetValue) {
return current; // 找到匹配的节点,返回它
}
// 移动到下一个节点
current = current.next;
}
// 遍历完成未找到目标值,返回 null
return null;
}
计算链表大小
public int size() {
// 从头节点的下一个节点(第一个数据节点)开始遍历链表
ListNode current = head.next;
int count = 0;
// 遍历链表,直到尾节点
while (current != last) {
// 增加计数器
count++;
// 移动到下一个节点
current = current.next;
}
// 返回计数器的值,即链表的大小
return count;
}
销毁链表
public void destroyList() {
// 从头节点的下一个节点开始遍历链表
ListNode current = head.next;
// 遍历链表,直到尾节点
while (current != last) {
// 获取当前节点的下一个节点
ListNode nextNode = current.next;
// 清除当前节点的前后链接
current.prev = null;
current.next = null;
// 可选:帮助垃圾回收
current = null;
// 移动到下一个节点
current = nextNode;
}
// 重置链表的头节点和尾节点
head.next = last; // 将头节点的下一个节点指向尾节点
last.prev = head; // 将尾节点的前一个节点指向头节点
}
单向环形链表
应用场景
Josephu(约瑟夫、约瑟夫环)问题
设编号为1,2,...,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列位置,由此产生一个出对编号的序列。
代码实现
public class JosephusProblem {
public static int solve(int n, int k) {
// 检查输入参数的有效性
if (n <= 0 || k <= 0) {
throw new IllegalArgumentException("Both n and k must be greater than 0.");
}
// 创建循环单向链表
CircularLinkedList list = new CircularLinkedList();
// 将所有添加到循环单向链表中
for (int i = 1; i <= n; i++) {
list.add(i);
}
// 获取链表的头节点
CircularLinkedList.ListNode current = list.getHead();
CircularLinkedList.ListNode prevNode = list.getHead();
// 初始化 prevNode
while (prevNode.next != current) {
prevNode = prevNode.next;
}
// 模拟约瑟夫环问题
while (current.next != current) {
// 移动到要删除节点的前一个节点
for (int i = 1; i < k; i++) {
prevNode = current;
current = current.next;
}
// 删除当前节点
System.out.println("Removing node: " + current.val);
current = list.remove(prevNode, current);
}
// 返回剩下的最后一个节点的值
return current.val;
}
public static void main(String[] args) {
int n = 7; // 总数
int k = 3; // 每隔 k 个删除
int lastRemaining = solve(n, k);
System.out.println("Last remaining person at position: " + lastRemaining);
}
}
class CircularLinkedList {
// 内部类,表示链表节点
static class ListNode {
int val; // 节点的值
ListNode next; // 下一个节点的指针
public ListNode(int val) {
this.val = val;
}
}
private ListNode head; // 链表的头节点
private ListNode tail; // 链表的尾节点
// 构造一个空的循环单向链表
public CircularLinkedList() {
head = null;
tail = null;
}
// 向链表添加节点
public void add(int val) {
ListNode newNode = new ListNode(val);
if (head == null) {
head = newNode;
tail = newNode;
tail.next = head; // 形成循环
} else {
tail.next = newNode;
tail = newNode;
tail.next = head; // 形成循环
}
}
// 返回链表的头节点
public ListNode getHead() {
return head;
}
// 删除节点并返回删除后的节点
public ListNode remove(ListNode prevNode, ListNode currentNode) {
// 如果要删除的是链表的头节点
if (currentNode == head) {
head = head.next;
tail.next = head; // 更新循环
}
// 更新前一个节点的下一个节点
prevNode.next = currentNode.next;
// 如果要删除的是链表的尾节点
if (currentNode == tail) {
tail = prevNode; // 更新尾节点
}
// 清除当前节点的 next 指针
currentNode.next = null;
return prevNode.next;
}
}