栈是一种先进后出的数据结构,咱们把容许插入和删除的一端称为栈顶,另外一端称为栈底,不含任何元素的栈称为空栈。node
一、栈的操做端一般被称为栈顶,另外一端被称为栈底。 二、栈的插入操做称为进栈(压栈|push);栈删除操做称为出栈(弹栈|pop)。面试
根据栈的存储方式,栈能够分为静态栈(数组实现)和动态栈(链表实现)。算法
对于静态栈,咱们通常经过数组来实现的。实现一个栈,里面主要涉及到 2 种状态和 2 种操做。数组
Java代码实现静态栈bash
首先咱们须要使用到一个数组来存储数据,其次还须要一个变量用于栈的容量大小,还有一个变量来指示栈顶的数据。所以栈的内部数据结构以下所示:数据结构
private T data[];// 用数组表示栈元素
private int maxSize;// 栈空间大小(常量)
private int top;// 栈顶指针(指向栈顶元素)
复制代码
对于栈的构造函数,咱们能够以下表示:函数
public LJStack(int maxSize) {
this.maxSize = maxSize;
this.top = -1;
this.data = (T[]) new Object[maxSize];
}
复制代码
对于栈空的判断很简单,只须要判断 top 是否等于-1便可:测试
/**
* 判断栈是否为空
*
* @return
*/
public boolean isEmpty() {
return this.top == (-1) ? true : false;
}
复制代码
对于栈满的状态判断也很简单,经过 top 的值是否等于栈的容量大小 -1 即:ui
/**
* 判断栈是否为满了
*
* @return
*/
public boolean isFull() {
return this.top == this.maxSize - 1 ? true : false;
}
复制代码
两种状态已经实现了,接下来咱们来看下两种操做,对于入栈操做,核心就是数组的增长而已:this
/**
* 压栈操做,将数据存入到栈中
*
* @param value
*/
public boolean push(T value) {
if (isFull()) {
return false;
} else {
data[++top] = value;
return true;
}
}
复制代码
对于出栈来讲就是将取出数组最后一个值:
/**
* 出栈操做
* @return
*/
public T pop() {
if (isEmpty()) {
return null;
} else {
return data[top--];
}
}
复制代码
以上即是用数组来实现的静态栈,咱们能够进行简单的测试一下:
LJStack<String> stack = new LJStack<String>(10);
System.out.println("是否栈空:" + stack.isEmpty());
stack.push("AAAA");
stack.push("BBBB");
stack.push("CCCC");
stack.push("DDDD");
stack.push("1111");
stack.push("2222");
stack.push("3333");
stack.push("4444");
stack.push("5555");
stack.push("6666");
stack.push("7777");
stack.push("8888");
System.out.println("是否栈满:" + stack.isFull());
复制代码
打印出结果是:
咱们观察结果会发现,打印栈里面的结果发现,居然没有“7777”,“8888”,那是由于栈满了,因此没法入栈了。所以能够判断,上面咱们写的代码是正确的。
接着咱们来讨论一下链式栈的实现。其实链式栈是经过链表实现栈,咱们能够想象有个栈顶节点,当入栈的时候,原先的栈顶节点成为新的节点后继节点,新的节点成为新的栈顶节点。同理,出栈的时候,将栈顶节点弹出,第二个节点成为新的栈顶节点。
Java代码实现链式栈
既然链式栈是经过链表来实现的,首先咱们须要构造一个链表的节点LJLinkNode,
public class LJLinkNode <T>{
private T data;//数据域
private LJLinkNode<T> next;//指针域
public LJLinkNode() {
this.data=null;
this.next=null;
}
public LJLinkNode(T data) {
this.data=data;
this.next=null;
}
public void setData(T data) {
this.data=data;
}
public T getData() {
return this.data;
}
public void setNext(LJLinkNode<T> next) {
this.next=next;
}
public LJLinkNode<T> getNext(){
return this.next;
}
}
复制代码
具体实现以下:
private LJLinkNode<T> topLinkNode;// 栈顶节点
/**
* 初始化
*/
public LJLinkStack() {
this.topLinkNode = new LJLinkNode<T>();
}
/**
* 初始化
*/
public void initLinkStack() {
this.topLinkNode.setData(null);
this.topLinkNode.setNext(null);
}
复制代码
判断栈空状态:
/**
* 判断是否栈空
*
* @return
*/
public boolean isEmpty() {
return this.topLinkNode.getNext() == null;
}
复制代码
压栈操做:
/**
* 压栈
* 当入栈的时候,原先的栈顶节点成为新的节点后继节点,新的节点成为新的栈顶节点。
*
* @param node
*/
public void push(LJLinkNode<T> node) {
if (isEmpty()) {
this.topLinkNode.setNext(node);
} else {
node.setNext(this.topLinkNode.getNext());
this.topLinkNode.setNext(node);
}
}
复制代码
出栈操做:
/**
* 出栈
* 出栈的时候,将栈顶节点弹出,第二个节点成为新的栈顶节点。
* @return
*/
public LJLinkNode<T> pop() {
if (isEmpty()) {
// 栈空没法弹栈
return null;
} else {
LJLinkNode<T> delNode = this.topLinkNode.getNext();// 取出删除节点
this.topLinkNode.setNext(this.topLinkNode.getNext().getNext());// 删除节点
return delNode;
}
}
复制代码
咱们来测试一下写的代码:
LJLinkStack<String> linkStack = new LJLinkStack<String>();
System.out.println("栈是否为空:" + linkStack.isEmpty());
linkStack.push(new LJLinkNode<String>("AAAAA"));
linkStack.push(new LJLinkNode<String>("BBBBB"));
linkStack.push(new LJLinkNode<String>("CCCCC"));
// 依次弹栈
System.out.println("弹栈顺序:");
System.out.println(linkStack.pop().getData());
System.out.println(linkStack.pop().getData());
System.out.println(linkStack.pop().getData());
复制代码
打印结果以下:
以上即是栈的静态及动态实现方式。那么这两种方式有什么具体运用呢?接着咱们来经过具体的面试题目来运用栈。
题目:匹配有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需知足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
复制代码
来源:力扣(LeetCode) 连接:leetcode-cn.com/problems/va…