先入先出的数据结构
在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素。
如上图所示,队列是典型的 FIFO 数据结构。插入(insert)操作也称作入队(enqueue),新元素始终被添加在队列的末尾。 删除(delete)操作也被称为出队(dequeue)。 你只能移除 第一个元素。
队列–实现
- 队列的初始化: 用一个动态数组与一个位置指针
- 队列的两个主要操作:入队与出队
// store elements
private List<Integer> data;
// a pointer to indicate the start position
private int p_start;
public MyQueue() {
data = new ArrayList<Integer>();
p_start = 0;
}
/** Insert an element into the queue. Return true if the operation is successful. */
public boolean enQueue(int x) {
data.add(x);
return true;
};
/** Delete an element from the queue. Return true if the operation is successful. */
public boolean deQueue() {
if (isEmpty() == true) {
return false;
}
p_start++;
return true;
}
/** Get the front item from the queue. */
public int Front() {
return data.get(p_start);
}
/** Checks whether the queue is empty or not. */
public boolean isEmpty() {
return p_start >= data.size();
}
缺点
动态数组的指针不断移动,空间越来越大,导致资源浪费。
循环队列
更有效的方法是使用循环队列。 具体来说,我们可以使用固定大小的数组和两个指针来指示起始位置和结束位置。 目的是重用我们之前提到的被浪费的存储。
比如:先创建一个固定长度为5的数组,入队五个元素,此时队列满
再进行连续两次出队操作
此时,执行一次入队操作,从队列开头入队元素,合理运用空间。
循环队列的实现
在循环队列中,我们使用一个数组和两个指针(front 和 rear)。 front 表示队列的起始位置,rear 表示队列的结束位置。
除了最重要的入队(enQueue)和出队(deQueue),还添加了获取首尾元素值以及判断空/满的方法。
注意:在入队出队时,指针的操作rear=(rear+1)%MAXSIZE; / front=(front+1)%MAXSIZE;
```java
class MyCircularQueue {
private int MAXSIZE;
private int arr[];
private int front;
private int rear;
private boolean flag;
/** Initialize your data structure here. Set the size of the queue to be k. */
public MyCircularQueue(int k) {
MAXSIZE=k;
arr= new int[MAXSIZE];
front=0;
rear=0;
flag=false;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
public boolean enQueue(int value) {
if(isFull()==true)
return false;
arr[rear]=value;
rear=(rear+1)%MAXSIZE;
flag=true;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
public boolean deQueue() {
if(isEmpty()==true)
return false;
front=(front+1)%MAXSIZE;
flag=false;
return true;
}
/** Get the front item from the queue. */
public int Front() {
if(isEmpty()==true)
return -1;
return arr[front];
}
/** Get the last item from the queue. */
public int Rear() {
if(isEmpty()==true)
return -1;
if(rear==0)
return arr[MAXSIZE-1];
else
return arr[rear-1];
}
/** Checks whether the circular queue is empty or not. */
public boolean isEmpty() {
if(flag==false&&rear==front)
return true;
else
return false;
}
/** Checks whether the circular queue is full or not. */
public boolean isFull() {
if(flag==true&&rear==front)
return true;
else
return false;
}
}
后入先出的数据结构
与队列不同,栈是一个 LIFO 数据结构。插入操作在栈中被称作入栈 (push) 。与队列类似,总是在堆栈的末尾添加一个新元素。与队列不同的是,删除操作被称作退栈 (pop) ,将始终删除队列中相对于它的最后一个元素。
栈的实现
- 队列的初始化:比队列更简便,只需要一个动态数组即可实现
- 队列的两个主要操作:入栈和退栈
// "static void main" must be defined in a public class.
class MyStack {
private List<Integer> data; // store elements
public MyStack() {
data = new ArrayList<>();
}
/** Insert an element into the stack. */
public void push(int x) {
data.add(x);
}
/** Checks whether the queue is empty or not. */
public boolean isEmpty() {
return data.isEmpty();
}
/** Get the top item from the queue. */
public int top() {
return data.get(data.size() - 1);
}
/** Delete an element from the queue. Return true if the operation is successful. */
public boolean pop() {
if (isEmpty()) {
return false;
}
data.remove(data.size() - 1);
return true;
}
};
public class Main {
public static void main(String[] args) {
MyStack s = new MyStack();
s.push(1);
s.push(2);
s.push(3);
for (int i = 0; i < 4; ++i) {
if (!s.isEmpty()) {
System.out.println(s.top());
}
System.out.println(s.pop());
}
}
}
栈的使用——判断括号字符串是否有效
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
且有效字符串满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
class Solution {
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
for (char c: s.toCharArray())
{
if(stack.empty())
{
stack.push(c);
continue;
}
if(c==')'&&stack.peek()=='('
||c==']'&&stack.peek()=='['
||c=='}'&&stack.peek()=='{')
stack.pop();
else
stack.push(c);
}
if (stack.isEmpty())
return true;
else
return false;
}
}
栈的使用——求逆波兰表达式(二元运算符)
根据逆波兰表示法,求表达式的值。
- 有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
- 整数除法只保留整数部分。给定逆波兰表达式总是有效的。
举例:输入: [“2”, “1”, “+”, “3”, “*”] 输出: 9 解释: ((2 + 1) * 3) = 9
class inversePolish {
public int evalRPN(String[] tokens) {
int a=0;int b=0;
Stack <String> stack=new Stack<>();
for (String e:tokens)
{
if(e.equals("+")||e.equals("-")||e.equals("*")||e.equals("/")) //字符串比较不能用==
{ //运算符则取出前两个变量
b=Integer.parseInt(stack.peek()); //string→int
stack.pop();
a=Integer.parseInt(stack.peek());
stack.pop();
}
switch(e)
{
case "+": stack.push(String.valueOf(a+b)); break; //int→string
case "-": stack.push(String.valueOf(a-b)); break;
case "*": stack.push(String.valueOf(a*b)); break;
case "/": stack.push(String.valueOf(a/b)); break;
default: stack.push(e); //数字直接放入
}
}
return Integer.parseInt(stack.peek());
}
}