栈 Stack
-
栈也是一种线性结构
-
相比数组,栈对于的操作以是数组的子集
-
只能从栈的一端添加元素,也只能从一端取出元素
-
这一端称为栈顶
-
-
把栈顶的元素取出来叫做出栈
-
栈是一种后进先出的数据结构
-
Last In First Out(LIFO)
-
在计算机的世界里,栈拥有者不可思议的作用
栈的应用
- 无处不在的Undo操作(撤销)
- 程序调用的系统栈
栈的实现
-
- 从用户的角度看,支持这些操作就好
- 具体底层实现,用户不关心
- 实现底层有多种实现方式
栈的复杂度分析
coding
`package arithmetic.stack;
import arithmetic.array.Array;
public class ArrayStack implements Stack {
Array<E> array = new Array<>();
public ArrayStack(int capacity) {
array = new Array<>(capacity);
}
public ArrayStack() {
}
@Override
public int getSize() {
return array.getSiaze();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
public int getCapacity() {
return array.getCapacity();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack: ");
res.append('[');
for (int i = 0; i < array.getSiaze(); i++) {
res.append(array.get(i));
if (i != array.getSiaze() - 1)
res.append(",");
}
res.append("] top");
return res.toString();
}
}
package arithmetic.stack;
public interface Stack {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peek();
}
`
栈的应用
- undo操作 - 编译器
- 系统调用栈 - 操作系统
- 括号匹配 - 编译器
(匹配括号)
leeCode
匹配括号
package arithmetic.stack;
import java.util.Stack;
public class Solution {
public boolean isValid(String s) {
Stack<Character> stack;
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 != '(')
return false;
if (c == '}' && topChar != '{')
return false;
if (c == ']' && topChar != '[')
return false;
}
}
return stack.isEmpty();
}
public static void main(String[] args) {
System.out.println((new Solution()).isValid("(){}[]"));
System.out.println((new Solution()).isValid("({{}[]"));
}
}
队列Queue
- 队列也是一种线性结构
- 相比数组,队列对应的操作数是数组的子集
- 只能从一端(队尾)添加元素,只能从另一端(対首)取出元素
- 队列是一种先进先出的数据结构(先到先得)
- Fitst In First Out(FIFO)
# 队列的实现
# 数组队列的复杂度分析
coding
package arithmetic.queue;
public interface Queue<E>{
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
package arithmetic.queue;
import arithmetic.array.Array;
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array;
public ArrayQueue(int capacity){
array = new Array<>(capacity);
}
public ArrayQueue(){
array = new Array<>();
}
@Override
public int getSize(){
return array.getSize();
}
@Override
public boolean isEmpty(){
return array.isEmpty();
}
public int getCapacity(){
return array.getCapacity();
}
@Override
public void enqueue(E e){
array.addLast(e);
}
@Override
public E dequeue(){
return array.removeFirst();
}
@Override
public E getFront(){
return array.getFirst();
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: ");
res.append("front [");
for(int i = 0 ; i < array.getSize() ; i ++){
res.append(array.get(i));
if(i != array.getSize() - 1)
res.append(", ");
}
res.append("] tail");
return res.toString();
}
public static void main(String[] args) {
ArrayQueue<Integer> queue = new ArrayQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
数组队列的问题
- 删除队首元素,后面的元素都要向前移动一位。
- 那么我们可不可以不移动,把队首和队尾的位置记录下来
- 基于以上的想法,我们就有了循环队列
coding
package arithmetic.loopQueue;
import arithmetic.queue.ArrayQueue;
import arithmetic.queue.Queue;
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity) {
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
public int getCapacity() {
return data.length - 1;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;
}
@Override
public E dequeue() {
if (isEmpty())
throw new IllegalArgumentException("cannot dequeue from an empty queue ");
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size--;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0)
resize(getCapacity() / 2);
return ret;
}
@Override
public E getFront() {
if (isEmpty())
throw new IllegalArgumentException("cannot dequeue from an empty queue ");
return data[front];
}
private void resize(int newCapaticy) {
E[] newData = (E[]) new Object[newCapaticy + 1];
for (int i = 0; i < size; i++) {
newData[i] = data[(front + i) % data.length];
}
data = newData;
front = 0;
tail = size;
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(String.format("queue: size = %d , capacity = %d\n", size, getCapacity()));
stringBuilder.append("front [");
for (int i = front; i != tail; i = (i + 1) % data.length) {
stringBuilder.append(data[i]);
if ((i + 1) % data.length != tail)
stringBuilder.append(",");
}
stringBuilder.append("] tail");
return stringBuilder.toString();
}
public static void main(String[] args) {
LoopQueue<Integer> queue = new LoopQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
package arithmetic.queue;
public interface Queue<E>{
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
循环队列的复杂度分析![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2139d8582a47ccedff482d2556c35fe3.png)
测试一下enqueue和dequeue的使用的时间
package arithmetic.queue;
import arithmetic.loopQueue.LoopQueue;
import java.util.Random;
public class Main {
private static double testQueue(Queue<Integer> q, int opCount) {
long startTime = System.nanoTime();
//...........
Random random = new Random();
for (int i = 0; i < opCount; i++) {
q.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i = 0; i < opCount; i++) {
q.dequeue();
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
}
public static void main(String[] args) {
int opCount = 100000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
double time1 = testQueue(arrayQueue,opCount);
System.out.println("ArrayQueue ,time :" + time1 + "s");
LoopQueue<Integer> loopQueue = new LoopQueue<>();
double time2 = testQueue(loopQueue,opCount);
System.out.println("ArrayQueue ,time :" + time2 + "s");
}
}
相差还是很大的