线性结构之栈结构

        栈是一种只能从一端存取数据并且遵循“后进先出”原则的线性存储结构。这句话中体现了栈结构的三个特征——只能从一端存取数据,遵循“后进先出”的原则和线性存储结构。因此如果我们要实现一个栈结构的数据结构,就必须要满足这三点要求。提到线性结构,最容易想到的就是数组,因此栈结构的底层可以通过数组来存储数据。那么如何实现只能从一段存取数据和“后进先出”的原则呢?这里要知道,后面的这两个条件都和存取数据有关,而且这个存取规则和数组的并不一样,所以就需要重新定义存取规则。这里用一个指针来实现栈结构的存储。

        定义一个指针,假设这个指针的名字叫a,一开始指针a指向的元素位置的索引为-1,即没有指向数组中的任何一个元素。当向数组中添加元素时,指针的位置就会向前移动,这样就实现了从一个端口添加元素。当完成添加元素的操作之后,假设已经添加了10个元素,此时最后一个元素的索引应该是9。当要取出元素时,让指针的位置向后移动,即如果取出一个元素,那么指针指向的元素的索引减一。所以进行获取数组中的元素的操作时,元素的索引的变化为9,8,7,6……-1。可以发现,最后添加的元素的索引为9,而它是最先被取出的,这样就实现了“后进先出”的规则。

        通过上面的描述,已经知道了实现一个栈结构的基本思路,接下来就来具体实现一个数据结构为栈结构的容器。参考java中已经有的栈容器,要实现一个栈容器需要实现几个基本的方法——empty(判断容器是否为空),pop(删除栈顶元素,并将栈顶元素返回),push(将指定的元素添加到栈容器中)。

        接下来创建一个栈容器结构,因为在执行添加元素的操作时添加的元素类型是不确定的,为了提高代码的复用性,这里应该将栈容器类定义为一个泛型类。然后根据上面的描述,需要定义一个私有数组来存储数据,这个数组的类型应该采用Object,因为事先并不知道要存入的数据的具体类型,而且因为JDK1.8之后支持延迟加载的操作,就是当需要的时候才创建对象,所以这里可以不指定数组长度。为了方便后期对数组进行扩容操作,还需要指定一个数组的默认长度。此外,记录栈容器当中的元素的个数以及操作元素索引的指针这个两个变量也是必须的。

        创建完相关变量,接下来就可以对栈容器中的方法做出具体实现了。首先要实现的是添加元素的方法。因为在定义数组时并没有给出数组的大小,因此要实现添加元素的方法首先就要对数组进行初始化,然后还要将元素添加到容器中,并且此时应该记录元素的数目变化。这里要注意的是,数组的初始化并不是说创建一个简单的数组,因为要提高代码的复用性,在不知道后期究竟要添加多少个元素的情况下,初始化数组应该分为两个部分——在没有数组的情况下创建一个新的数组用来存储添加进来的元素以及在已经有数组的情况下如果原数组的长度不够要对原数组进行扩容两个部分。这样这一个步骤的实现代码就比较长,为了提高代码的可读性应定义一个方法来实现数组的初始化操作。这个方法在演示代码中定义为capacity,在capacity中难点在于理解数组的扩容操作。

        扩容操作中包括两个部分,第一个部分,判断是否需要进行扩容操作。这一部分采用的代码为this.size - (this.defaultLength-1) >= 0,其中this.size指的是当前的元素个数,this.defahltLength指的是数组长度,减去1即(this.defaultLength-1)指的是数组的最大索引数。可能会有人认为这里的判断有问题,觉得当前元素个数减去数组的最大索引数这个判断条件不严谨,但是要注意一个问题,那就是如果数组长度已经不能存储元素了,那么就会提示数组元素溢出了。所以应该在数组还没有被存满的情况下进行扩容操作。第二个部分就是进行扩容的部分了,这里采用的代码为        this.defaultLength = this.defaultLength + (this.defaultLength>>1);
            this.arr = Arrays.copyOf(this.arr,this.defaultLength);

第一行代码中的右位移运算符>>的意思是除以2,所以这一行代码之后,原数组的长度就被扩充到了原来的1.5倍。第二行是数组的拷贝操作,也就是将原数组的内容拷贝到新的数组中去的操作。

        完成了数组的初始化,接下来就需进行添加元素的操作了。这里就是一个简单的赋值操作,因为一开始指针指向的位置为-1,并不是数组中的下标,所以在进行赋值操作之前要先移动指针的位置,具体体现在代码this.arr[++index] = item;中。元素个数的记录就极为简单了,只需要在完成添加元素的操作之后记录个数的变量加一即可。

        实现了栈容器中最难实现的添加元素的方法,相对简单的获取元素的方法pop和判断容器是否为空的方法就很简单了。pop方法的内容是返回栈顶元素,因此在返回栈顶元素之前必须要判断容器是否为空,如果元素为空系统就要抛出一个异常,这个异常在java自带的栈容器中有,这里只需要导入即可。而判断容器是否为空也很简单,只需要看指针是否指向-1就行。如果容器不为空,则返回栈顶元素,也就是此时指针指向的元素,返回后指针需要向前一个元素进行移动,具体体现在代码return (E)this.arr[index--];中。这里要说明的是,栈容器中用pop方法取出容器后元素数量会减少,因此应该对size变量进行对应操作。此外,因为定义的数组为Object型,所以取出元素时要对数组进行强制转型。

        有了上面的基础,判断栈容器是否为空的方法就超级简单了,只需要根据指针的位置是否在-1的位置上做出相应的判断即可。

import java.util.Arrays;
import java.util.EmptyStackException;


/**
 * 自定义栈容器
 */


public class MyStack<E> {
    private Object[] arr;//存放元素的物理结构
    private int defaultLength = 4;//数组的默认长度
    public  int size;//记录栈容器的元素个数
    private int  index = -1;//操作数组索引位置的指针
    /**
     * 向容器中添加元素
     */
    public E push(E item){
        //初始化数组
        this.capacity();
        //添加元素
        this.arr[++index] = item;
        //记录元素个数
        this.size++;
        return item;
    }
    /**
     * 数组初始化或者以1.5倍容量扩容
     */
    private void capacity(){
        //数组初始化
        if(this.arr == null){
            this.arr = new Object[defaultLength];
        }
        //数组扩容
        if(this.size - (this.defaultLength-1) >= 0){
            this.defaultLength = this.defaultLength + (this.defaultLength>>1);
            this.arr = Arrays.copyOf(this.arr,this.defaultLength);
        }
    }
    /**
     * 获取栈顶元素
     */
    public E pop(){
        //如果容器为空,抛出异常
        if(index == -1){
            throw new EmptyStackException();
        }
        //记录元素个数
        this.size--;
        //返回元素
        return (E)this.arr[index--];
    }
    /**
     * 判断容器是否为空
     */
    public boolean empty(){
        return this.size == 0;
    }

}

        至此,我们就创建出了一个栈容器类了,为了验证我们创建的栈容器类是否合理,我们创建一个测试类来对其中的方法进行测试。具体结果如下:

package com.data.demo;

public class MyStackTest {
    public static void main(String[] args) {
        MyStack<String> myStack = new MyStack<>();
        myStack.push("a");
        myStack.push("b");
        myStack.push("c");
        myStack.push("d");
        myStack.push("e");
        myStack.push("f");
        System.out.println(myStack.empty());
        System.out.println(myStack.size);
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.pop());
        System.out.println(myStack.empty());

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/760435.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

产品经理系列1—如何实现一个电商系统

具体笔记如下&#xff0c;主要按获客—找货—下单—售后四个部分进行模块拆解

ubuntu 系统中 使用docker 制作 Windows 系统,从此告别 vmware虚拟机

我的系统是 ubuntu 24 前期准备工作&#xff1a; 安装dockerdocker pull 或者 手动制作镜像 docker build 的话 必须要 科学上网&#xff0c; 好像阿里镜像都下不下来。需要 知道 docker 和docker compose 命令的使用方式 我是给docker 挂了 http代理 如果你能pull下来镜像 …

大家都在跳槽,我需要跳槽吗?

文章目录 1. 前言2. 最初的跳槽想法萌芽3. 跳槽想法的再次萌芽4. 我是否需要跳槽呢?5. 那些跳槽的同学怎么样了&#xff1f;6. 小结 1. 前言 两周前&#xff0c;看到研究生同班同学发的一条朋友圈&#xff0c;内容为”下一站 杭州~”&#xff0c;配图是拍的北京开往杭州的列车…

同步的问题及解决方案

同步 同步的问题 当给狗狗食物的同时&#xff0c;狗狗又在吃&#xff0c;这会导致在运行过程中会出现食物的数据的错乱&#xff0c;有时候会多出数据&#xff0c;有时候会少出数据&#xff0c;这就让狗狗有时候会很吃亏&#xff0c;那么该如何解决呢&#xff1f; 实验体现 pa…

实验6 形态学图像处理

1. 实验目的 ①掌握数字图像处理中&#xff0c;形态学方法的基本思想&#xff1b; ②掌握膨胀、腐蚀、开运算、闭运算等形态学基本运算方法&#xff1b; ③能够利用形态学基本运算方法&#xff0c;编程实现图像去噪&#xff0c;边界提取等功能。 2. 实验内容 ①调用Matlab /…

Excel 数据筛选难题解决

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

一个中文和越南语双语版本的助贷平台开源源码

一个中文和越南语双语版本的助贷平台开源源码。后台试nodejs。 后台 代理 前端均为vue源码&#xff0c;前端有中文和越南语。 前端ui黄色大气&#xff0c;逻辑操作简单&#xff0c;注册可对接国际短信&#xff0c;可不对接。 用户注册进去填写资料&#xff0c;后台审批&…

职场必备:三大神器助你完美驾驭工作与生活;从 GTD 到SMART再到OKR:提升效率的终极指南;告别拖延,高效工作的秘密武器!

在现代职场和个人生活中&#xff0c;有效的时间管理和目标设定是成功的关键。我们每天都面临着无数的任务和目标。如何在纷繁复杂的日常中保持专注&#xff0c;高效地完成工作&#xff1f; GTD&#xff08;Getting Things Done&#xff09; GTD&#xff08;Getting Things Don…

10款好用不火的PC软件,真的超好用!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/市场上有很多软件&#xff0c;除了那些常见的大众化软件&#xff0c;还有很多不为人知的小众软件&#xff0c;它们的作用非常强大&#xff0c;简洁…

web全屏api,实现元素放大全屏,requestFullscreen,exitFullscreen

全屏api 主要方法 document.exitFullscreen(); 退出页面全屏状态&#xff0c;document是全局文档对象 dom.requestFullscreen(); 使dom进入全屏状态&#xff0c;异步&#xff0c;dom是一个dom元素 dom.onfullscreenchange&#xff08;&#xff09;; 全…

imx6ull/linux应用编程学习(6)jpeg和png的图片显示

1.JPEG图片显示 JPEG&#xff08;Joint Photographic Experts Group&#xff09;是由国际标准组织为静态图像所建立的第一个国际数字图像压缩标准&#xff0c;也是至今一直在使用的、应用最广的图像压缩标准。JPEG 由于可以提供有损压缩&#xff0c;因此压缩比可以达到其他传统…

SpringBoot | 使用jwt令牌实现登录认证,使用Md5加密实现注册

对于登录认证中的令牌&#xff0c;其实就是一段字符串&#xff0c;那为什么要那么麻烦去用jwt令牌&#xff1f;其实对于登录这个业务&#xff0c;在平常我们实现这个功能时&#xff0c;可能大部分都是通过比对用户名和密码&#xff0c;只要正确&#xff0c;就登录成功&#xff…

美团外卖搜索基于Elasticsearch的优化实践--图文解析

美团外卖搜索基于Elasticsearch的优化实践–图文解析 前言 美团在外卖搜索业务场景中大规模地使用了 Elasticsearch 作为底层检索引擎&#xff0c;随着业务量越来越大&#xff0c;检索速度变慢了&#xff0c;CPU快累趴了&#xff0c;所以要进行优化。经过检测&#xff0c;发现…

智慧校园-办公管理系统总体概述

智慧校园行政办公系统是专为高校及教育机构定制的数字化办公解决方案&#xff0c;它整合了众多办公应用与服务&#xff0c;旨在全面提升校园行政管理的效率与便捷性&#xff0c;推动信息的自由流动&#xff0c;实现绿色无纸化办公环境。该系统作为一个综合平台&#xff0c;将日…

redis实战-缓存穿透问题及解决方案

定义理解 缓存穿透&#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远都不会生效&#xff08;只有数据库查到了&#xff0c;才会让redis缓存&#xff0c;但现在的问题是查不到&#xff09;&#xff0c;会频繁的去访问数据库。 解决…

【Spring】DAO 和 Repository 的区别

DAO 和 Repository 的区别 1.概述2.DAO 模式2.1 User2.2 UserDao2.3 UserDaoImpl 3.Repository 模式3.1 UserRepository3.2 UserRepositoryImpl 4.具有多个 DAO 的 Repository 模式4.1 Tweet4.2 TweetDao 和 TweetDaoImpl4.3 增强 User 域4.4 UserRepositoryImpl 5.比较两种模式…

RabbitMQ实践——临时队列

临时队列是一种自动删除队列。当这个队列被创建后&#xff0c;如果没有消费者监听&#xff0c;则会一直存在&#xff0c;还可以不断向其发布消息。但是一旦的消费者开始监听&#xff0c;然后断开监听后&#xff0c;它就会被自动删除。 新建自动删除队列 我们创建一个名字叫qu…

【CodinGame】CLASH OF CODE - 20240630

前言 本文是CodinGame&#xff08;图片来自此&#xff09;随手做的几个&#xff0c;供记录用 要求&#xff1a; 代码 import math import syss input()for n in range(len(s)):print(s[n:])要求 代码 import sys import math# Auto-generated code below aims at helpi…

大模型压缩量化方案怎么选?无问芯穹Qllm-Eval量化方案全面评估:多模型、多参数、多维度

基于 Transformer架构的大型语言模型在各种基准测试中展现出优异性能&#xff0c;但数百亿、千亿乃至万亿量级的参数规模会带来高昂的服务成本。例如GPT-3有1750亿参数&#xff0c;采用FP16存储&#xff0c;模型大小约为350GB&#xff0c;而即使是英伟达最新的B200 GPU 内存也只…

SpringBoot使用redis 笔记(视频摘抄 哔哩哔哩博主(感谢!):遇见狂神)

springboot集成redis步骤 1.创建springboot项目 2.配置连接 3.测试 创建springboot项目 创建以一个Maven项目 创建之后查看pom.xml配置文件&#xff0c;可以看到 pom文件里面导入了 data-redis 的依赖&#xff0c;那我们就可以在知道&#xff0c;springboot集成redis操作…