数据结构——栈

1.什么是栈

  • 先进后出,后进先出,这就是典型的栈结构
  • 从栈的操作特性来看,是一种 操作受限 的线性表,只允许在端插入和删除数据

2.为什么需要栈

  • 栈是一种操作受限的数据结构,其操作特性用数组和链表均可实现
  • 但任何数据结构都是对特定应用场景的抽象,数组和链表虽然使用起来更加灵活,但却暴露了几乎所有的操作,难免会引发错误操作的风险
  • 所以,当某个数据集合只涉及在某端插入和删除数据,且满足后进者先出,先进者后出的操作特性时,这个时候 栈 这种数据结构就能够排得上用场

3.如何自己实现栈

    //      这里用到了泛型
public class MyStack<T> {

    //判断栈是否满
    private boolean isFull() {

    }
    //入栈
    public void push(T value) {

    }
    //判断栈是否为空
    private boolean isEmpty() {

    }
    //出栈
    public T pop() {

    }
    //获得栈顶元素,但不出栈
    public T peek() {

    }
}

数组实现

  • 时间复杂度分析:数组实现(自动扩容)符合大多数情况是O(1)级别复杂度,个别情况是O(n)级别复杂度,比如在满的时候自动扩容时,会进行完整数据的拷贝
  • 空间复杂度分析:在入栈和出栈的过程中,只需要一两个临时变量存储空间,所以O(1)级别。我们说空间复杂度的时候,是指除了原本的数据存储空间外,算法运行还需要额外的存储空间
public class MyStack<T> {
    public T[] elem;
    public int top;//下标

    public MyStack() {
        this.elem = (T[]) new Object[10];
        this.top = 0;
    }
    private boolean isFull() {
        return this.top == this.elem.length;
    }
    public void push(T value) {
        if (isFull()) {
            this.elem = Arrays.copyOf(this.elem,this.elem.length*2);//二倍扩容
            System.out.println("扩容成功");
        }
        this.elem[this.top] = value;
        this.top++;
    }
    private boolean isEmpty() {
        return this.top == 0;
    }
    //出栈
    public T pop() {
        if (isEmpty()) {
            return null;
        }
        T tmp = this.elem[this.top-1];
        this.top--;
        return tmp;
    }
    //获得栈顶元素,但不出栈
    public T peek() {
        if (isEmpty()) {
            return null;
        }
        return this.elem[this.top-1];
    }
}

链表实现

  • 时间复杂度分析:入栈和出栈的时间复杂度均为O(1)级别,因为只需更改单个节点的 next
  • 空间复杂度分析:在入栈和出栈的过程中,只需要一两个临时变量存储空间,所以O(1)级别。我们说空间复杂度的时候,是指除了原本的数据存储空间外,算法运行还需要额外的存储空间
class MyListStack<T>{
    //定义一个内部类 就可以直接使用类型参数
    class Node{
        T data;
        Node next;
    }

    private Node first;
    private int size;
    //构造方法
    public MyListStack() {
    }
    //入栈
    public void push(T data) {
        Node oldfirst = first;
        first = new Node();
        first.data = data;
        first.next = oldfirst;
        size++;
    }
    //出栈
    public boolean pop() {
        T data = first.data;
        first = first.next;
        size--;
        return true;
    }
    //获得栈顶元素
    public T peek(){
        return first.data;
    }
    public int size() {
        return size;
    }
}

4.栈的应用

栈在函数中的应用

  • 操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,就会将其中的临时变量作为栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈

栈在表达式求值中的应用(34 + 13*9 + 44 -12/3)

  • 利用两个栈,其中一个用来保存操作数,另一个用来保存运算符,我们从左向右遍历表达式,当遇到数字,我们就直接入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较,若比运算符栈顶元素优先级高,就将当前运算符入栈,若比运算符栈顶元素的优先级低或者相同,从运算符栈中取出栈顶运算符,从操作数栈顶取出2个操作数,然后进行计算,把计算完的结果入操作数栈,继续比较

栈在括号匹配中的应用({ } { [ ( ) ] ( ) })

  • 用栈保存为匹配的左括号,从左到右一次扫描字符串,当扫描到左括号时,则将其入栈;当扫描到右括号时,从栈顶取出一个左括号,如果能匹配上,则继续扫描剩下的字符串。如果扫描过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式

如何实现浏览器的前进后退功能

  • 我们使用两个栈X和Y,我们把首次浏览的页面依次压如栈X,当点击后退按钮时,再依次从栈X中出栈,并将出栈的数据一次放入Y栈。当点击前进按钮时,我们依次从栈Y中取出数据,放入栈X中。当栈X中没有数据时,说明没有页面可以继续后退浏览了。当Y栈没有数据,那就说明没有页面可以点击前进浏览了

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值