目录
栈
- 栈也是一种数据结构
- 相比数组,栈对应的操作是数组的子集
- 只能从一端添加元素,也只能从一端取出元素
- 这一端称为栈顶
- 栈是一种后进先出的数据结构
- Last In First Out(LIFO)
栈的应用
- 无处不在的Undo操作(撤销)
- 程序调用的系统栈
如图,当我们执行程序A到第二行时,要跳转去执行B而暂停A,这时系统就会将A2放入系统栈中,说明说明A暂停在了第二行中断了,在执行B时到第二行时会中断跳转到C中,将状态B2压入系统栈中,在执行完C后系统会在系统栈中依次取出之前中断的B2,A2继续执行。
栈的实现
Stack
- void push(E):入栈
- E pop():出栈
- E peek():查看栈顶元素(不出)
- int getSize():返回栈元素个数
- boolean isEmpty():栈是否为空
这里我们使用基于动态数组的实现方式
Java版本:
/**
* Created by binzhang on 2019/3/16.
*/
public interface Stack<E> {
int getSize();
boolean isEmpty();
void push(E e);
E pop();
E peak();
}
/**
* Created by binzhang on 2019/3/16.
*/
public class ArrayStack<E> implements Stack<E> {
// 这里用的是我们自己定义的泛型数组,在数组篇中有写
Array<E> array;
public ArrayStack(int capacity){
array = new Array<>(capacity);
}
public ArrayStack(){
array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
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 peak() {
return array.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack: ");
res.append("[");
for (int i = 0 ; i < array.getSize() ; i ++){
res.append(array.get(i));
if(i != array.getSize() - 1)
res.append(", ");
}
res.append("] top");
return res.toString();
}
public class Main {
public static void main(String[] args) {
ArrayStack<Integer> stack = new ArrayStack<>();
for (int i = 0 ; i < 5 ; i ++){
stack.push(i);
System.out.println(stack);
}
stack.pop();
System.out.println(stack);
}
}
}
C版本:
#include <stdlib.h>
#include <stdio.h>
#define OK 1
#define FALSE 0
#define ERROR -1
#define OVERFLOW -2
#define Stack_INIT_SIZE 80 //顺序栈的长度
#define Stack_INCREMENT 3 //顺序栈的增长步长
typedef int Status;
typedef int SElemType;
typedef struct
{
SElemType *Base; //栈底指针
SElemType *Top; //栈顶指针
int Stack_size; //栈的大小
}SqStack;
//功能:栈初始化操作
Status Init_Stack(SqStack &S)
{
S.Base = (SElemType *)malloc(Stack_INIT_SIZE*sizeof(SElemType)); //分配存储空间
if(!S.Base) //分配不成功
exit(OVERFLOW);
S.Top = S.Base; //将栈底指针=栈顶指针,表示空栈
S.Stack_size = Stack_INIT_SIZE; //初始化栈的长度
return OK;
}
//功能:取栈顶元素
Status GetTop(SqStack S, SElemType &e)
{
if(S.Top == S.Base) //判断是否为空栈
return ERROR;
else
e = *(S.Top-1); //非空栈,读栈顶元素
return OK;
}
//功能:将数据存入栈,即放入栈顶指针所指向的区域,使栈顶+1
Status Push(SqStack &S, SElemType e)
{
if((S.Top - S.Base)>=S.Stack_size) //是否超过事先分配的存储空间
{
S.Base = (SElemType *)realloc(S.Base, (S.Stack_size + Stack_INCREMENT)*sizeof(SElemType)); //重新申请
if(!S.Base)
exit(OVERFLOW);
S.Top = S.Base + S.Stack_size; //改变栈顶指针
S.Stack_size += Stack_INCREMENT; //改变栈长
}
*S.Top++ = e; //写入数据到栈顶
return OK;
}
//功能:弹栈操作
Status Pop(SqStack &S, SElemType &e)
{
if(S.Top == S.Base)
return ERROR;
e = *--S.Top;
return OK;
}
//功能:清除栈的所有数据
Status ClearStack(SqStack &S)
{
int e, i = 0;
if(S.Top == S.Base)
return ERROR;
while(S.Top != S.Base)
{
Pop(S, e); i++;
printf("第%d个栈顶元素:%d", i, e);
}
return OK;
}
int EmptyStack(SqStack S)
{
if(S.Base == S.Top)
return 1;
else
return 0;
}
void Ten2r(int n, int r)
{
SqStack s;
int m = n;
if(Init_Stack(s) == OK)
{
while(m!=0)
{
Push(s, m%r);
m/=r;
}
printf("转换后的数据为:");
while(s.Top !=s.Base)
{
Pop(s, m);
printf("%4d", m);
}
printf("\n");
free(s.Base);
}
else
printf("错误提示:将10进制数 %d 转换为 %d 进制数失败!\n", n, r);
}
//回文:指顺读和倒读都一样的词语
//重新定义一个字符栈,按栈的操作进行初始化,入栈;弹栈和原数据的第一个元素开始比较,
//如果相等,则继续弹栈,比较第二个,中间某次比较不等,则说明不是回文。
//最终结果判定指向字符串的指针='\0'且栈空,表示是回文
struct CharStack
{
char *Base; //栈底指针
char *Top; //栈顶指针
int Stack_Size; //栈的大小
};
int Palindrome(char *str)
{
CharStack s;
char *p = str;
int i = 0;
s.Base = (char *)malloc(Stack_INIT_SIZE*sizeof(char));
if(!s.Base)
{
printf("错误提示:不能申请内存空间,判断失败!\n");
exit(OVERFLOW);
}
s.Top = s.Base;
s.Stack_Size = Stack_INIT_SIZE;
for(p = str; *p!='\0'; p++) //入栈
{
if((s.Top - s.Base) >= s.Stack_Size)
{
s.Base = (char *)realloc(s.Base, (s.Stack_Size + Stack_INCREMENT)*sizeof(char));
if(!s.Base)
{
printf("错误提示:栈满重新不能申请内存空间,判断失败!\n");
exit(OVERFLOW);
}
s.Top = s.Base + s.Stack_Size;
s.Stack_Size += Stack_INCREMENT;
}
s.Top++;
*s.Top = *p;
}
p = str;
while((s.Top != s.Base)&&(*p != '\0'))
{
if(*s.Top != *p)
return FALSE;
p++;
s.Top--;
}
if((!*p) && (s.Top == s.Base))
return OK;
else
return FALSE;
}
void MainMenu()
{
printf("\n");
printf("\n********************顺序(动态数组)栈的实现********************\n\n");
printf("\t\t1 数 据 入 栈\n");
printf("\t\t2 数 据 出 栈\n");
printf("\t\t3 取 栈 顶 元 素\n");
printf("\t\t4 清 空 栈\n");
printf("\t\t5 栈的应用1:10—r进制转换\n");
printf("\t\t6 栈的应用2:回文判断\n");
printf("\t\t0 退 出 系 统\n");
printf("\n********************顺序(动态数组)栈的实现********************\n\n");
printf("\n");
}
void main()
{
SqStack S;
int choice;
int r, e;
char s1[Stack_INIT_SIZE];
printf("正在对顺序栈初始化......\n");
if(Init_Stack(S))
{
printf("顺序栈初始化成功!\n");
system("CLS");
while(1)
{
MainMenu();
printf("Please choose(请选择功能): ");
scanf("%d",&choice);
switch(choice)
{
case 1:
{
printf("入栈数据:");
scanf("%d", &e);
if(Push(S, e))
printf("数据入栈成功!\n");
else
{
printf("错误提示:数据入栈错误!\n");
exit(0);
}
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 2:
{
if(Pop(S, e) == OK)
{
printf("数据出栈成功!\n");
printf("出栈的数据是:%d\n", e);
}
else
printf("错误提示:栈已空,不能数据不能出栈,数据出栈失败,!\n");
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 3:
{
if(GetTop(S, e))
{
printf("取栈顶元素成功!\n");
printf("栈顶元素是:%d\n", e);
}
else
printf("错误提示:取栈顶元素失败!\n");
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 4:
{
if(ClearStack(S))
printf("栈中元素全部被清空!\n");
else
printf("错误提示:栈清空失败!\n");
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 5:
{
printf("请输入转换的进制:");
scanf("%d", &r);
printf("请输入一个10进制数:");
scanf("%d", &e);
Ten2r(e, r);
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 6:
{
printf("请输入一串字符:");
scanf("%s", s1);
if(Palindrome(s1)==OK)
printf("%s是回文!\n", s1);
else
printf("%s不是回文!\n", s1);
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
case 0:
{
system("CLS");
printf("谢谢使用!\n");
exit(0);
}
default:
{
printf("提示信息:选择出错,请选择数字0—6.\n");
printf("按任意键返回主菜单!\n");
getchar(); getchar(); system("CLS");
break;
}
}
}
}
}
栈的时间复杂度
ArrayStack
- void push(e) O(1) 均摊
- E pop() O(1) 均摊
- E peek() O(1)
- int getSize() O(1)
- boolean isEmpty() O(1)
栈的应用
- undo操作-编辑器
- 系统调用栈-操作系统
- 括号匹配-编译器
使用栈解决有效的括号问题(LeetCode第二十号问题)
- 题目描述
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
- 示例
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
栈顶元素反应了在嵌套的层次关系中,最近的需要匹配的元素
- 解答代码
import java.util.Stack;
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
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 topChar = stack.pop();
if (c == ')' && topChar != '(')
return false;
if (c == ']' && topChar != '[')
return false;
if (c == '}' && topChar != '{')
return false;
}
}
return stack.isEmpty();
}
public static void main(String[] args) {
System.out.println((new Solution()).isValid("{}[]()")); // true
System.out.println((new Solution()).isValid("([)]")); // false
}
}