数据结构:栈

1.什么是栈?

    • 后进者先出,先进者后出,这就是典型的栈结枸.

    • 从栈的操作特性来看,是一种'操作受限'的线性表,只允许在端插入和删除数据.

 

2.为什么需要栈?

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

 

3.如何实现栈

3.1 栈的API

       boolean             isEmpty() // 判断当前栈是否为空
synchronized E             peek() //获得当前栈顶元素
synchronized E             pop() //获得当前栈顶元素并删除
             E             push(E object) //将元素加入栈顶
synchronized int           search(Object o)  //查找元素在栈中的位置,由栈低向栈顶方向数

3.2 数组实现(自动扩容)

    时间复杂度分析:根据均摊复杂度的定义,可以用数组实现(自动扩容)符合大多数情况是O(1)级別复杂虞,个別情況是O(n)级別复家度,比加自动扩容时,会进行完整数据的拷贝.

    空间复杂度分析:在入栈和出栈的过程中,只需要一两个临时变量存储空间,所以0(1) 级别,我们说空间宴杂度的时候,是指除了原本的数据存储空间外,算法运行还需要额外的存储空间.

public class StackBaseArrayStackList {

    private String[] items;  // 数组
    private int count;       // 栈中元素个数
    private int n;           // 栈的大小

    // 初始化数组,申请一个大小为 n 的数组空间
    public StackBaseArrayStackList(int n) {
        this.items = new String[n];
        this.n = n;
        this.count = 0;
    }

    // 入栈操作
    public boolean push(String item) {
        // 数组空间不够了,直接返回 false,入栈失败。
        if (count == n) return false;
        // 将 item 放到下标为 count 的位置,并且 count 加一
        items[count] = item;
        ++count;
        return true;
    }

    // 出栈操作
    public String pop() {
        // 栈为空,则直接返回 null
        if (count == 0) return null;
        // 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一
        String tmp = items[count-1];
        --count;
        return tmp;
    }
}

3.3 链表实现

    时间复奈度分析:压栈和弹栈的时间复杂度均为0(1)級別,因为只需更改单个节点的索引即可.

    空间复杂度分析:在入栈和出栈的过程中,只需要一两个临时存储空间,所以0(1)级别.我们说空间复杂度的时候,是指除了原本的数据存储空间外,算法运行还需要额外的存储空间。

public class StackBasedLinkedList {
    private Node top = null;

    public void push(int value) {
        Node newNode = new Node(value, null);
        // 判断是否栈空
        if (top == null) {
            top = newNode;
        } else {
            newNode.next = top;
            top = newNode;
        }
    }


    /**
     * 我用-1表示栈中没有数据。
     */
    public int pop() {
        if (top == null) return -1;
        int value = top.data;
        top = top.next;
        return value;
    }


    public void printAll() {
        Node p = top;
        while (p != null) {
            System.out.print(p.data + " ");
            p = p.next;
        }
        System.out.println();
    }


    private static class Node {
        private int data;
        private Node next;


        public Node(int data, Node next) {
            this.data = data;
            this.next = next;
        }


        public int getData() {
            return data;
        }
    }
}

 

4.栈的应用

4.1 栈在函数调用中的应用

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

 

4.2 栈在表法式求值中的应用(比如:34+13*9+44-12/3)

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

 

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

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

    当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式:否则,说明未匹配的左括号为非法格式。

 

4.4 如何实现浏览器的前过后退功能?

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

 

5.思考?

5.1 为什么函数调用要用"栈"来保存临时变量呢?用其他数据结构不行吗?

    其实,我们不一定非要用栈来保存临时变量,只不过如果这个函数调用符合后进先出的特性,用栈这种数据结构来实现,是最顺理成章的选择。

    从调用函数进入被调用函数,对于数据来说,变化的是什么呢?是作用域。所以根本上,只要能保证每进入一个新的函数, 都是一个新的作用域就可以。而要实现这个,用栈就非常方便。在进入被调用函数的时候, 分配一段栈空间给这个函数的变量,在函数结束的时候,将栈顶复位,正好回到调用函数的作用域内。

 

5.2  我们都知道,JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用,堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟我们这里说的“栈”是不是一回事呢?如果不是,那它为什么又叫作“栈”呢?

    内存中的堆栈和数据结构堆栈不是一个概念,可以说内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构。

    内存空间在逻辑上分为三部分:代码区、静态数据区和动态数据区,动态数据区又分为栈区和堆区.

    代码区:存储方法体的二进制代码。高级调度(作业调度)、中级调度(内存调度)、低级调度(进程调度)控制代码区执行代码的切换。

    静态数据区:存储全局变量、静态变量、常量,常量包括final修饰的常量和String常量。系统自动分配和回收.

    栈区:存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。

    堆区:new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。

 

6.代码示例:https://github.com/luomingkui/dataalgo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员学习圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值