1、使用队列实现栈的下列操作
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
思路:
先创建一个队列,用队列实现入栈操作
很简单,直接在队列的末尾增加元素就可以:
pop操作
移除栈顶元素相对来说比较复杂,在上面那个示例中,栈顶元素是5,在最右边,而出队列又只能在最左边进行,要移除5这个元素,我们首先得把前面的4个元素出队列,再尾插到队列中,这样一来5就在最左边,最后出掉队首元素,也就是移除了栈顶元素。
top操作
获取栈顶元素跟pop操作的过程基本是一样的,需要注意的是,我们查看了栈顶元素5之后,必须把5出队列,然后再尾插到队列中,恢复栈中数据原来的样子,避免出错。
empty操作
查看栈是否为空也就是看当前队列是否为空。
直接看代码:
class MyStack {
private LinkedList<Integer> queue;
/**
* Initialize your data structure here.
*/
public MyStack() {
this.queue = new LinkedList<Integer>();
}
/**
* Push element x onto stack.
*/
public void push(int x) {
this.queue.addLast(x);
}
/**
* Removes the element on top of the stack and returns that element.
*/
public int pop() {
//出栈的时候,把前面的数据(size-1)出栈再尾插到队尾,最后出掉最后一个元素
int size = this.queue.size();
for (int i = 0; i < size - 1; i++) {
int v = this.queue.get(0);
this.queue.remove(0);
this.queue.addLast(v);
}
int v = this.queue.get(0);
this.queue.remove(0);
return v;
}
/**
* Get the top element.
*/
public int top() {
int size = this.queue.size();
for (int i = 0; i < size - 1; i++) {
int v = this.queue.get(0);
this.queue.remove(0);
this.queue.addLast(v);
}
int v = this.queue.get(0);
this.queue.remove(0);
this.queue.addLast(v);
return v;
}
/**
* Returns whether the stack is empty.
*/
public boolean empty() {
return this.queue.size() == 0;
}
}
2、设计一个循环队列
要求:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
思路:
- enQueue(value)入队列,如果队列已满,返回false,否则的话将数据放入rear指向的位置,然后将rear引用指向下一个可用的位置,注意数组越界的情况,依然使用 (rear+1) % length ,最后把队列的大小+1。
代码如下:
public boolean enQueue(int value) {
if (this.size == this.array.length){
return false;
}
this.array[this.rear] = value;
this.rear = (this.rear+1)%this.array.length;
this.size++;
return true;
}
- deQueue()出队列,如果队列为空,返回false,否则的话将front引用指向下一个位置,还是要考虑数组越界的情况,最后将队列大小减1.
代码如下:
public boolean deQueue() {
if (this.size==0){
return false;
}
this.front = (this.front+1) % this.array.length;
this.size--;
return true;
}
- Front()获取队列的头并返回,如果队列为空,返回-1,否则返回front引用保存的数据。
代码如下:
public int Front() {
if (this.size==0){
return -1;
}
return this.array[this.front];
}
- Rear()获取队尾的元素,如果队列为空,返回-1,否则要去寻找队尾元素,注意,队尾元素并不是rear引用的指向。
通常情况下,rear-1的位置就是队尾元素,但是如果是下面这种特殊情况,直接减1会数组越界。
按照数学的思维,我们可以使用 (this.rear-1+this.array.length)%this.array.length 来处理这种特殊情况。
代码如下:
public int Rear() {
if (this.size==0){
return -1;
}
int index = (this.rear-1+this.array.length)%this.array.length;
return this.array[index];
}
还有一种简单的方法,观察上图可以看出,只有当rear==0的时候会出现特殊情况,而这时它的队尾元素在数组下标为7的位置,其他情况rear-1的位置就是队尾元素,所以我们可以采用三目运算符来处理这种特殊情况。
代码如下:
public int Rear2(){
if (this.size==0){
return -1;
}
int index = this.rear==0?this.array.length-1:this.rear-1;
return this.array[index];
}
- 检查队列是否为空,有两种方法,一种是直接判断队列的大小等不等于0,另一种是判断rear是否等于front。
代码如下:
public boolean isEmpty() {
return this.size == 0;
}
public boolean isEmpty2() {
return this.rear == this.front;
}
- 检查循环队列是否已满,通常情况下,我们认为只要rear+1==front,那么这个循环队列就是满的。而像下图这种极端情况,如果使用rear+1判断,会出现数组越界的情况。
那么要怎么处理呢?按照数学的思想,我们可以采用 (rear+1) % length 的思想,如果 (rear+1) % length == front,说明循环队列是满的,这样也不会有数组越界的情况。
代码如下:
public boolean isFull2(){
if ((this.rear+1)%this.array.length == this.front){
return true;
}
return false;
}
也可以直接使用队列大小与数组大小相比较来判断循环队列是否已满。
public boolean isFull() {
return this.size==this.array.length;
}
完整代码在我的Github上:
https://github.com/Nanfeng11/DataStructure/blob/master/queue/src/main/java/com/nanfeng/Interview.java