栈(Stack)与队列(Queue)
栈
定义:
一种运算受限的线性数据结构,只能在表尾进行插入删除操作(栈底固定,栈顶浮动),表尾称为栈顶,另一端称为栈底
特点:
- 后进先出
- Last In First Out(LIFO)
操作:
- push(E) 入栈
- pop() 出栈
- peek() 返回栈顶元素
- getSize() 返回栈中元素个数
- isEmpty() 判断栈是否为空
栈的实现
创建一个stack接口,声明栈的几个基本方法
public interface Stack<T> {
void push(T elem);
T pop();
T peek();
int getSize();
boolean isEmpty();
}
数组实现
使用之前的自定义数组类来实现自定义栈
package com.company.project.subject.day2.stack;
public class MyselfArray<T> {
private int size;
private T[] data;
public MyselfArray() {
this(10);
}
public MyselfArray(int capacity) {
this.size = 0;
data = (T[]) new Object[capacity];
}
public void add(int index, T elem) {
if (index < 0 || index > this.size) {
throw new IllegalArgumentException("index is error");
}
if (this.size == data.length) {
resize(2 * data.length);
}
for (int i = this.size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = elem;
this.size++;
}
public void addFirst(T elem) {
add(0, elem);
}
public void addLast(T elem) {
add(this.size, elem);
}
public void resize(int newCapacity) {
T[] newData = (T[]) new Object[newCapacity];
for (int i = 0; i < this.size; i++) {
newData[i] = data[i];
}
this.data = newData;
}
public T remove(int index) {
if (index < 0 || index >= this.size) {
throw new IllegalArgumentException("index is invalid");
}
if (this.size <= (data.length / 4) && data.length / 2 > 0) {
resize(data.length / 2);
}
T result = data[index];
for (int i = index + 1; i < this.size; i++) {
data[i - 1] = data[i];
}
this.size--;
data[size] = null;
return result;
}
public int removeElem(T elem) {
int index = this.isContains(elem);
if (index == -1) {
return -1;
} else {
remove(index);
}
return index;
}
public T removeFirst() {
return remove(0);
}
public T removeLast() {
return remove(this.size - 1);
}
public T getElemByIndex(int index) {
if (index < 0 || index >= this.size) {
throw new ArrayIndexOutOfBoundsException("index is error");
}
return data[index];
}
public void modifyElemByIndex(int index, T elem) {
if (index < 0 || index >= this.size) {
throw new ArrayIndexOutOfBoundsException("index is error");
}
data[index] = elem;
}
public boolean isEmpty() {
return this.size == 0;
}
public int isContains(T elem) {
for (int i = 0; i < size; i++) {
if (data[i].equals(elem)) {
return i;
}
}
return -1;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("[");
for (int i = 0; i < this.size; i++) {
stringBuffer.append(data[i]);
if (i != this.size - 1) {
stringBuffer.append(",");
}
}
stringBuffer.append("]");
return stringBuffer.toString();
}
public int getSize() {
return this.size;
}
public int getCapacity() {
return data.length;
}
public T getLastElem(){
return data[size-1];
}
public T getFirstElem(){
return data[0];
}
}
自定义栈
package com.company.project.subject.day2.stack;
public class MyStack<T> implements Stack<T>{
MyselfArray<T> myArray;
public MyStack(){
this(10);
}
public MyStack(int capacity){
myArray=new MyselfArray<T>(capacity);
}
@Override
public void push(T elem) {
myArray.addLast(elem);
}
@Override
public T pop() {
return myArray.removeLast();
}
@Override
public T peek() {
return myArray.getLastElem();
}
@Override
public int getSize() {
return myArray.getSize();
}
@Override
public boolean isEmpty() {
return myArray.isEmpty();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("栈元素数: "+this.getSize()+" 栈容量: "+myArray.getCapacity()+" 栈元素:\r\n");
while (!this.isEmpty()){
stringBuilder.append(this.pop()+"<<<");
}
return stringBuilder.substring(0,stringBuilder.lastIndexOf("<")-2).toString();
}
}
测试
package com.company.project.subject.day2.stack;
public class StatckTest {
public static void main(String[] args) {
Stack<Integer> stack = new MyStack<>();
System.out.println("当前栈为空:"+stack.isEmpty());
for (int i = 0; i < 5; i++) {
stack.push(i);
}
System.out.println(stack.toString());
}
}
运行:
package com.company.project.subject.day2.stack;
public class StatckTest {
public static void main(String[] args) {
Stack<Integer> stack = new MyStack<>();
System.out.println("当前栈为空:"+stack.isEmpty());
for (int i = 0; i < 5; i++) {
stack.push(i);
}
System.out.println("栈当前元素个数:"+stack.getSize());
int length = stack.getSize();
for (int i = 0; i<length; i++) {
System.out.print(stack.pop());
}
}
}
运行:
链表实现
先空着…
栈的应用
有效的括号(20)
链接:https://leetcode-cn.com/problems/valid-parentheses
题目:
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。
public boolean isValid(String s) {
Stack<Character> stringStack = new Stack();
if (s==null){
throw new IllegalArgumentException("字符串为空");
}
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if(ch=='{'||ch=='('||ch=='['){
stringStack.push(ch);
}else{
if(stringStack.isEmpty()){
return false;
}
char res = stringStack.pop();
if(res=='{'&&ch!='}'){
return false;
}
if(res=='['&&ch!=']'){
return false;
}
if(res=='('&&ch!=')'){
return false;
}
}
}
return stringStack.isEmpty();
}
棒球比赛(682)
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
整数 x - 表示本回合新获得分数 x “+” - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。 “D” - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。 “C” - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。 请你返回记录中所有得分的总和。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/baseball-game
package com.company.project.arithmetic;
import java.util.Stack;
public class Solution682 {
public static void main(String[] args) {
String[] ops = {"5","2","C","D","+"};
System.out.println(new Solution682().calPoints(ops));
}
public int calPoints(String[] ops) {
int res = 0;
if (ops == null) {
throw new IllegalArgumentException("所给记录为空!");
}
Stack<Integer> scoreStack = new Stack();
for (int i = 0; i < ops.length; i++) {
String s = ops[i];
switch (s) {
case "C":
if (scoreStack.size() < 1) {
throw new IllegalArgumentException("数据不合法");
}
scoreStack.pop();
break;
case "D":
if (scoreStack.size() < 1) {
throw new IllegalArgumentException("数据不合法");
}
scoreStack.push(scoreStack.peek() * 2);
break;
case "+":
if(scoreStack.size()<2){
throw new IllegalArgumentException("数据不合法");
}
int scoreTemp1 = scoreStack.pop();
int scoreTemp2 = scoreTemp1 + scoreStack.peek();
scoreStack.push(scoreTemp1);
scoreStack.push(scoreTemp2);
break;
default:
scoreStack.push(Integer.parseInt(s));
break;
}
}
while (!scoreStack.isEmpty()) {
res += scoreStack.pop();
}
return res;
}
}
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S ,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
输入:"abbaca"
输出:"ca"
package com.company.project.arithmetic.leetcode;
import java.util.Stack;
public class Solution1047 {
public String removeDuplicates(String s) {
Stack<String> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
String s1 = String.valueOf(s.charAt(i));
if(stack.isEmpty()){
stack.push(s1);
}else if(stack.peek().equals(s1)){
stack.pop();
}else {
stack.push(s1);
}
}
StringBuilder res = new StringBuilder();
for(String ss: stack){
res.append(ss);
}
return res.toString();
}
public static void main(String[] args) {
System.out.println(new Solution1047().removeDuplicates("abbaca"));
}
}
队列
定义:
队列是一种操作受限的先线性表,只能在队首(即表头)front进行删除,队尾(即表尾)tail进行插入操作
特点:
- 先进先出
- First In First Out(FIFO)
操作
- enqueue(E e) 入队
- dequeue() 出队
- getFront() 返回队首元素
- getSize() 返回队列元素个数
- isEmpty() 判断队列是否为空
队列的实现
数组队列
声明一个队列接口
package com.company.project.subject.day2.queue;
public interface Queue<T> {
void enqueue(T elem);
T dequeue();
T getFront();
int getSize();
boolean isEmpty();
}
使用数组实现
package com.company.project.subject.day2.queue;
import com.company.project.subject.day2.stack.MyselfArray;
public class MyQueue<T> implements Queue<T> {
private MyselfArray<T> myArray;
public MyQueue(){
this(10);
}
public MyQueue(int capacity){
myArray=new MyselfArray<>(capacity);
}
@Override
public void enqueue(T elem) {
myArray.addLast(elem);
}
@Override
public T dequeue() {
return myArray.removeFirst();
}
@Override
public T getFront() {
return myArray.getFirstElem();
}
@Override
public int getSize() {
return myArray.getSize();
}
@Override
public boolean isEmpty() {
return myArray.isEmpty();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if(this.isEmpty()){
return null;
}
sb.append("队首>>> ");
while (!this.isEmpty()){
sb.append(this.dequeue()+",");
}
String str= sb.substring(0, sb.lastIndexOf(","));
str+=" <<<队尾";
return str.toString();
}
}
测试
package com.company.project.subject.day2.queue;
import com.company.project.subject.day2.queue.MyQueue;
public class QueueTest {
public static void main(String[] args) {
MyQueue<Integer> myQueue = new MyQueue<>();
System.out.println("队列是否为空: "+myQueue.isEmpty());
for (int i = 0; i < 10; i++) {
myQueue.enqueue(i+1);
}
System.out.println("出队:"+myQueue.dequeue());
System.out.println("元素个数:"+myQueue.getSize());
System.out.println("队列:"+myQueue);
}
}
时间复杂度分析
- enqueue(E e) O(1)
- dequeue() O(n) 出队后还要将其他元素前移
- getFront() O(1)
- getSize() O(1)
- isEmpty() O(1)
循环队列
使用数组实现队列的问题
在进行出队操作时,出队队首元素,其余元素需前移,时间复杂度为O(N)
为了解决每次出队需要移动其余数组元素的问题,我们使用front来记录队首的位置,tail记录队尾
-
情况1
-
情况2
- 情况3 (tail+1)%capacity==front 队满
实现循环队列
新建一个自己的循环队列类
package com.company.project.subject.day2.queue;
public class LoopQueue<T> {
}
声明属性
private T[] data;
private int size;
private int frant;
private int tail;
构造方法
public LoopQueue() {
this(10);
}
public LoopQueue(int capacity) {
data = (T[]) new Object[capacity+1];
this.size = 0;
this.frant = 0;
this.tail = 0;
}
返回队首元素
public T getFront() {
if (this.isEmpty()) {
throw new IllegalArgumentException("this queue is empty");
}
return data[this.frant];
}
改变容量
public void resize(int newCapacity) {
T[] newDate = (T[]) new Object[newCapacity];
for (int i = 0; i < this.size; i++) {
newDate[i] = data[(this.frant + i) % getCapacity()];
}
this.data = newDate;
this.frant = 0;
this.tail = this.size;
}
入队
public void enQueue(T elem) {
if ((tail + 1) % this.getCapacity() == this.frant) {
resize(this.getCapacity() * 2 - 1);
}
this.data[this.tail] = elem;
this.size++;
this.tail = (this.tail + 1) % this.getCapacity();
}
出队
public T deQueue() {
if (this.isEmpty()) {
throw new IllegalArgumentException("this queue is empty");
}
T res = this.data[this.frant];
this.size--;
this.frant = (this.frant + 1) % this.getCapacity();
if ((this.getSize() == this.getCapacity() / 4) && (this.getCapacity() / 2 > 1)) {
resize((this.getCapacity() + 1) / 2);
}
return res;
}
其他方法
public boolean isEmpty() {
return this.frant == this.tail;
}
public int getSize() {
return this.size;
}
public int getCapacity() {
return data.length;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if(this.isEmpty()){
return null;
}
sb.append("队首>>> ");
while (!this.isEmpty()){
sb.append(this.deQueue()+",");
}
String str= sb.substring(0, sb.lastIndexOf(","));
str+=" <<<队尾";
return str.toString();
}
完整代码
package com.company.project.subject.day2.queue;
public class LoopQueue<T> {
private T[] data;
private int size;
private int frant;
private int tail;
public LoopQueue() {
this(10);
}
public LoopQueue(int capacity) {
data = (T[]) new Object[capacity];
this.size = 0;
this.frant = 0;
this.tail = 0;
}
public boolean isEmpty() {
return this.frant == this.tail;
}
public int getSize() {
return this.size;
}
public int getCapacity() {
return data.length;
}
public T getFront() {
if (this.isEmpty()) {
throw new IllegalArgumentException("this queue is empty");
}
return data[this.frant];
}
public void enQueue(T elem) {
if ((tail + 1) % this.getCapacity() == this.frant) {
resize(this.getCapacity() * 2 - 1);
}
this.data[this.tail] = elem;
this.size++;
this.tail = (this.tail + 1) % this.getCapacity();
}
public void resize(int newCapacity) {
T[] newDate = (T[]) new Object[newCapacity];
for (int i = 0; i < this.size; i++) {
newDate[i] = data[(this.frant + i) % getCapacity()];
}
this.data = newDate;
this.frant = 0;
this.tail = this.size;
}
public T deQueue() {
if (this.isEmpty()) {
throw new IllegalArgumentException("this queue is empty");
}
T res = this.data[this.frant];
this.size--;
this.frant = (this.frant + 1) % this.getCapacity();
if ((this.getSize() <= this.getCapacity() / 4) && (this.getCapacity() / 2 > 1)) {
resize((this.getCapacity() + 1) / 2);
}
return res;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if(this.isEmpty()){
return null;
}
sb.append("队首>>> ");
while (!this.isEmpty()){
sb.append(this.deQueue()+",");
}
String str= sb.substring(0, sb.lastIndexOf(","));
str+=" <<<队尾";
return str.toString();
}
}
测试
package com.company.project.subject.day2.queue;
public class LoopQueueTest {
public static void main(String[] args) {
LoopQueue<Integer> loopQueue = new LoopQueue<>();
System.out.println("初始容量: "+loopQueue.getCapacity());
for (int i = 0; i < 10; i++) {
loopQueue.enQueue(i+1);
}
System.out.println("队满后扩容: "+loopQueue.getCapacity());
for (int i = 0; i < 6; i++) {
loopQueue.deQueue();
}
System.out.println("出队后缩容:"+loopQueue.getCapacity());
System.out.println(loopQueue);
}
}
时间复杂度分析
- enqueue(E e) O(1)
- dequeue() O(1)
- getFront() O(1)
- getSize() O(1)
- isEmpty() O(1)
性能比较
package com.company.project.subject.day2.queue;
public class QueueCompare {
public static void main(String[] args) {
LoopQueue<Integer> loopQueue = new LoopQueue<>();
long loopStart = System.nanoTime();
for (int i = 0; i < 2000000; i++) {
loopQueue.enQueue(i+10);
if(i%3000==0){
while (!loopQueue.isEmpty()){
System.out.println(loopQueue.deQueue());
}
}
}
while (!loopQueue.isEmpty()){
System.out.println(loopQueue.deQueue());
}
long loopEnd = System.nanoTime();
MyQueue<Integer> queue = new MyQueue<>();
long start = System.nanoTime();
for (int i = 0; i < 2000000; i++) {
queue.enqueue(i+10);
if(i%3000==0){
while (!queue.isEmpty()){
System.out.println(queue.dequeue());
}
}
}
while (!queue.isEmpty()){
System.out.println(queue.dequeue());
}
long end = System.nanoTime();
System.out.println("循坏队列耗时: "+(loopEnd-loopStart)/1000000000.0);
System.out.println("普通队列耗时: "+(end-start)/1000000000.0);
}
}
运行:
队列应用
栈实现队列
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
class CQueue {
private Stack<Integer> stack1;
private Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(!stack2.isEmpty()){
return stack2.pop();
}
if(stack1.isEmpty()){
return -1;
}else {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
}
队列实现栈
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push 、top 、pop 和 empty )。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/implement-stack-using-queues
package com.company.project.arithmetic.leetcode;
import java.util.LinkedList;
import java.util.Queue;
public class MyStack225 {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack225() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()){
queue2.offer(queue1.poll());
}
Queue<Integer> queueTemp = queue1;
queue1 = queue2;
queue2 = queueTemp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
}
|