入门数据结构JAVA DS ——手搓 栈

前言

栈作为基础的数据结构,拥有着先进后去的特点,但他的本质依旧是一个线性表,在函数管理调用和算法等方面有广泛的应用,笔者也写过一些用栈的算法题,本篇博客是介绍如何自己手搓一个栈出来,虽然前任已经替我们总结好了,我们可以直接用,但是知其然和知其所以然还是有很大区别的话不多说,开始吧

栈的概念

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFOLast In First Out)的原则。

压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶
出栈:栈的删除操作叫做出栈。 出数据在栈顶

 

 栈的部分方法

本次我们手搓的栈,有如下功能

    void push(int x);
    // 进栈

    int size();
    // 栈内有多少元素

    int peek();
    // 显示最近入栈的元素

    int pop();
    // 弹出栈顶的元素

    boolean empty();
  // 判断是否为空
    

内容不多,大致的流程也和此前的手搓顺序表类似

JavaDS-学习数据结构之如果从零开始手搓顺序表,顺带学习自定义异常怎么用!-CSDN博客 

 

接口

我们首先定义一个接口

在接口中,我们规定了我们的栈有那些方法,接下来我们就要实现这些方法

package stack;

public interface MyStack
{
    void push(int x);
    // 进栈

    int size();
    // 栈内有多少元素

    int peek();
    // 显示最近入栈的元素

    int pop();
    // 弹出栈顶的元素

    boolean empty();
  // 判断是否为空

}

 Stack类

属性

在Stack类中,首先要定义好各类属性,我们这次是以数组的方式来实现,当然了,双向链表也可以,但是单向链表不行,至于为什么后面会说

  private int [] elem;

    private int usedsize;
    //存放数据的下标

    private  static  final  int DEFAULT_NUM =10;

    public Stack()
    {
        this.elem = new int[DEFAULT_NUM];
        this.usedsize = usedsize;
    }

我们默认的构造是一个大小为10的数组

push方法

push方法也就是入栈,那么,我们应该怎么入栈的,通过栈的特点我们可以知道,每次都是从最后的位置插进去,换而言之,就是从数组的最后面一个个插进去,那么我们前面已经定义了usedsize,很显然,

我们就是通过usedsize,令我们需要插的元素的下标为usedsize即可

具体写法如下

 private  void  checkcap()
 {
     if(usedsize==elem.length)
     {
         elem= Arrays.copyOf(elem,elem.length*2);
     }
 }
   @Override
    public void push(int x)
    {
       checkcap();
       elem[usedsize]=x;
       usedsize++;
       //保证永远是放在数组的最后一个位置
    }

可以看到,我们首先检查我们的数组大小是否够,不够就扩容

然后插入元素,最后usedsize++;

size方法

这个EZ ,直接返回usedsize

    @Override
    public int size()
    {
        return usedsize;
    }

 empty方法

同样不难就是了

判断usedsize是否为0

   @Override
    public boolean empty()
    {
        if(usedsize==0)
        {
            return true;
        }
       return false;
    }
}

 

pop方法

pop方法就是弹出栈顶的元素,换而言之,就是删除最新插入的元素,那我们应该怎么做????

那就返回最新插入的元素呗,这个元素的下标是多少呢? usedsize-1呗

为什么?

因为每次插入以后,我们的usedsize会+1,当作下次插入的元素的下标,那么它的前一个,即usedsize-1,也就是栈顶元素的下标,弹出后,再令usedsize-1,代表确实弹出去了

package stack;

public class EmptyException  extends RuntimeException

{
    public EmptyException(String message)
    {
        super(message);
    }
}
    @Override
    public int pop()
    {
        if(empty())
        {
            throw  new EmptyException("栈为空");
        }
        int oldnum=elem[usedsize-1];
        usedsize--;
          // elem[usedsize]=null;
        return oldnum;
    }

 同时注意,我们的例子是基础数据,如果是引用类型,还是要置为空的. 如果栈本来就是空的,就要抛异常了,所以还要自定义异常,这都没什么说的

peek方法

也就是C++中的top(),展示栈顶元素,那好办的,usedsize不要-1就好了

    @Override
    public int peek()
    {
        if(empty())
        {
            throw  new EmptyException("栈为空");
        }
        int oldnum=elem[usedsize-1];
        // 不删
        return oldnum;

    }

 至此,一切都已经结束了!这就是一个简单的数据结构——栈!!!!!!!

链表实现栈的问题

以链表实现的栈叫做链式栈,那么,双向链表和单向链表,那个可以实现呢?

在回答这个问题之前,我们要明白,插入一个节点和删除一个节点,一定要它相邻的节点的地址,这个没有问题吧!!!!!!!!!

如果是一个双向链表,当然没问题了

你如果以head为入口插入栈,不管是出栈还是入栈,时间复杂度都是O(1),毋庸置疑

哪怕我们以last为入口,也没问题啊,因为我们知道last的地址,last 节点也保留了前一个元素的位置

只要

     last.prev.next=null;
     last.prev=last;
// 删除的

 

 last.next=temp;
 temp.prev=last;
 last=temp;
// 增加的

 

那么单链表可以吗?如果是head当作入口,那么没问题

如果是Last,那么,铁定不行,因为要插入或者删除,你一定要遍历一下链表,找到前一个的坐标,那么时间复杂度就是0(N)了,所以是不行滴

 结尾

暑假开摆了,没有怎么更新了,多点点赞,这样我才好有动力去更新

而且最近流量收益也砍了,看来CSDN是要数量不要质量了

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值