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");
    }
}

操作讲解

  1. 插入节点:使用 insertAtEnd 方法,我们将新节点插入链表末尾。首先找到一个空闲位置(即下一个可用的索引),并将数据插入。然后,我们通过遍历链表,找到末尾节点并将其 next 指向新节点。
  2. 删除节点:使用 deleteNode 方法,我们遍历链表找到指定值的节点。如果找到,修改前一个节点的 next 指针指向当前节点的下一个节点,从而删除当前节点。
  3. 打印链表:通过 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");
    }
}

操作讲解

  1. 插入节点:与单链表类似,通过 insertAtEnd 方法将节点插入到链表末尾。我们维护了 nextprev 数组,使得每个节点能够链接到前后节点。
  2. 删除节点:使用 deleteNode 方法,我们通过 prevnext 数组,调整节点的链接关系来删除指定节点。
  3. 打印链表:通过遍历 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();
    }
}

操作讲解

  1. 入栈:通过 push 方法,将元素添加到栈顶,并更新栈顶指针。
  2. 出栈:通过 pop 方法,移除并返回栈顶元素,同时更新栈顶指针。
  3. 查看栈顶元素:通过 peek 方法,返回栈顶元素,但不移除它。
  4. 打印栈:通过 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]);
    }
}

操作讲解

  1. 入队:通过 enqueue 方法,将元素添加到队列尾部,并更新尾指针。
  2. 出队:通过 dequeue 方法,移除并返回队列前端元素,同时更新前端指针。
  3. 查看队列前端元素:通过 peek 方法,返回队列前端元素,但不移除它。
  4. 打印队列:通过 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
    }
}

总结

本文详细介绍了如何使用数组来实现单链表、双链表、栈和队列,并提供了相关的常见操作实现。通过这些操作,您可以加深对数据结构的理解,并能够使用数组在一定场景下模拟这些常用数据结构。

posted @ 2025-03-07 15:26  咋还没来  阅读(90)  评论(0)    收藏  举报