栈的定义以及用java实现栈的API的两种形式--链表和数组

本文介绍了栈的基本定义,并详细探讨了使用Java通过链表和数组实现栈的API,包括性能分析。链表实现提供常数时间操作但占用更多空间,而数组实现则在空间效率上占优,适合对速度要求不高的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

栈的定义以及用java实现栈的API的两种形式–链表和数组

最近在学习算法第四版,看网课的同时,才发现之前学数据结构的时候囫囵吞枣,没有领会到其中的精髓,故对栈的相关知识进行略微总结,如有不对之处请指出,谢谢!

栈的定义

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
在这里插入图片描述

public class StackOfStrings
------------------------------------------------------------------------
         StackOfStrings                  //创建一个空栈    
         void push(String item)          //往栈里压入一个数据
         String pop()                    //弹出栈顶的数据
         boolean isEmpty()               //判断栈是否为空
         int size()                      //返回栈里面元素的个数
         // An highlighted block

API实现之链表

public class LinkedStackOfString{
    private Node first = null;
    private class Node{
        String item;
        Node next;
    }//将node作为类的内部类
    public boolean isEmpty(){
        return first == null;}
        
    public void push(String item){
       Node oldfirst = first;
       first = new Node();
       first.item = item;
       first.next = oldfirst;  
    }//建立一个新的结点并将它指向原本的头结点,使之成为新的头结点,完成入栈
    public String pop(){
        String item = first.item;
        first = first.next;
        return item;
    }//创建一个字符串来保存头结点的值并打印,使头结点的指针指向下一个结点,完成出栈
}    

性能分析:
栈的链表实现中,每个操作在最坏情况下都是常数时间,并且不存在循环;从空间上来看,在Java中,每个对象都会有16字节的额外空间,由于内部类的存在,还需要8字节的额外空间。在类Node中有两个引用,一个指向字符串,另一个指向Node类,各自需要8字节,因此,每个栈节点需要40字节的空间,如果一个栈的大小为N,由于还有对于first的引用,所以大概需要40N的空间。

API实现之数组

public class ResizingArrayStackOfString{
    public ResisingArrayStackOfString(){
        s = new String[1];
    }
    public void push(String item){
        if(N == s.length)  resize(2*s.length);
        s[N++]=item;  
    }//通过resize方法来创建一个大小为原来2倍的数组,并将入栈的数组存在最后一位
    private void resize(int capacity){
        String[] copy = new String[capacity];
        for(int i=0;i<N;i++)
            copy[i]=s[i]; 
        s=copy;
    }//建立一个大小为原来capacity倍的数组,然后把原来的数组复制到其中
    
    public String pop(){
        String item = s[--N];
        s[N] = null;
        if(N>0 && N == s.length/4)  resize(s.length/2);
        return item;
    }
   public boolean isEmpty(){
        return N == 0;}
}

性能分析:
在栈的数组实现中,使用了一种叫反复倍增的方法,在数组填满时,建立一个大小翻倍的新数组,然后把数据全都复制过去,来避免频繁地建立数组以及复制数据。这样,只有在数组元素填满的情况下,才需要去访问整个数组,从而减少所有操作的访问次数,这叫做平摊分析。
接下来考虑缩小数组,我们可以在数组元素只有一半的时候,把数组缩小一半,但如果在此时出现抖动(客户端反复交替入栈出栈)现象时,会导致数组反复翻倍减半,花费大量的时间。因此,我们在数组变为1/4满的时候再进行减半。
从空间上来看,数组实现要比链表实现更节省空间,大概是8N~32N,取决于数组有多满。

两种实现的优缺点

这两种实现,实际上是同一种API的不同实现,客户端可以进行互换使用,因此,选用哪种实现,取决于客户端的需求。对于链表实现,每个操作都只需要常数时间,但为了处理链接需要一些额外的空间,所以链表实现会慢一点;可调大小的数组实现有很好的分摊时间,所以整个过程中也许会有更快的实现。当需要迅速完成某些操作时,通常会使用链表实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值