java使用数组和链表实现栈和队列

2021-03-08 11:29

阅读:340

标签:sar   reads   his   alt   most   复杂   object   htm   base   

前言

栈(Stack)是一种后进先出的数据结构,仅允许在栈顶插入、删除、读取。队列(Queue)是一种先进先出的数据结构,队头读取、删除,队尾插入。
技术图片

使用数组实现栈

使用到的MyArrayList和MyLinkedList详情请查看 java实现一个自己的ArrayList和LinkedList

public interface Stack {

  /**
   * 栈是否为空
   */
  boolean isEmpty();

  /**
   * 栈顶添加元素
   */
  void push(E e);

  /**
   * 栈顶删除元素
   */
  E pop();

  /**
   * 查询栈顶元素
   */
  E peek();
}

定义栈的接口

/**
 * 使用数组实现栈
 */
public class ArrayStack implements Stack {

  /**
   * 代理对象
   */
  private List delegate;

  public ArrayStack(int capacity) {
    delegate = new MyArrayList(capacity);
  }

  public ArrayStack() {
    delegate = new MyArrayList();
  }

  @Override
  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  @Override
  public void push(E e) {
    delegate.add(e);
  }

  @Override
  public E pop() {
    return delegate.remove(delegate.size() - 1);
  }

  @Override
  public E peek() {
    return delegate.get(delegate.size() - 1);
  }

  @Override
  public String toString() {
    return delegate.toString();
  }
}

使用链表实现栈

/**
 * 使用链表实现栈
 */
public class LinkedStack implements Stack {

  private List delegate;

  public LinkedStack() {
    delegate = new MyLinkedList();
  }

  @Override
  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  @Override
  public void push(E e) {
    delegate.add(e);
  }

  @Override
  public E pop() {
    return delegate.remove(delegate.size() - 1);
  }

  @Override
  public E peek() {
    return delegate.get(delegate.size() - 1);
  }

  @Override
  public String toString() {
    return delegate.toString();
  }
}

使用数组实现队列

public interface Queue {

  /**
   * 队列是否为空
   */
  boolean isEmpty();

  /**
   * 入队
   */
  void enqueue(E e);

  /**
   * 出队
   */
  E dequeue();

  /**
   * 查询队头元素
   */
  E peek();
}

定义接口

/**
 * 使用数组实现队列
 */
public class ArrayQueue implements Queue {

  /**
   * 代理对象
   */
  private List delegate;

  public ArrayQueue(int capacity) {
    delegate = new MyArrayList(capacity);
  }

  public ArrayQueue() {
    delegate = new MyArrayList();
  }

  @Override
  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  @Override
  public void enqueue(E e) {
    delegate.add(e);
  }

  @Override
  public E dequeue() {
    return delegate.remove(0);
  }

  @Override
  public E peek() {
    return delegate.get(0);
  }


  @Override
  public String toString() {
    return delegate.toString();
  }
}

使用链表实现队列

/**
 * 使用链表实现队列
 */
public class LinkedQueue implements Queue {

  /**
   * 代理对象
   */
  private List delegate;

  public LinkedQueue() {
    delegate = new MyLinkedList();
  }

  @Override
  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  @Override
  public void enqueue(E e) {
    delegate.add(e);
  }

  @Override
  public E dequeue() {
    return delegate.remove(0);
  }

  @Override
  public E peek() {
    return delegate.get(0);
  }


  @Override
  public String toString() {
    return delegate.toString();
  }
}

使用数组实现循环队列

对于使用数组实现的队列来说,每次出队都需要将所有队头之后的元素往前移动一位,时间复杂度为O(n),这种情况我们可以使用循环队列来优化,
技术图片

使用两个指针表示队头(front)和队尾(rear),每次出队不移动元素,只移动队头指针。
为了区分队空和队满的情况,有两种处理方式,

  • 使用size字段表示实际容量
size == 0 表示队空
size == capacity 表示队满
  • 牺牲一个数组单元空间
front == tail 表示队空
(tail + 1) % capacity == front 表示队满
(tail - front + capacity) % capacity 为实际容量

第一种方式

/**
 * 使用数组实现循环队列
 */
public class LoopQueue implements Queue {

  /**
   * 数组容器
   */
  private Object[] data;
  /**
   * 队头和队尾指针
   */
  private int front, tail;
  /**
   * 队列实际容量
   */
  private int size;

  public LoopQueue(int capacity) {
    data = new Object[capacity];
    front = 0;
    tail = 0;
    size = 0;
  }

  public LoopQueue() {
    this(10);
  }

  private int capacity() {
    return data.length;
  }

  @Override
  public boolean isEmpty() {
    return size == 0;
  }

  @Override
  public void enqueue(E e) {
    int oldCapacity = capacity();
    if (size == oldCapacity) {
      //扩容1.5倍
      resize(oldCapacity + (oldCapacity >> 1));
    }
    data[tail] = e;
    tail = inc(tail + 1);
    size++;
  }

  @Override
  public E dequeue() {
    rangeCheck();
    E ret = front();
    data[front] = null;
    front = inc(front + 1);
    size--;
    int capacity = capacity();
    //在实际容量为总容量的4分之一时缩容
    if (size == (capacity >> 2) && (capacity >> 1) != 0) {
      //缩容2分之1
      resize(capacity >> 1);
    }
    return ret;
  }

  private E front() {
    return (E) data[front];
  }

  @Override
  public E peek() {
    rangeCheck();
    return front();
  }

  private void resize(int newCapacity) {
    Object[] newData = new Object[newCapacity];
    for (int i = 0; i 

第二种方式

/**
 * 使用数组实现循环队列
 */
public class LoopQueueWithoutSize implements Queue {

  /**
   * 数组容器
   */
  private Object[] data;
  /**
   * 队头和队尾指针
   */
  private int front, tail;

  public LoopQueueWithoutSize(int capacity) {
    /**
     * 在需要的容量上增加一个容量
     */
    data = new Object[capacity + 1];
    front = 0;
    tail = 0;
  }

  public LoopQueueWithoutSize() {
    this(10);
  }

  private int capacity() {
    return data.length - 1;
  }

  @Override
  public boolean isEmpty() {
    return size() == 0;
  }

  private int size() {
    return inc(tail + data.length - front);
  }

  @Override
  public void enqueue(E e) {
    int oldCapacity = capacity();
    if (size() == oldCapacity) {
      //扩容1.5倍
      resize(oldCapacity + (oldCapacity >> 1));
    }
    data[tail] = e;
    tail = inc(tail + 1);
  }

  @Override
  public E dequeue() {
    rangeCheck();
    E ret = front();
    data[front] = null;
    front = inc(front + 1);
    //在实际容量为总容量的4分之一时缩容
    int capacity = capacity();
    if (size() == (capacity >> 2) && (capacity >> 1) != 0) {
      //缩容2分之1
      resize(capacity >> 1);
    }
    return ret;
  }

  private E front() {
    return (E) data[front];
  }

  @Override
  public E peek() {
    rangeCheck();
    return front();
  }

  private void resize(int newCapacity) {
    Object[] newData = new Object[newCapacity + 1];
    int size = size();
    for (int i = 0; i 

jdk也提供了一个数组实现的循环队列ArrayDeque,就是使用第二种方式实现的

/**
 * Resizable-array implementation of the {@link Deque} interface.  Array
 * deques have no capacity restrictions; they grow as necessary to support
 * usage.  They are not thread-safe; in the absence of external
 * synchronization, they do not support concurrent access by multiple threads.
 * Null elements are prohibited.  This class is likely to be faster than
 * {@link Stack} when used as a stack, and faster than {@link LinkedList}
 * when used as a queue.
 *
 * 

Most {@code ArrayDeque} operations run in amortized constant time. * Exceptions include * {@link #remove(Object) remove}, * {@link #removeFirstOccurrence removeFirstOccurrence}, * {@link #removeLastOccurrence removeLastOccurrence}, * {@link #contains contains}, * {@link #iterator iterator.remove()}, * and the bulk operations, all of which run in linear time. * *

The iterators returned by this class‘s {@link #iterator() iterator} * method are fail-fast: If the deque is modified at any time after * the iterator is created, in any way except through the iterator‘s own * {@code remove} method, the iterator will generally throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the * future. * *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs. * *

This class and its iterator implement all of the * optional methods of the {@link Collection} and {@link * Iterator} interfaces. * *

This class is a member of the * * Java Collections Framework. * * @author Josh Bloch and Doug Lea * @param the type of elements held in this deque * @since 1.6 */ public class ArrayDeque extends AbstractCollection implements Deque, Cloneable, Serializable { /** * 数组容器 */ transient Object[] elements; /** * 队头指针 */ transient int head; /** * 队尾指针 */ transient int tail; /** * 队列实际容量 */ public int size() { return sub(tail, head, elements.length); } /** * 计算实际容量 */ static final int sub(int i, int j, int modulus) { if ((i -= j)

两种队列性能对比

我们简单对比一下我们自己实现的ArrayQueue和LoopQueue

import java.util.Random;

public class Main {

  // 测试使用运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
  private static double testQueue(Queue q, int opCount) {

    long startTime = System.nanoTime();

    Random random = new Random();
    for (int i = 0; i  arrayQueue = new ArrayQueue();
    double time1 = testQueue(arrayQueue, opCount);
    System.out.println("ArrayQueue, time: " + time1 + " s");

    Queue loopQueue = new LoopQueue();
    double time2 = testQueue(loopQueue, opCount);
    System.out.println("LoopQueue, time: " + time2 + " s");
  }
}

运行结果为

ArrayQueue, time: 0.520611 s
LoopQueue, time: 0.0196162 s

可以看到循环队列的优势还是很明显的。

java使用数组和链表实现栈和队列

标签:sar   reads   his   alt   most   复杂   object   htm   base   

原文地址:https://www.cnblogs.com/strongmore/p/14203303.html


评论


亲,登录后才可以留言!