一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 24 天,点击查看活动详情
日积月累,水滴石穿 😄
栈的定义
栈(Stack)是线性结构的一种,栈中的元素拥有先进后出的特点,即 Last In First Out (LIFO)。比如:放盘子的时候都是从下往上一个个的放,拿的时候是从上往下一个个的拿,这种其实就是栈型数据结构。
栈的原理
栈是一个限定仅在表尾进行插入和删除操作的线性表,这一端被称为栈顶,相对的,把另一端称为栈底。向一个栈插入新元素称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈的实现方式
由于栈的结构也是线性表,那么栈的实现可以使用链表或者数组进行实现。各位可能会有疑问了,既然数组和链表可以实现,为什么要单独将栈拎出来呢作为数据结构呢?这是因为数组和链表暴露了太多的接口,在不熟悉其中方法的情况下,在使用上可能会出错,所以在某些特定场景下最好是选择栈这个数据结构。
- 基于数组实现栈:数组头为栈底,入栈和出栈在数组尾部进行插入和删除操作,操作数组尾部时间复杂度为O(1)。
- 基于单链表实现栈:链表头节点为栈底,入栈和出栈在链表的头部进行插入和删除操作,操作链表头部时间复杂度为O(1)。每次入栈时将 next 指针指向新节点,出栈时将 next 指向下个节点。
Java 中并没有栈结构对应的的接口,但是有 Stack 类,但是 Stack类已经过时了,并且是基于 Vector 实现的。由于 Vector 的相关方法都是加锁实现的,所以更推荐使用 Deque(双端队列)来完成栈的相关操作。
数组栈
/**
* @author: cxyxj
*/
public class ArrayStack<T> {
/**
* 数据
*/
Object[] data;
/**
* 元素个数
*/
int size;
/**
* 初始化数组容量
*/
public ArrayStack(int cap) {
data = new Object[cap];
}
/**
* 入栈
*/
public void push(T e) {
//这里可以做数组扩容、缩减
data[size++] = e;
}
/**
* 出栈
*/
public T pop() {
if (isEmpty()){
return null;
}
Object datum = data[--size];
//方便 GC
data[size] = null;
return (T)datum;
}
/**
* 判断栈中是否还有元素
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 返回栈中元素个数
*/
public synchronized int size() {
return size;
}
public void print() {
System.out.print("size:" + size);
for(int i = 0; i < size; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
// 先进后出
ArrayStack mystack = new ArrayStack(2);
mystack.push("1110");
mystack.push("2222");
mystack.print();
System.out.println(mystack.pop());
System.out.println(mystack.pop());
mystack.print();
System.out.println(mystack.pop());
}
}
输出结果
size:21110 2222
2222
1110
size:0
null
链表栈
public class LinkedListStack {
private MyNode headNode;
private int size;
public void push(String data){
// 构建新节点
MyNode newNode = new MyNode(data);
// 将旧的头节点 作为 新节点的下个节点
newNode.next = headNode;
// 新节点作为头节点
headNode = newNode;
size++;
}
public String pop(){
// 头节点为 null,说明链表中没有元素了
if(headNode == null){
return null;
}
// 获得头节点的值
String item = headNode.item;
//获得头节点的下个节点
MyNode headNext = headNode.next;
//将下个节点赋值给头节点
headNode = headNext;
size--;
return item;
}
/**
* 判断栈中是否还有元素
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 返回栈中元素个数
*/
public synchronized int size() {
return size;
}
public void print(){
MyNode cur = headNode;
while (cur != null){
System.out.println(cur.item + " ");
cur = cur.next;
}
}
public static void main(String[] args) {
LinkedListStack linkedListStack = new LinkedListStack();
linkedListStack.push("111");
linkedListStack.push("2222");
linkedListStack.push("3333");
System.out.println( linkedListStack.pop());
System.out.println( linkedListStack.pop());
System.out.println( linkedListStack.pop());
System.out.println( linkedListStack.pop());
linkedListStack.print();
}
private static class MyNode{
String item;
MyNode next;
public MyNode(String item){
this.item = item;
}
}
}
结果如下
3333
2222
111
null
看完了上面数组栈和链表栈的实现源码,如果各位小伙伴看了小杰前面数组、链表的俩篇文章,小伙伴是不是心里大呼,就这就这,太简单了吧,数组只允许操作尾元素,链表只允许操作头节点。
栈的应用
括号匹配
假如传入的表达式中包含三种括号:圆括号、方括号和花括号,并且可以任意嵌套。例如{[()][{}]}或[{([])}]为正确格式,而{[]()]或[(])]为不正确的格式。那怎么检测传入的表达式是否正确呢?
思路
获得传入的字符,循环判断每个字符.
-
如果是左括号,则直接入栈。
-
如果是右括号,则直接弹出栈顶元素。
-
- 1、判断栈是否为空,为空则代表没有对应的左括号与之匹配,返回false。
-
- 2、不为空,判断栈顶元素是不是相匹配的同类右括号,如果不是则直接返回false,
-
- 3、当字符读完时,判断栈是否是空的,如果是则表达式是合法的。反之,则是异常表达式。
public class KuoHaoStack {
public static boolean isOk(String s) {
ArrayStack<Character> brackets = new ArrayStack<>(10);
char c[] = s.toCharArray();
Character top;
for (char x : c) {
switch (x) {
case '}':
top = brackets.pop();
if (top == null || top != '{') {
return false;
} else {
break;
}
case ')':
top = brackets.pop();
if (top == null || top != '(') {
return false;
} else {
break;
}
case ']':
top = brackets.pop();
if (top == null || top != '[') {
return false;
} else {
break;
}
default:
brackets.push(x);
break;
}
}
return brackets.isEmpty();
}
public static void main(String[] args) {
String s = "[()[]]";
System.out.println(s + "匹配结果:" + isOk(s));
String s1 = "[()[]}";
System.out.println(s1 + "匹配结果:" + isOk(s1));
}
}
输出结果
[()[]]匹配结果:true
[()[]}匹配结果:false
- 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。