基本数据结构之栈、队列及其数组实现

栈是一种“先进后出”的数据结构
在这里插入图片描述

用数组实现栈:

接口IStack:

public interface IStack {
    /**
     * 元素入栈(压栈)
     *
     * @param o
     * @return
     */
    boolean push(Object o) throws Exception;

    /**
     * 元素出栈(弹栈)
     *
     * @return
     */
    Object pop() throws Exception;

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

    /**
     * 栈是否满了
     *
     * @return
     */
    boolean isFull();

    /**
     * 获取栈顶元素
     *
     * @return
     */
    Object getPeek() throws Exception;

    /**
     * 获取元素在栈中的位置
     *
     * @param obj
     * @return
     */
    int getIndex(Object obj);

    /**
     * 获取栈的实际长度
     *
     * @return
     */
    int getSize();

    /**
     * 获取栈的容量
     *
     * @return
     */
    int getCapacity();

    /**
     * 打印栈
     */
    void print();

}

实现类StackImpl:

public class StackImpl implements IStack {
    public static void main(String[] args) throws Exception {}

    //用数组来模拟栈
    private Object[] stack;
    //栈的容量
    private int capacity;
    //栈顶指针
    private int top = -1;

    public StackImpl() {
        this(10);
    }

    public StackImpl(int initCapacity) {
        int capacity = initCapacity > 0 ? initCapacity : 10;
        this.capacity = capacity;
        stack = new Object[capacity];
    }

    @Override
    public boolean push(Object o) throws Exception {
        if (isFull()) {
            throw new Exception("栈已满,新元素 " + o + " 无法入栈!");
        }
        stack[++top] = o;
        return true;
    }

    @Override
    public Object pop() throws Exception {
        if (isEmpty()) {
            throw new Exception("栈为空,没有元素可以出栈!");
        }
        Object o = stack[top];
        stack[top--] = null;
        return o;
    }

    @Override
    public boolean isEmpty() {
        return top == -1;
    }

    @Override
    public boolean isFull() {
        return top == capacity - 1;
    }

    @Override
    public Object getPeek() throws Exception {
        if (isEmpty()) {
            throw new Exception("栈为空,无法获取栈顶元素!");
        }
        return stack[top];
    }

    @Override
    public int getIndex(Object obj) {
        for (int i = 0; i <= top; i++) {
            if (stack[i] == obj) {
                return i + 1;
            }
        }
        return -1;
    }

    @Override
    public int getSize() {
        return top + 1;
    }

    @Override
    public int getCapacity() {
        return capacity;
    }

    @Override
    public void print() {
        if (top == -1) {
            System.out.println("[]");
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i <= top; i++) {
                stringBuilder.append(i == 0 ? "[" : "")
                        .append(stack[i] != null ? stack[i] : "")
                        .append(i != top ? ", " : "]");
                System.out.print((stack[i] != null ? stack[i] : "") + (i != top ? ", " : "]"));
            }
            System.out.println(stringBuilder);
        }
    }
}

队列

  • 队列是一种“先进先出”的数据结构

  • 一般队列在初始时会有一个头指针和一个尾指针,他们都指向队列(数组)的头部(队首),当有元素入队时,尾指针后移;当有元素出队时,头指针后移。

顺序队列

顺序队列容易产生一个问题,即当队列满了时,此时头指针和尾指针都指向队尾,即便此时有元素出队给队列“腾出位置”,但是出队操作并不会改变尾指针的位置,所以即便此时所有元素都出了队,尾指针仍然在队尾的位置,新的元素仍然不能入队,这种问题又叫“假溢出”
在这里插入图片描述

用数组实现队列:

队列接口IQueue:

public interface IQueue {
    /**
     * 元素入队
     *
     * @param o
     * @return
     */
    boolean push(Object o) throws Exception;

    /**
     * 元素出队
     *
     * @return
     * @throws Exception
     */
    Object pop() throws Exception;

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

    /**
     * 队列是否已满
     *
     * @return
     */
    boolean isFull();

    /**
     * 获取队首元素
     *
     * @return
     * @throws Exception
     */
    Object getPeek() throws Exception;

    /**
     * 获取元素在队列中的位置
     *
     * @param obj
     * @return
     */
    int getIndex(Object obj);

    /**
     * 获取队列的实际长度
     *
     * @return
     */
    int getSize();

    /**
     * 获取队列的容量
     *
     * @return
     */
    int getCapacity();

    /**
     * 打印队列
     */
    void print();
}

队列接口实现类顺序队列SequentialQueueImpl:

public class SequentialQueueImpl implements IQueue {
    public static void main(String[] args) throws Exception {}

    //用数组来模拟队列
    private Object[] queue;
    //队首指针
    private int front = 0;
    //队尾指针
    private int rear = 0;
    //队列的容量
    private int capacity;

    public SequentialQueueImpl() {
        this(10);
    }

    public SequentialQueueImpl(int initCapacity) {
        this.capacity = initCapacity;
        queue = new Object[capacity];
    }

    @Override
    public boolean push(Object o) throws Exception {
        if (isFull()) {
            throw new Exception("队列已满,新元素 " + o + " 无法入队!");
        }
        queue[rear++] = o;
        return true;
    }

    @Override
    public Object pop() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,没有元素可以出队!");
        }
        Object o = queue[front];
        queue[front++] = null;
        return o;
    }

    @Override
    public boolean isEmpty() {
        return front == rear && (front == 0 || (queue[front - 1] == null));
    }

    @Override
    public boolean isFull() {
        return rear == capacity;
    }

    @Override
    public Object getPeek() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,无法获取队首元素!");
        }
        return queue[front];
    }

    @Override
    public int getIndex(Object obj) {
        for (int i = 0; i <= capacity; i++) {
            if (queue[i] == obj) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int getSize() {
        return rear - front;
    }

    @Override
    public int getCapacity() {
        return this.capacity;
    }

    @Override
    public void print() {
        if (getSize() == 0) {
            System.out.println("[]");
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("[");
            for (int i = front; i < rear; i++) {
                stringBuilder.append(queue[i]).append(i != rear - 1 ? ", " : "]");
            }
            System.out.println(stringBuilder);
        }
    }
}

上面这种顺序队列的“假溢出”现象:

SequentialQueueImpl sequentialQueue = new SequentialQueueImpl();
for (int i = 0; i < 10; i++) {
    sequentialQueue.push(i);
}
for (int i = 0; i < 10; i++) {
    sequentialQueue.pop();
}
sequentialQueue.print();
sequentialQueue.push(999);

在这里插入图片描述

“假溢出”现象的解决方案:

循环队列

循环队列通过将队列的首和尾连接起来,只要队列不是真正的满了,就不会溢出:

在这里插入图片描述

那么怎么判断循环队列是否满/空了呢?

循环队列的判满/判空的三种方法

法一

  • 使用一个默认值为0的flag标签,每当有元素入队时,flag=1;每当有元素出队时,flag=0
  • 在front==rear的前提下,如果flag为0,该循环队列为空队列;否则如果flag为1,该循环队列则为满队列

flag标记的循环队列的数组实现:

public class CircleQueueWithFlagImpl implements IQueue {
    public static void main(String[] args) throws Exception {}

    //用数组来模拟队列
    private Object[] queue;
    //队首指针
    int front = 0;
    //队尾指针
    int rear = 0;
    //队列的容量
    int capacity;
    //判满/空标签
    int flag = 0;

    public CircleQueueWithFlagImpl() {
        this(10);
    }

    public CircleQueueWithFlagImpl(int initCapacity) {
        this.capacity = initCapacity;
        queue = new Object[capacity];
    }

    @Override
    public boolean push(Object o) throws Exception {
        if (isFull()) {
            throw new Exception("队列已满,新元素 " + o + " 无法入队!");
        }
        queue[rear++ % capacity] = o;
        flag = 1;
        return true;
    }

    @Override
    public Object pop() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,没有元素可以出队!");
        }
        Object o = queue[front % capacity];
        queue[front++ % capacity] = null;
        flag = 0;
        return o;
    }

    @Override
    public boolean isEmpty() {
        return front == rear && flag == 0;
    }

    @Override
    public boolean isFull() {

        return rear - front == capacity && flag == 1;
    }

    @Override
    public Object getPeek() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,无法获取队首元素!");
        }
        return queue[front];
    }

    @Override
    public int getIndex(Object obj) {
        for (int i = 0; i <= capacity; i++) {
            if (queue[i] == obj) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int getSize() {
        return rear - front;
    }

    @Override
    public int getCapacity() {
        return this.capacity;
    }

    @Override
    public void print() {
        if (getSize() == 0) {
            System.out.println("[]");
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("[");
            for (int i = front; i < rear; i++) {
                stringBuilder.append(queue[i % capacity]).append(i != rear - 1 ? ", " : "]");
            }
            System.out.println(stringBuilder);
        }
    }
}

此时就不会碰到顺序队列可能会碰到的“假溢出”问题了:

CircleQueueWithFlagImpl circleQueueWithFlag = new CircleQueueWithFlagImpl();
for (int i = 0; i < 10; i++) {
    circleQueueWithFlag.push(i);
}
circleQueueWithFlag.print();
for (int i = 0; i < 10; i++) {
    circleQueueWithFlag.pop();
}
circleQueueWithFlag.print();
circleQueueWithFlag.push(999);
circleQueueWithFlag.print();

在这里插入图片描述

法二

  • 使用一个计数器count,每当有元素入队时,count+1;每当有元素出队时,count-1
  • 如果count==0,该循环队列为空队列;否则如果count=capacity,该循环队列则为满队列
public class CircleQueueWithCountImpl implements IQueue {
    public static void main(String[] args) throws Exception {}

    //用数组来模拟队列
    private Object[] queue;
    //队首指针
    private int front = 0;
    //队尾指针
    private int rear = 0;
    //队列的容量
    private int capacity;
    //队列计数器
    private int count = 0;

    public CircleQueueWithCountImpl() {
        this(10);
    }

    public CircleQueueWithCountImpl(int initCapacity) {
        this.capacity = initCapacity;
        queue = new Object[capacity];
    }

    @Override
    public boolean push(Object o) throws Exception {
        if (isFull()) {
            throw new Exception("队列已满,新元素 " + o + " 无法入队!");
        }
        queue[rear++ % capacity] = o;
        count++;
        return true;
    }

    @Override
    public Object pop() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,没有元素可以出队!");
        }
        Object o = queue[front % capacity];
        queue[front++ % capacity] = null;
        count--;
        return o;
    }

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

    @Override
    public boolean isFull() {
        return count == capacity;
    }

    @Override
    public Object getPeek() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,无法获取队首元素!");
        }
        return queue[front];
    }

    @Override
    public int getIndex(Object obj) {
        for (int i = 0; i <= capacity; i++) {
            if (queue[i] == obj) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int getSize() {
        return rear - front;
    }

    @Override
    public int getCapacity() {
        return this.capacity;
    }

    @Override
    public void print() {
        if (getSize() == 0) {
            System.out.println("[]");
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("[");
            for (int i = front; i < rear; i++) {
                stringBuilder.append(queue[i % capacity]).append(i != rear - 1 ? ", " : "]");
            }
            System.out.println(stringBuilder);
        }
    }
}

法二同时也可以解决“假溢出”问题:

CircleQueueWithCountImpl circleQueueWithCount = new CircleQueueWithCountImpl();
for (int i = 0; i < 10; i++) {
    circleQueueWithCount.push(i);
}
circleQueueWithCount.print();
for (int i = 0; i < 10; i++) {
    circleQueueWithCount.pop();
}
circleQueueWithCount.print();
circleQueueWithCount.push(999);
circleQueueWithCount.print();

在这里插入图片描述

法一和法二的原理是类似的

法三

  • 牺牲一个元素的位置用来判定
  • 如果front=rear,则该队列为空队列;否则如果(rear + 1) % capacity = front,则该队列为满队列

第三种方法和前两种方法的不同之处在于,第三种方法舍弃了一个元素的位置用来判定,但是没有使用额外的变量(flag、count之类的)来进行判定
在这里插入图片描述

public class CircleQueueWithSacrificeImpl implements IQueue {
    public static void main(String[] args) throws Exception {}

    //用数组来模拟队列
    private Object[] queue;
    //队首指针
    private int front = 0;
    //队尾指针
    private int rear = 0;
    //队列的容量
    private int capacity;

    public CircleQueueWithSacrificeImpl() {
        this(10);
    }

    public CircleQueueWithSacrificeImpl(int initCapacity) {
        this.capacity = initCapacity;
        queue = new Object[capacity];
    }

    @Override
    public boolean push(Object o) throws Exception {
        if (isFull()) {
            throw new Exception("队列已满,新元素 " + o + " 无法入队!");
        }
        queue[rear++ % capacity] = o;
        return true;
    }

    @Override
    public Object pop() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,没有元素可以出队!");
        }
        Object o = queue[front % capacity];
        queue[front++ % capacity] = null;
        return o;
    }

    @Override
    public boolean isEmpty() {
        return front == rear;
    }

    @Override
    public boolean isFull() {
        return (rear + 1) % capacity == front;
    }

    @Override
    public Object getPeek() throws Exception {
        if (isEmpty()) {
            throw new Exception("队列为空,无法获取队首元素!");
        }
        return queue[front];
    }

    @Override
    public int getIndex(Object obj) {
        for (int i = 0; i <= capacity; i++) {
            if (queue[i] == obj) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int getSize() {
        return rear - front;
    }

    @Override
    public int getCapacity() {
        return this.capacity;
    }

    @Override
    public void print() {
        System.out.println(front+"  "+rear);
        if (getSize() == 0) {
            System.out.println("[]");
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("[");
            for (int i = front; i < rear; i++) {
                stringBuilder.append(queue[i % capacity]).append(i != rear - 1 ? ", " : "]");
            }
            System.out.println(stringBuilder);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值