一、Stack:
-
2.具体实现:
使用数组实现
public class Stack<E>{
private int size;
private E[] data;
// 直接指定capacity
public Stack(){
data = (E[]) new Object[capacity];
size = 0;
}
// 数组扩容或者缩小
public E[] resize(int capacity){
E[] temp = (E[]) new Object[capacity];
for(int i = 0; i < size; i++){
temp[i] = data[i];
}
data = temp;
return data;
}
// 添加元素
public void push(E e){
if(size == data.length){
resize(2 * data.length);
}
data[size] = e;
size++;
}
// 获取栈顶元素
public E peek(){
return data[size - 1];
}
// 弹出栈顶元素
public E pop(){
if(size == 0){
throw new IlleageArgumentAccess("empty Stack");
}
E res = data[size - 1];
data[size - 1] = null;
size--;
if(size == (data.length/4 ) && size/2 != 0){
resize(data.length/2);
}
return res;
}
// 获取数组内元素个数
public int getSize(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
}
3.栈中应用的复杂度分析:
- void push O(1) 均摊
- E pop() O(1) 均摊
- E peek() O(1)
- int getSize() O(1)
-
4.括号匹配(编译器):
相关leetcode问题 : https://leetcode-cn.com/problems/valid-parentheses/
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack();
for(int i = 0; i<s.length();i++){
char c = s.charAt(i);
if(c == '(' || c == '[' || c=='{'){
stack.push(c);
}else{
if(stack.isEmpty()){
return false;
}
char topChar = stack.pop();
if(c == ')' && topChar!='(' || c == '}' && topChar!='{'|| c == ']' && topChar!='['){
return false;
}
}
}
return stack.isEmpty();
}
}
二、Queue(队列)
实际上是一个接口,通常由LinkedList 实现
也是数据结构,同样操作也是数组的子集
- 只能从队尾添加元素,从队首取出元素
- 多使用链表实现
队列的各种操作的复杂度分析
- void enqueue(E) O(1) 从尾部添加元素 addLast()
- E dequeue() O(n) 从队首删除元素 removeFirst()
- E front() O(1)
- int getSize() O(1)
boolean isEmpty() O(1)
public class Queue<E>{
private int size;
private E[] data;
public Queue(){
data = (E[]) new Object[15];
size = 0;
}
// resize 数组
public E[] resize(int capacity){
E[] temp = (E[]) new Object[capacity];
for(int i = 0; i < size; i++){
temp[i] = data[i];
}
data = temp;
return data;
}
// 入队操作
public void enqueue(E e){
if(size == data.length){
resize(2*data.length);
}
data[size] = e;
size++;
}
// 出队操作
public E dequeue(){
if(size == 0){
throw new IllegalArgumentException("empty Stack");
}
E res = data[0];
for(int i = 0; i < size - 1; i++){
data[i] = data[i+1];
}
data[size - 1] = data[size];
size--;
return res;
}
// 队首元素
public E front(){
return data[0];
}
// 获取元素个数
public int getSize(){
return size;
}
// 查看是否为空
public boolean isEmpty(){
return size==0;
}
// 从尾部添加元素
public void addLast(E e){
enqueue(e);
}
// 从队首删除元素
public E removeLast(){
return dequeue();
}
}
三、循环队列(记录队首)
front / tail
- front == tail (队列为空) | | (tail + 1) % c == front (队列为满)
对于循环队列的出队操作 dequeue() 复杂度为O (1) ```java // 实现方法 : 1.浪费一个空间 2.不浪费一个空间 // 循环数组需要确定头节点与尾结点,因为此时头和尾不再是 0 和 data.length - 1 public LoopQueue
{ private int size; private E[] data; private int front, tail; public LoopQueue(){
data = (E[]) new Object[15];
size = 0;
front = 0;
tail = 0;
}
// 对数组进行扩容 private E[] resize(int capacity){
E[] temp = (E[]) new Object[capacity];
for(int i = 0; i < data.lenght; i++){
temp[i] = data[(i+front)%(data.length)];
}
data = temp;
front = 0;
tail = data.length - 1;
return data;
}
// 队尾入栈 enqueue public void enqueue(E e){
if(size == data.length){
resize(2*data.length);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
// 队首出栈 dequeue public E dequeue(){
if(size == 0){
throw new IllegalArgumentException("Queue is empty");
}
data[front] = null;
front = (front + 1) % data.length;
size--;
if(size == getCapacity() /4 && getCapacity()/2!=0){
resize(getCapacity()/2);
}
return res;
}
// 获得栈内元素个数 public int getSize(){
return size;
}
// 获取队首元素 public E getFirst(){
return data[front];
}
// 获取队尾元素 public E getLast(){
return data[tail];
}
// 查看数组是否为空 public boolean isEmpty(){
return size == 0;
}
}
<a name="8dc02c70"></a>
## 四、数组队列与循环队列的复杂度分析
实际上 浪费空间使用 size() 是为了,保证 tali == front 时可以判断非空还是非满,假如不使用size 的话如何实现<br />使用无 size进行实现<br />![image-20211007201244134.png](https://cdn.nlark.com/yuque/0/2021/png/190166/1635579117334-9703a71b-10e8-4580-a443-41b5f8c3379a.png#clientId=uff1bc9d2-35c4-4&from=drop&id=u04e6aa36&margin=%5Bobject%20Object%5D&name=image-20211007201244134.png&originHeight=244&originWidth=585&originalType=binary&ratio=1&size=23453&status=done&style=none&taskId=u87d7e189-4809-4a41-bd68-b85ed497121)
但是实际上,tail != front , 主要是因为, 在 enqueue 的时候, 使用size的是先把元素填进去之后,再改变tail。 假设 capacity = 3, 实际为4;<br />arr [ tail = 0 ] = 1 ; tail ++ = 1; arr [ tail = 1 ] = 2 ; tail ++ = 2;<br />arr [ tail = 2 ] = 3 ; tail ++ = 3; 此时 (tail + 1) % data.length == 0, 进行resize() ; 即 index = 3 的空间没有用到。 此时使用 size 是因为 tail 超前++ ; 存在空值,可以直接使用size++ 计算数量
<a name="98848ff9"></a>
## 五、双端队列(Deque):
可以在两端添加元素,也可以在队列的两端删除元素<br />addFront() / addLast()<br />removeFront() / removeLast()
```java
// 实现 Deque
public class Deque<E>{
public void addFront(){
if(size == data.length){
resize(2*data.length);
}
if(front == 0){
front = (front - 1 + data.length);
data[front] = e;
} else {
front = front - 1;
data[front] = e;
}
size++;
}
public E removeLast(){
if(size == 0){
throw new IllegalArgumentException("Queue is empty");
}
E res;
if(tail == 0){
tail = tail - 1 + data.length;
res = data[tail];
data[tail] = null;
} else {
tail = tail - 1;
data[tail] = null;
}
size--;
return res;
}
}
六、使用动态数组实现栈和队列
https://leetcode-cn.com/problems/implement-stack-using-queues/ 用队列实现栈
// 后入先出
class MyStack {
private Queue<Integer> queue1;
private Queue<Integer> queue2;
public MyStack(){
queue1 = new LinkedList<Integer>;
queue2 = new LinkedList<Integer>;
}
public void push(int x){
while(!queue1.isEmpty()){
queue2.add(queue1.poll());
}
queue1.add(x);
while(!queue2.isEmpty()){
queue1.add(queue2.poll());
}
}
public int pop(){
return queue1.poll();
}
public int top(){
return queue1.peek();
}
public boolean empty(){
return queue1.isEmpty();
}
}
https://leetcode-cn.com/problems/implement-queue-using-stacks/ 用栈实现队列
// 先入先出
class MyQueue {
private Stack<Integer> stack;
private Stack<Integer> stack1;
public MyQueue() {
stack = new Stack<Integer>();
stack1 = new Stack<Integer>();
}
public void push(int x) {
while(!stack.isEmpty()){
stack1.add(stack.pop());
}
stack.add(x);
while(!stack1.isEmpty()){
stack.add(stack1.pop());
}
}
public int pop() {
return stack.pop();
}
public int peek() {
return stack.peek();
}
public boolean empty() {
return stack.isEmpty();
}
}