数据结构之栈

本篇是数据结构与算法之美学习笔记

栈是一种特殊的线性表,仅能在线性表的一端操作数据,只允许在栈顶操作,特性:后进先出。

就像我们往一个盒子里放东西,最先放的东西总是在下面,往外拿的时候,先拿到最后放进去的。
在这里插入图片描述
当某个数据集合,只涉及在一端插入和删除数据,并且满足后进先出的特性,这时候首选栈这种数据结构。

从上面的定义可以看出,栈主要包括两个操作,入栈和出栈,也就是从栈顶插入一个数据,和从栈顶删除一个数据。那怎么来实现一个栈呢?

栈可以用数组来实现也可以用链表来实现,用数组实现的栈叫顺序栈,用链表实现的栈叫链式栈、

使用数组实现一个栈:

public class ArrayStack {
    private String[] items;
    private int count;//栈中的元素的个数
    private int n;//栈的总大小

    public ArrayStack(int n) {
        this.n = n;
        this.items = new String[n];
        this.count = 0;
    }

    //入栈操作
    public boolean push(String item){
        //如果栈满了,直接返回false
        if(count == n) return false;
        items[count] = item;
        ++count;
        return true;
    }

    //出栈操作
    public String pop(){
        //如果栈为空 返回null
        if(count == 0) return null;
        String tmp = items[count-1];
        --count;
        return tmp;
    }

}

上面是实现的一个固定大小的栈,在创建栈的时候需要制定栈的大小,栈满了之后就无法再往里面添加数据了,虽然使用链式栈大小不受限制,但是需要存储next指针,内存消耗比较多。那么怎么使用数组实现一个可以动态扩容的栈呢?

我们先看数组动态扩容的原理:当数组的空间不够的时候,就重新申请一块更大的内存,把原来的数组中数据全部拷贝过去。

所以想要实现一个可动态扩容的栈,让其底层依赖一个可动态扩容的数组就可以了。

使用链表实现一个栈:

public class LinkedListStack {
    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;
    }


    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;
        }
    }
}

函数调用栈:

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

比如下面的函数

 public int main(){
       int a = 1;
       int res = 0;
       int ret = 0;
       ret = add(5,6);
       res = a + ret;
       System.out.println("res"+res);
       return  res;
    }

    public int add(int x ,int y){
        int sum = 0;
        sum = x + y;
        return sum;
    }

从上面代码可以看到,main()方法调用了add()函数,并且与临时变量a相加。最后打印出res并返回。

main栈帧:a = 1,res = 0,ret = 0
add栈帧: x = 5,y = 6,sum = 0

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

因为函数调用符合后进先出的特性,使用栈这种数据结构来实现比较好

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值