1、栈
栈也是一种线性结构,相比数组,栈对应的操作是数组的子集。栈只能从一端添加元素,也只能从一端取出元素,这一端称为栈顶。
栈是一种后进先出的数据结构——Last In First Out(LIFO),在计算机的世界里,栈拥有着不可思议的作用。
栈的应用
1)无处不在的Undo操作(撤销);
2)程序调用的系统栈。(子过程子逻辑的调用)
栈的实现
这部分参考StackTest项目,代码如下:
package com.lkj;
/*
复杂度分析:全部5个方法的时间复杂度都是O(1)。另外,push以及pop方法既是会触发栈的容积变化,但是均摊复杂度依然是O(1)
boolean idEmpty();
int getSize();
void push(E e);//入栈
E pop();//出栈
E peek();//获取栈顶的元素
*/
public class ArrayStack<E> implements Stack<E>
{
/*
注意,我们是基于前面的动态数组来创建我们的栈的,因此我们初始化栈的时候,必须先初始化一个动态数组。
*/
//有一个成员变量:类型是我们前面创建的动态数组
Array<E> array;
//------------------------------------------构造函数
public ArrayStack(int capacity)//因为我们的栈是基于动态数组实现的,因此就有容积这个概念
{
array = new Array<E>(capacity);//首先创建一个容量为capacity的动态数组
}
public ArrayStack()
{
array = new Array<E>();//创建一个默认容量的动态数组(默认为10)
}
@Override
public boolean idEmpty()
{
//数组是空的,那么依靠数组实现的栈也是空的
return array.isEmpty();
}
@Override
public int getSize()
{
//数组的元素个数等于栈的元素个数
return array.getSize();
}
//获取栈的容积——这个方法Stack接口并没有,因为Stack接口与栈的具体实现无关,
// 只有我们通过动态数组实现栈,才有容积这个说法。因此这个方法是ArrayStack特有的
public int getCapacity()
{
return array.getCapacity();
}
//----------------------------------------------------------------------入栈出栈,以及获取栈顶元素的方法
/**
* 1、根据栈先进后出的原则,我们在数组的末尾添加,就必须在数组的末尾取出,这样才满足先进后出。(栈顶是在数组的末尾)
* 2、另外,由于我们送他数组Array中会根据元素个数以及数组容积自动进行扩容和缩容,因此用户不需要考虑空间够不够使用的问题。
* 3、我们不需要判断栈是否为空,因为如果栈为空,相当于动态数组为空,那么操作出错的时候自然会抛出异常。
* 4、我们没有获取栈中任意角标元素的方法:get(index),因为对于栈这种数据结构,只能看到栈顶的元素,而无法获取栈中的某个元素。
* 当然,栈也无法实现将元素插入栈中某一个位置:add(int index , E e),或者删除栈中任意位置的元素:remove(int index)。
* 既对于栈来说,我们只能往栈顶入栈(push)或者出栈元素(pop),或者获取栈顶元素(peek),其他位置的操作不能实现。
*/
@Override
public void push(E e)
{
//往数组的末尾添加元素
array.addLast(e);
}
@Override
public E pop()
{
//在数组末尾删除元素
return array.removeLast();
}
@Override
public E peek()
{
//此处,栈顶是在数组的末尾,因此要getLast
return array.getLast();
}
//------------------------------------------
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("[");
//注意,此时栈的元素个数相当于数组的Array的size
// 此处Array不是java的数组,而是我们自己定义的数组类,因此不能使用Array[x]、Array.length这种写法,而要使用Array的get或者getSize方法
for (int i = 0; i < array.getSize() ; i++)
{
sb.append(array.get(i));
if(i != array.getSize()-1)
sb.append(",");
}
sb.append("] top" );//top表示栈顶在尾部
return sb.toString();
}
}
栈的一个应用——括号匹配
在完成括号匹配应用的时候,我们同时解决LeetCode中的一个问题:
思路:我们使用栈来完成这个功能。
1)对于这个只包含括号的字符串,首先我们利用一个循环,从头开始读取每个字符,如果这个字符是“(”、“[”、“{” 这类左括号,我们将这个字符推入栈;
2)当读取到的字符不是左括号而是右括号的时候,我们就先判断栈是否为空,如果栈为空,而这里有一个右括号,说明字符串无效,返回false;(这种情况是前面左括号不够而右括号过多,如:()} )
3)如果栈不为null,我们将栈顶的元素出栈,判断这个右括号与栈顶的左括号是否是同一个类型。如果不是同一个类型,说明这个字符串无效,返回false;(这种情况是左括号与右括号不匹配,如:([)] ),如果2个括号是同一个类型的,继续读取下一个字符,注意,取出元素的时候栈顶的元素已经出栈。
4)当字符串的字符读取完毕后,我们判断栈里面还有没有元素,既判断栈是否为null,如果还有元素,字符串无效,返回false。(这种情况是前面左括号过多而右括号不够,如:({} )如果没有元素,说明字符串有效,返回true。
- 注意,需要排除左括号与右括号不匹配,左括号过多,右括号过多3种错误情况。
- 我们是一直在排除错误的情况,如果循环后没有返回false,说明字符串有效,所有错误情况被排除,返回true
首先,使用java提供的Stack类解决。代码如下(参考项目StackResolveProblem)
import java.util.Stack;
public class Solution
{
public boolean isValid(String s) {
//先创建一个栈,注意这个栈存储的是char类型的字符数据</