热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

动图演示:手撸堆栈的两种实现方法!

作者|王磊来源|Java中文社群(ID:Javacn666)头图|CSDN下载自东方IC随着软件开发行业竞争的日益激烈,面试

作者 | 王磊

来源 | Java中文社群(ID:Javacn666)

头图 |  CSDN 下载自东方IC

随着软件开发行业竞争的日益激烈,面试的难度也在逐渐增加,因为企业要从众多的面试人中选出最优秀的人,只能提高面试的难度,而算法和数据结构比较烧脑的硬核技能之一,自然也就成了面试的首选科目。并且随着时间的推移,算法和数据结构出现的频率和占比也会不断增加,因此为了顺应时代发展的潮流,我们也要做一些调整,所以在后面的一些文章中,我会陆续更新一些关于算法和数据结构的文章,希望大家能够喜欢。

PS:当然随着智能系统的普及(如今日头条和抖音),算法和数据结构在企业中应用也越来越多,因此学习算法和数据结构也是迫在眉睫的事了。

栈定义

栈(Stack)又叫堆栈(简称栈),它是在同一端进行插入和删除数据的线性表。

栈是最基础也是最常见的数据结构之一,它的数据结构和操作流程如下图所示:

其中,允许进行插入和删除的一端叫作栈顶(Top),另一端叫作栈底(Bottom),栈底固定,栈顶浮动。

当栈中的元素为零时,该栈叫作空栈。添加数据时一般叫作入栈或进栈(Push),删除数据叫作出栈或退栈(Pop)。栈是后进先出(Last In First Out,LIFO)的线性表。

物理结构&逻辑结构

在手撸算法之前,我们先来认识一下数据结构中的两个重要概念:物理结构和逻辑结构。

当谈到“物理”和“逻辑”一词时,我们可以会想到数据库中的逻辑删除和物理删除。

所谓的物理删除是指通过删除命令真实的将数据从物理结构中删除的过程;而逻辑删除是指通过修改命令将数据更改为“已删除”的状态,并非真实的删除数据。

这里的逻辑结构和物理结构和上面的概念类似,所谓的物理结构是指可以将数据存储在物理空间中,比如数组和链表都属于物理数据结构;而逻辑结构则是用于描述数据间的逻辑关系的,比如本文要讲的栈就属于逻辑结构。

可能有些人看到这里就蒙了,没关系,我这里举一个例子你就明白了。

如果用人来表示物理结构和逻辑结构的话,那么真实存在的有血有肉的人就属于物理结构,而人的思想和信念就属于逻辑结构了。

自定义栈I:数组实现

通过上面的内容,我们知道了栈属于逻辑结构,因此它的实现方式就可以有很多种了,比如数组的实现方式或者是链表的实现方式。那么我们就先用数组实现一下,栈的主要方法有:

1、定义结构

那么我们先来定义它的结构:

public class MyStack {private Object[] value &#61; null; // 栈存储容器private int top &#61; -1; // 栈顶&#xff08;的指针&#xff09;private int maxSize &#61; 0; // 栈容量// 构造函数&#xff08;初始化默认容量&#xff09;MyStack() {this.maxSize &#61; 10;value&#61;new Object[9];}// 有参构造函数MyStack(int initSize) throws Exception {if (initSize <&#61; 0) {throw new Exception("栈容量必须大于 0");} else {value &#61; new Object[initSize];maxSize &#61; initSize;top &#61; -1;}}
}

其中栈中数据会存储在 Object[] value 数组中&#xff0c;top 变量代表栈顶的指针&#xff0c;它其实存储的是栈顶元素的下标&#xff0c;会随着入栈不断变化&#xff08;后进先出&#xff09;&#xff0c;maxSize 表示栈的最大容量。

2、入栈

此方法是给栈添加数据的&#xff0c;实现代码如下&#xff1a;

// 入栈&#xff08;数据添加&#xff09;
public boolean push(E e) throws Exception {if (maxSize - 1 &#61;&#61; top) {throw new Exception("入栈失败&#xff0c;栈已满");} else {value[&#43;&#43;top] &#61; e;return true;}
}

每次当有数据插入时&#xff0c;只需在数组中添加一个值&#xff0c;并将栈顶的下标 &#43;1 即可。

入栈操作如下图所示&#xff1a;

3、出栈

此方法是删除栈中的数据的&#xff0c;实现代码如下&#xff1a;

// 数据移除&#xff08;出栈&#xff09;
public E pop() throws Exception {if (top <&#61; -1) {throw new Exception("移除失败&#xff0c;栈中已无数据");} else {return (E) value[top--];}
}

出栈只需删除数组中栈顶数据&#xff08;最后加入的数据&#xff09;&#xff0c;并修改栈顶下标 -1 即可。

出栈操作如下图所示&#xff1a;

4、数据查询

除了以上操作方法之外&#xff0c;我们还需要添加一个查询栈顶数据的方法&#xff1a;

// 数据查询
public E peep() throws Exception {if (top <&#61; -1) {throw new Exception("移除失败&#xff0c;栈中已无数据");} else {return (E) value[top];}
}

5、代码测试

到此为止栈的数据结构就已经实现完了&#xff0c;接下来我们来测试一下&#xff1a;

// 代码测试
public static void main(String[] args) throws Exception {MyStack stack &#61; new MyStack(10);stack.push("Hello");stack.push("Java");System.out.println(stack.peep());stack.pop();System.out.println(stack.pop());
}

以上程序的执行结果为&#xff1a;

Java

Hello

从上述代码可以看出&#xff0c;我们添加栈的顺序是 HelloJava 而输出的顺序是 Java、 Hello 符合栈的定义&#xff08;后进先出&#xff09;。

自定义栈II&#xff1a;链表实现

除了数组之外&#xff0c;我们可以还可使用链表来实现栈结构&#xff0c;它的实现稍微复杂一些&#xff0c;我们先来看链表本身的数据结构&#xff1a;

使用链表实现栈的流程如下&#xff1a;

也就是说&#xff0c;入栈时我们将数据存储在链表的头部&#xff0c;出栈时我们从头部进行移除&#xff0c;并将栈顶指针指向原头部元素的下一个元素&#xff0c;实现代码如下。

我们先来定义一个链表节点&#xff1a;

public class Node {Object value; // 每个节点的数据Node next; // 下一个节点public Node(Object value) {this(value, null);}/*** 创建新节点* &#64;param value 当前节点数据* &#64;param next 指向下一个节点&#xff08;头插法&#xff09;*/public Node(Object value, Node next) {this.value &#61; value;this.next &#61; next;}
}

接下来我们使用链表来实现一个完整的栈&#xff1a;

public class StackByLinked {private Node top &#61; null; // 栈顶数据private int maxSize &#61; 0; // 栈最大容量private int leng &#61; 0; // 栈实际容量public StackByLinked(int initSize) throws Exception {if (initSize <&#61; 0) {throw new Exception("栈容量不能小于等于0");}top &#61; null;maxSize &#61; initSize;leng &#61; 0;}/*** 容量是否已满* &#64;return*/public boolean isFull() {return leng >&#61; maxSize;}/*** 是否为空* &#64;return*/public boolean isEmpty() {return leng <&#61; 0;}/*** 入栈* &#64;param val* &#64;return* &#64;throws Exception*/public boolean push(Object val) throws Exception {if (this.isFull()) {// 容量已满throw new Exception("容量已满");}top &#61; new Node(val, top); // 存入信息&#xff0c;并将当前节点设置为头节点leng&#43;&#43;;return true;}/*** 出栈&#xff08;移除&#xff09;* &#64;return* &#64;throws Exception*/public Node pop() throws Exception {if (this.isEmpty()) {throw new Exception("栈为空&#xff0c;无法进行移除操作");}Node item &#61; top; // 返回当前元素top &#61; top.next;leng--;return item;}/*** 查询栈顶信息* &#64;return*/public Node peek() throws Exception {if (isEmpty()) {throw new Exception("你操作的是一个空栈");}return top;}// 代码测试public static void main(String[] args) throws Exception {StackByLinked stack &#61; new StackByLinked(10);stack.push("Hello");stack.push("Java");System.out.println(stack.peek().value);stack.pop();System.out.println(stack.pop().value);}
}

以上程序的执行结果是&#xff1a;

Java

Hello


总结

本文我们使用了数组和链表等物理结构来实现了栈&#xff0c;当然我们也可以使用其他容器来实现&#xff0c;比如 Java 中的 List&#xff0c;我们只需要保证在操作栈时是后进先出的执行顺序&#xff0c;并且至少包含 3 个重要方法&#xff1a;入栈、出栈和查询栈顶元素就可以了。

算法和数据结构的学习是 3 分学 7 分练&#xff0c;只看不练是没办法学好算法的&#xff0c;而且学习算法和数据结构是一个循序渐进的过程&#xff0c;短时间内不会有明显的收效。因为这些算法经过了几百年的发展和积累才得以流传下来的&#xff0c;所以想要“玩得转”还需要一点耐心。

这里给你讲一个学习算法的“秘诀”&#xff1a;看不懂的知识要反复看&#xff0c;如果反复看还是看不懂&#xff0c;那么别着急&#xff0c;休息一下再继续看&#xff01;相信我&#xff0c;对于学习算法这件事&#xff0c;所有人的过程都是一样的。

更多精彩推荐
☞2020互联网公司中秋礼盒大比拼&#xff01;&#xff08;文末送福利&#xff09;
☞如果国庆加班发三倍工资&#xff0c;你愿意吗&#xff1f;| 每日趣闻
☞Azure Arc 正式商用、Power Platform&#43;GitHub 世纪牵手&#xff0c;一文看懂 Ignite 2020
☞自拍卡通化&#xff0c;拯救动画师&#xff0c;StyleGAN再次玩出新花样
☞深夜&#xff0c;我偷听到程序员要对session下手......
☞DeFi深陷大规模“走出去”困境&#xff1f;Wing祭出三大杀手锏

点分享点点赞点在看


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
author-avatar
瀑布下的鱼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有