我们都知道栈是一种比较重要的数据结构然而他重要在哪呢?有何用处?如何设计我们自己的栈?本章将会探究解开栈的神秘面纱。
本文要点
了解栈
1 概念:
一种后进先出(LIFO)的数据结构,属于线性数据结构。
2 特点:
其实相对于数组来说栈操作的是数组的子集。只需要对数组添加约束便形成栈的特有功能。
添加约束:
1 规定只能向栈的末尾添加元素
2 规定只能从栈的末尾(栈顶)取出元素
ps:通过以上我们可以知道本文探讨的栈,底层还是用数组实现的(动态通用数组封装),如果不了解动态数组可以参考:https://blog.csdn.net/qq_38350635/article/details/86744319
3 常见应用栗子:
- undo操作
操作一步就把这一步入栈 - 程序调用使用的系统栈
使用系统栈记录程序的调用过程中断的点入栈 - 括号匹配 (本节实现)
设计栈
通过以上我们知道了栈的特点后进先出,因此有关栈也有一些api:
因此我们结合动态数组设计我们自己的栈,看看栈到底是如何实现的。
1 首先设计接口(注意Stack为我们自定义不是java util包下的)
package stack;
/**
* Create by SunnyDay on 2019/02/05
* <p>
* 自定义栈(与java api 中util包中stack栈同名)
*/
public interface Stack<E> {
/**
* 元素入栈
*/
void push(E e);
/**
* 元素出栈
*/
E pop();
/**
* 查看栈顶元素 有的使用 top命名 本处使用 peek
* */
E peek();
/**
* 栈中元素个数
* */
int getSize();
/**
* 栈 是否为空
* */
boolean isEmpty();
}
2 实现自定义栈ArrayStack
package stack;
import array.UseGeneric;
/**
* Create by SunnyDay on 2019/02/05
*
* 基于通用动态数组实现
*/
public class ArrayStack<E> implements Stack<E> {
private UseGeneric<E> array;// 成员 动态数组对象引用
// 构造
public ArrayStack(int capacity){
array = new UseGeneric<>(capacity);
}
public ArrayStack(){
array = new UseGeneric<>();
}
/**
* 栈的容量
* */
public int getCapacity(){
return array.getCapacity();
}
/**
* 元素入栈
* */
@Override
public void push(E e){
//入栈就是把元素添加到栈的末尾
array.addLast(e);
}
/**
* 元素出栈
* */
@Override
public E pop() {
// 出栈 把当前的元素(栈的顶端栈顶)删除
return array.removeLast();
}
/**
* 查看栈顶元素(最后一个索引对应元素)
* */
@Override
public E peek() {
return array.getLast();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
/**
*
* 栈 toString 特有
* */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Stack: ");
sb.append("[");
for (int i = 0; i < array.getSize(); i++) {
sb.append(array.get(i));
if (i!=array.getSize()-1){
sb.append(", ");
}
}
sb.append("] :StackTop");
return sb.toString();
}
}
嗯?仅仅简单的两步就实现了我们自己的栈?这么简单哈哈,其实这主要是运用我们上一文章动态数组,简单封装就行就完事了。吧动态数组与栈的概念结合,就这么简单的完成了。不错我可以自信的对你说,经过一番探讨你读java自己stack的源码便easy多了哈哈。
栈代码实战
学过算法的同学应该练习过一道题“括号匹配”,接下来我们就实战下。
题目来源:
https://leetcode.com/problems/valid-parentheses/
(没错题目来源自大名鼎鼎的leetcode网站,练习题20,没看过的可以先读下题目)
1 首先我们进行分析
假如给了我们字符创 s ="([{}])" ,我们可以遍历字符,把字符左( 左 { 左[ 入栈 ,当遍历到右面的字符时拿来与栈顶作对比判断是否匹配(参看下图)
Talk is cheap show me code 哈哈
public boolean isValid(String s) {
// 此处使用java util 包下的Stack 类实现算法,方便我们提交
ArrayStack<Character> stack = new ArrayStack<>();
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 topC = stack.pop();
if (c == ')' && topC != '(') {
return false;
}
if (c == ']' && topC != '[') {
return false;
}
if (c == '}' && topC != '{') {
return false;
}
}
}
// 栈空有效(入栈元素都匹配出栈)
return stack.isEmpty();
}
小结
栈虽是一种相对简单的数据结构,但是栈的应用还是满可以的。有时我们可能碰见问题但是想不到使用,还需要多练啊。
The end
笔冢研穿 任重道远 我们共同努力!!!