数据结构之栈详解

目录

栈的应用

栈的实现

栈的时间复杂度

栈的应用

使用栈解决有效的括号问题(LeetCode第二十号问题)




  • 栈也是一种数据结构
  • 相比数组,栈对应的操作是数组的子集
  • 只能从一端添加元素,也只能从一端取出元素
  • 这一端称为栈顶
  • 栈是一种后进先出的数据结构
  • 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
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值