1.. 栈的特点:
  • 栈也是一种线性结构;
  • 相比数组,栈所对应的操作是数组的子集;
  • 栈只能从一端添加元素,也只能从这一端取出元素,这一端通常称之为"栈顶";
  • 向栈中添加元素的过程,称之为"入栈",从栈中取出元素的过程称之为"出栈";
  • 栈的形象化描述如下图:
  • "入栈"的顺序若为1-2-3-4,那么出栈的顺序只能为4-3-2-1,即,栈是一种"后进先出"(Last In First Out)的数据结构;
  • 从用户的角度,只能看到栈顶元素,其它元素对用户是不可见的;
  • 在计算机世界里,"栈"有着不可思意的作用;
2.. 简单举例"栈"的应用
  • 应用程序中的"撤销"(Undo)操作的底层原理就是通过"栈"来实现的;
  • 程序调用的系统栈,通过下图简单示例:
3.. 栈的实现
  • 任务目标如下:
  • Stack<E>
    ·void push(E)   //入栈
    ·E pop()    // 出栈
    ·E peek()   // 查看栈顶元素
    ·int getSize()  // 查看栈中共有多少元素
    ·boolean isEmpty()   // 查看栈是否为空

  • 需要提一下,从用户的角度来看,只要实现上述操作就好,具体底层实现,用户并不关心,实际上,底层确实有多种实现方式。
  • 我们准备在之前实现的动态数组基础上,来实现"栈"这种数据结构。
  • 先定义一个接口Interface,如下:
  • public interface Stack<E> {  // 支持泛型int getSize();boolean isEmpty();void push(E e);E pop();E peek();
    }

  • 然后实现一个ArrayStack类,如下:
  • public class ArrayStack<E> implements Stack<E> {Array<E> array;//构造函数public ArrayStack(int capacity) {array = new Array<>(capacity);}//无参数构造函数public ArrayStack() {array = new Array<>();}//实现getSize方法
        @Overridepublic int getSize() {return array.getSize();}//实现isEmpty方法
        @Overridepublic boolean isEmpty() {return array.isEmpty();}//实现getCapacity方法public int getCapacity() {return array.getCapacity();}//实现push方法
        @Overridepublic void push(E e) {array.addLast(e);}//实现pop方法
        @Overridepublic E pop() {return array.removeLast();}//实现peek方法public E peek() {return array.getLast();}//方便打印测试
        @Overridepublic String toString() {StringBuilder res = new StringBuilder();res.append("Stack: ");res.append('[');for (int i = 0; i < array.getSize(); i++) {res.append(array.get(i));if (i != array.getSize() - 1) {res.append(", ");}}res.append("] top");return res.toString();}
    }

  • Array类的业务逻辑如下:
  • public class Array<E> {private E[] data;  //设置为private,不希望用户从外部直接获取这些信息,防止用户篡改数据private int size;//构造函数,传入数组的容量capacity构造Arraypublic Array(int capacity) {data = (E[]) new Object[capacity];size = 0;}//无参数构造函数,默认数组容量capacity=10public Array() {this(10);    //这里的capacity是IDE自动添加的提示信息,实际不存在
        }//获取数组中的元素个数public int getSize() {return size;}//获取数组的容量public int getCapacity() {return data.length;}//判断数组是否为空public boolean isEmpty() {return size == 0;}//向数组末尾添加一个新元素epublic void addLast(E e) {add(size, e);}//向数组开头添加一个新元素epublic void addFirst(E e) {add(0, e);}//在index位置插入一个新元素epublic void add(int index, E e) {if (index < 0 || index > size) {throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size");}if (size == data.length) {resize(2 * size); //扩大为原容量的2倍
            }for (int i = size - 1; i >= index; i--) {data[i + 1] = data[i];}data[index] = e;size++;}//获取index位置的元素public E get(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Get failed. Index is illegal.");}return data[index];}//获取最后一个元素public E getLast() {return get(size - 1);}//获取开头的元素public E getFirst() {return get(0);}//修改index位置的元素为epublic void set(int index, E e) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Set failed. Index is illegal.");}data[index] = e;}//查找数组中是否存在元素epublic boolean contains(E e) {for (int i = 0; i < size; i++) {if (data[i].equals(e)) {return true;}}return false;}//查看数组中元素e的索引,若找不到元素e,返回-1public int find(E e) {for (int i = 0; i < size; i++) {if (data[i].equals(e)) {return i;}}return -1;}//删除掉index位置的元素,并且返回删除的元素public E remove(int index) {if (index < 0 || index >= size) {throw new IllegalArgumentException("Remove failed. Index is illegal.");}E ret = data[index];for (int i = index + 1; i < size; i++) {data[i - 1] = data[i];}size--;   //data[size]会指向一个类对象,这部分空间不会被释放loitering objectsdata[size] = null;if (size == data.length / 4 && data.length / 2 != 0) {resize(data.length / 2);  //被利用的空间等于总空间的一半时,将数组容量减少一半
            }return ret;}//删除掉数组开头的元素,并返回删除的元素public E removeFirst() {return remove(0);}//删除掉数组末尾的元素,并返回删除的元素public E removeLast() {return remove(size - 1);}//如果数组中有元素e,那么将其删除,否则什么也不做public void removeElement(E e) {int index = find(e);if (index != -1) {remove(index);}}@Overridepublic String toString() {    //覆盖父类的toString方法
    StringBuilder res = new StringBuilder();res.append(String.format("Array: size=%d, capacity=%d\n", size, data.length));res.append('[');for (int i = 0; i < size; i++) {res.append(data[i]);if (i != size - 1) {res.append(", ");}}res.append(']');return res.toString();}private void resize(int newCapacity) {E[] newData = (E[]) new Object[newCapacity];for (int i = 0; i < size; i++) {newData[i] = data[i];}data = newData;}
    }

4.. 对我们实现的栈进行测试:

  • public class Main {public static void main(String[] args) {ArrayStack<Integer> stack = new ArrayStack<>();//测试入栈pushfor (int i = 0; i < 5; i++) {stack.push(i);System.out.println(stack);}//测试出栈
            stack.pop();System.out.println(stack);}
    }

  • 输出结果如下:
  • Stack: [0] top
    Stack: [0, 1] top
    Stack: [0, 1, 2] top
    Stack: [0, 1, 2, 3] top
    Stack: [0, 1, 2, 3, 4] top
    Stack: [0, 1, 2, 3] top

5.. 栈的时间复杂度分析

  • Stack<E>
    ·void push(E)    O(1) 均摊
    ·E pop()    O(1)   均摊
    ·E peek()    O(1)
    ·int getSize()    O(1)
    ·boolean isEmpty()    O(1)

6.. 栈的另外一个应用——括号匹配
  • 业务逻辑如下:
  • import java.util.Stack;class Solution {public boolean isValid(String s) {Stack<Character> stack = new Stack<>();for (int i = 0; i < s.length(); i++) {char c = s.charAt(i);if (c == '(' || c == '[' || c == '{') {stack.push(c);} else {if (stack.isEmpty()) {return false;}char topChar = stack.pop();if (topChar == '(' && c != ')') {return false;}if (topChar == '[' && c != ']') {return false;}if (topChar == '{' && c != '}') {return false;}}}return stack.isEmpty();   //这里很巧妙
        }//测试public static void main(String[] args){System.out.println((new Solution()).isValid("()"));System.out.println((new Solution()).isValid("()[]}{"));System.out.println((new Solution()).isValid("({[]})"));System.out.println((new Solution()).isValid("({)}[]"));}
    }

转载于:https://www.cnblogs.com/xuezou/p/9276961.html

第二十三篇 玩转数据结构——栈(Stack)相关推荐

  1. 第二十三篇玩转【斗鱼直播APP】系列之直播总体概览

    直播总体概览 概述 直播的现状 2016年,是一个直播年.直播行业快速发展,同时也滋生了大大小小上千家相关的公司. 中国互联网络信息中心发布的报告显示,截至今年6月,我国网络直播用户规模达到3.25亿 ...

  2. 九宫怎么排列和使用_广告视频配音剪映零基础入门教程第二十三篇:剪辑之九宫格教程...

    朋友圈最火九宫格视频你们知道是怎样制作的吗?我们常常在玩朋友圈的时候想用九宫格照片,但是你们有没有遇到这种情况,想玩九宫格却发现找不到那么多能用的照片,那这时候怎么办呢?玩腻了平常图片的发法,今天我们 ...

  3. STM32F429第二十三篇之电容按键

    文章目录 前言 硬件分析 原理 源程序 主函数 TpadInit GetTimeUntoched GetTimeCharge TpadScan 前言 本文主要介绍电容按键的原理与使用方法,主要使用的A ...

  4. python数据结构和算法 时间复杂度分析 乱序单词检测 线性数据结构 栈stack 字符匹配 表达式求值 queue队列 链表 递归 动态规划 排序和搜索 树 图

    python数据结构和算法 参考 本文github 计算机科学是解决问题的研究.计算机科学使用抽象作为表示过程和数据的工具.抽象的数据类型允许程序员通过隐藏数据的细节来管理问题领域的复杂性.Pytho ...

  5. 在java的实现栈的插入数据_Java实现数据结构栈stack和队列Queue

    回顾JDK提供的集合类 容器(集合)框架如下: 集合类存放于java.util包中.集合类存放的都是对象的引用,而非对象本身. 集合类型主要有3种:set(集).list(列表)和map(映射). C ...

  6. java 栈队列区别是什么意思_Java实现数据结构栈stack和队列Queue是什么?

    回顾JDK提供的集合类 容器(集合)框架如下: 集合类存放于java.util包中.集合类存放的都是对象的引用,而非对象本身. 集合类型主要有3种:set(集).list(列表)和map(映射). C ...

  7. 第二十二篇 玩转数据结构——构建动态数组

    1.. 数组基础 数组就是把数据码成一排进行存放. Java中,数组的每个元素类型必须相同,可以都为int类型,string类型,甚至是自定义类型. 数组的命名要语义化,例如,如果数组用来存放学生的成 ...

  8. python全栈开发基础【第二十三篇】线程

    一.什么是线程 线程:顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才 ...

  9. python基本数据结构栈stack和队列queue

    1,栈,后进先出,多用于反转 Python里面实现栈,就是把list包装成一个类,再添加一些方法作为栈的基本操作. 栈的实现: class Stack(object):#初始化栈为空列表def __i ...

  10. 【第二十三篇】Spring Boot集成redis

    1.1 简介 REmote DIctionary Server(Redis)是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANSI C语言 ...

最新文章

  1. makefille的使用
  2. iPhone游戏编程教程一步步教你游戏开发
  3. self的含义,为什么类调用方法时需要传参数?
  4. 服务器性能/压力测试工具http_load、webbench、ab、Siege使用教程
  5. pytorch模型部署
  6. 【解析】1057 数零壹 (20分)(进制转换)
  7. 企业有了程序员为什么还要用 低代码/无代码
  8. Java讲课笔记12:static关键字
  9. neo4j values
  10. 九、Linux的网络配置
  11. c/c++教程 - 1.4 数据类型 sizeof
  12. 领域驱动设计系列 (六):CQRS
  13. 世平数据库保密检查工具再次荣膺中国“优秀软件产品”称号
  14. 计算机网络习题——循环冗余校验
  15. java 事务管理 子父线程_java父线程子线程(转)
  16. 用outook发邮件能撤回吗?邮件怎么撤回技巧
  17. Hexo历险记之三本地安装Hexo
  18. python实现俄罗斯方块小游戏
  19. ubuntu 编译安装opencv官网教程
  20. 基于Python实现的桌面图书管理系统

热门文章

  1. R语言实战-读书笔记整理
  2. 9判断整数序列是不是二元查找树的后序遍历结果
  3. Windows Server 8 Beta 初体验之二:Hyper-v
  4. PHP设计模式——装饰器模式
  5. Qt5学习笔记之串口助手四:增加16进制/ASCII切换、周期发送
  6. chrome devTool
  7. SQLite语法与Android数据库操作
  8. linux的swap相关
  9. 小程序开发小结-线下服务器域名部署等
  10. Python爬虫从入门到放弃(十五)之 Scrapy框架中Spiders用法