Java 数组实现单链表、双链表、栈与队列
使用 Java 数组实现单链表、双链表、栈与队列及常见操作的详细讲解
数据结构是计算机科学中的基础,链表、栈、队列等数据结构在日常编程中都经常使用。虽然链表通常使用指针来表示,但是我们可以使用数组来模拟这些数据结构。在本文中,我们将详细讲解如何使用数组实现单链表、双链表、栈和队列,以及它们的常见操作。
1. 用数组实现单链表
单链表是一种线性数据结构,每个节点包含一个数据部分和指向下一个节点的指针。在数组实现的单链表中,我们使用两个数组来模拟链表的行为:
data[]:存储节点的数据。next[]:存储每个节点的“指针”,即指向下一个节点的数组索引。
单链表的实现
class ArraySingleLinkedList {
private int[] data; // 存储节点数据
private int[] next; // 存储每个节点的 next 指针(下一个节点的索引)
private int size; // 当前链表的大小
private int head; // 链表的头节点索引
// 构造函数,初始化链表
public ArraySingleLinkedList(int capacity) {
data = new int[capacity]; // 用一个固定大小的数组来存储数据
next = new int[capacity]; // 用一个数组来模拟链表的 next 指针
size = 0;
head = -1; // 初始化链表为空
for (int i = 0; i < capacity; i++) {
next[i] = -1; // 初始化所有节点的 next 为 -1,表示没有连接
}
}
// 插入一个节点到链表末尾
public void insertAtEnd(int value) {
if (size == data.length) {
System.out.println("链表已满,无法插入新元素");
return;
}
// 找到空闲位置
int index = size;
data[index] = value; // 将数据插入到链表的空位置
next[index] = -1; // 新节点的 next 指向 null(-1)
// 将前一个节点的 next 指向新节点
if (head == -1) {
head = index; // 如果链表为空,头节点指向第一个插入的节点
} else {
int current = head;
while (next[current] != -1) {
current = next[current]; // 遍历到链表末尾
}
next[current] = index; // 修改末尾节点的 next 指向新节点
}
size++;
}
// 删除指定值的节点
public void deleteNode(int value) {
int current = head;
int prev = -1;
// 遍历链表,找到指定值的节点
while (current != -1 && data[current] != value) {
prev = current;
current = next[current];
}
if (current == -1) {
System.out.println("未找到值为 " + value + " 的节点");
return;
}
// 如果删除的是头节点
if (prev == -1) {
head = next[current];
} else {
next[prev] = next[current]; // 修改前一个节点的 next 指向当前节点的下一个节点
}
next[current] = -1; // 将删除的节点的 next 设置为 -1,表示已经不再使用
size--;
}
// 打印链表
public void printList() {
int current = head;
while (current != -1) {
System.out.print(data[current] + " -> ");
current = next[current];
}
System.out.println("null");
}
}
操作讲解
- 插入节点:使用
insertAtEnd方法,我们将新节点插入链表末尾。首先找到一个空闲位置(即下一个可用的索引),并将数据插入。然后,我们通过遍历链表,找到末尾节点并将其next指向新节点。 - 删除节点:使用
deleteNode方法,我们遍历链表找到指定值的节点。如果找到,修改前一个节点的next指针指向当前节点的下一个节点,从而删除当前节点。 - 打印链表:通过
printList方法,遍历链表并输出节点的数据。
示例执行
public class Main {
public static void main(String[] args) {
ArraySingleLinkedList list = new ArraySingleLinkedList(10);
list.insertAtEnd(10);
list.insertAtEnd(20);
list.insertAtEnd(30);
list.printList(); // 输出: 10 -> 20 -> 30 -> null
list.deleteNode(20);
list.printList(); // 输出: 10 -> 30 -> null
}
}
2. 用数组实现双链表
双链表是一种链式数据结构,每个节点包含数据部分、指向下一个节点的 next 指针和指向前一个节点的 prev 指针。在数组实现的双链表中,我们使用三个数组来模拟链表的行为:
data[]:存储节点的数据。next[]:存储每个节点的next指针,指向下一个节点的数组索引。prev[]:存储每个节点的prev指针,指向前一个节点的数组索引。
双链表的实现
class ArrayDoubleLinkedList {
private int[] data; // 存储节点数据
private int[] next; // 存储每个节点的 next 指针
private int[] prev; // 存储每个节点的 prev 指针
private int size; // 当前链表的大小
private int head; // 链表的头节点索引
private int tail; // 链表的尾节点索引
// 构造函数,初始化链表
public ArrayDoubleLinkedList(int capacity) {
data = new int[capacity];
next = new int[capacity];
prev = new int[capacity];
size = 0;
head = -1;
tail = -1;
for (int i = 0; i < capacity; i++) {
next[i] = -1;
prev[i] = -1;
}
}
// 插入节点到链表末尾
public void insertAtEnd(int value) {
if (size == data.length) {
System.out.println("链表已满,无法插入新元素");
return;
}
int index = size; // 使用当前的 size 作为新节点的位置
data[index] = value;
next[index] = -1;
prev[index] = tail;
if (head == -1) {
head = index;
} else {
next[tail] = index;
}
tail = index;
size++;
}
// 删除指定值的节点
public void deleteNode(int value) {
int current = head;
while (current != -1 && data[current] != value) {
current = next[current];
}
if (current == -1) {
System.out.println("未找到值为 " + value + " 的节点");
return;
}
if (prev[current] != -1) {
next[prev[current]] = next[current];
} else {
head = next[current];
}
if (next[current] != -1) {
prev[next[current]] = prev[current];
} else {
tail = prev[current];
}
next[current] = -1;
prev[current] = -1;
size--;
}
// 打印链表
public void printList() {
int current = head;
while (current != -1) {
System.out.print(data[current] + " <-> ");
current = next[current];
}
System.out.println("null");
}
}
操作讲解
- 插入节点:与单链表类似,通过
insertAtEnd方法将节点插入到链表末尾。我们维护了next和prev数组,使得每个节点能够链接到前后节点。 - 删除节点:使用
deleteNode方法,我们通过prev和next数组,调整节点的链接关系来删除指定节点。 - 打印链表:通过遍历
next数组打印每个节点的值。
示例执行
public class Main {
public static void main(String[] args) {
ArrayDoubleLinkedList list = new ArrayDoubleLinkedList(10);
list.insertAtEnd(10);
list.insertAtEnd(20);
list.insertAtEnd(30);
list.printList(); // 输出: 10 <-> 20 <-> 30 <-> null
list.deleteNode(20);
list.printList(); // 输出: 10 <-> 30 <-> null
}
}
3. 用数组实现栈
栈(Stack)是一种遵循先进后出(LIFO, Last In First Out)原则的数据结构。可以使用数组来模拟栈的行为,栈只有一个指针——栈顶指针。
栈的实现
class ArrayStack {
private int[] data; // 存储栈元素的数组
private int top; // 栈顶指针,指向栈顶元素
// 构造函数,初始化栈
public ArrayStack(int capacity) {
data = new int[capacity];
top = -1; // 栈为空
}
// 判断栈是否为空
public boolean isEmpty() {
return top == -1;
}
// 入栈
public void push(int value) {
if (top == data.length - 1) {
System.out.println("栈已满,无法插入新元素");
return;
}
data[++top] = value;
}
// 出栈
public int pop() {
if (isEmpty()) {
System.out.println("栈为空,无法出栈");
return -1;
}
return data[top--];
}
// 查看栈顶元素
public int peek() {
if (isEmpty()) {
System.out.println("栈为空");
return -1;
}
return data[top];
}
// 打印栈
public void printStack() {
if (isEmpty()) {
System.out.println("栈为空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.print(data[i] + " ");
}
System.out.println();
}
}
操作讲解
- 入栈:通过
push方法,将元素添加到栈顶,并更新栈顶指针。 - 出栈:通过
pop方法,移除并返回栈顶元素,同时更新栈顶指针。 - 查看栈顶元素:通过
peek方法,返回栈顶元素,但不移除它。 - 打印栈:通过
printStack方法,从栈顶到底部打印所有元素。
示例执行
public class Main {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(5);
stack.push(10);
stack.push(20);
stack.push(30);
stack.printStack(); // 输出: 30 20 10
System.out.println("栈顶元素: " + stack.peek()); // 输出: 栈顶元素: 30
System.out.println("出栈: " + stack.pop()); // 输出: 出栈: 30
stack.printStack(); // 输出: 20 10
}
}
4. 用数组实现队列
队列(Queue)是一种遵循先进先出(FIFO, First In First Out)原则的数据结构。我们可以使用数组来模拟队列,队列维护两个指针:front(队列前端)和 rear(队列尾部)。
队列的实现
class ArrayQueue {
private int[] data; // 存储队列元素的数组
private int front; // 队列的前端
private int rear; // 队列的尾端
private int size; // 队列的大小
// 构造函数,初始化队列
public ArrayQueue(int capacity) {
data = new int[capacity];
front = 0;
rear = -1;
size = 0;
}
// 判断队列是否为空
public boolean isEmpty() {
return size == 0;
}
// 入队
public void enqueue(int value) {
if (size == data.length) {
System.out.println("队列已满,无法入队");
return;
}
rear = (rear + 1) % data.length;
data[rear] = value;
size++;
}
// 出队
public int dequeue() {
if (isEmpty()) {
System.out.println("队列为空,无法出队");
return -1;
}
int value = data[front];
front = (front + 1) % data.length;
size--;
return value;
}
// 查看队列前端元素
public int peek() {
if (isEmpty()) {
System.out.println("队列为空");
return -1;
}
return data[front];
}
// 打印队列
public void printQueue() {
if (isEmpty()) {
System.out.println("队列为空");
return;
}
for (int i = front; i != rear; i = (i + 1) % data.length) {
System.out.print(data[i] + " ");
}
System.out.println(data[rear]);
}
}
操作讲解
- 入队:通过
enqueue方法,将元素添加到队列尾部,并更新尾指针。 - 出队:通过
dequeue方法,移除并返回队列前端元素,同时更新前端指针。 - 查看队列前端元素:通过
peek方法,返回队列前端元素,但不移除它。 - 打印队列:通过
printQueue方法,从队列前端到尾部打印所有元素。
示例执行
public class Main {
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(5);
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
queue.printQueue(); // 输出: 10 20 30
System.out.println("队列前端元素: " + queue.peek()); // 输出: 队列前端元素: 10
System.out.println("出队: " + queue.dequeue()); // 输出: 出队: 10
queue.printQueue(); // 输出: 20 30
}
}
总结
本文详细介绍了如何使用数组来实现单链表、双链表、栈和队列,并提供了相关的常见操作实现。通过这些操作,您可以加深对数据结构的理解,并能够使用数组在一定场景下模拟这些常用数据结构。

浙公网安备 33010602011771号