栈是非常重要的数据结构,栈具有后进先出的特点。

在JVM内部,每个线程维护一个栈,对于每个方法调用,入栈一个元素,成为栈帧,当方法执行完成后,对应的栈帧出栈。

栈帧中,也包含一个栈,称为操作数栈。

一、定义栈

public interface Stack<Item> {
// 添加一个元素
void push(Item item);
// 删除最近添加的元素
Item pop();
// 栈是否为空
boolean isEmpty();
// 栈中的元素数量
int size();
}

二、数组实现

/**
* 数组实现
* @param <Item>
*/
public class ResizingArrayStack<Item> implements Stack<Item>, Iterable<Item> {
private Item[] a;
// 表示栈实际大小
int N; /**
* 初始的数组容量为16
*/
public ResizingArrayStack(){ this(16); }
public ResizingArrayStack(int cap){ a = (Item[]) new Object[cap]; }
@Override
public void push(Item item) {
if(N == a.length) resize(2 * N);
a[N++] = item;
} /**
* 扩容,每次扩2倍的空间
* @param size
*/
private void resize(int size) {
if(size <= 16){
return;
}
System.out.println("触发扩容,原容量: " + a.length + ", 扩容后:" + size);
Item[] temp = (Item[]) new Object[size];
for (int i = 0; i < N; i++) {
temp[i] = a[i];
}
a = temp;
}
@Override
public Item pop() {
if(N == a.length / 4) resize(a.length / 2);
return a[--N];
}
@Override
public boolean isEmpty() { return N == 0; }
@Override
public int size() { return N; }
// *********** 以下代码与算法实现无关,仅为方便测试使用 *************
@Override
public Iterator<Item> iterator() {
return new Iterator<Item>() {
private int i = N;
@Override
public boolean hasNext() { return i>0; } @Override
public Item next() { return a[--i]; } @Override
public void remove() { } };
} public void print(){
System.out.print("当前元素(自栈顶至栈底):\t");
Iterator<Item> iterator = iterator();
while (iterator.hasNext()){
System.out.print(iterator.next() + "\t");
} System.out.println();
}
}

三、链表实现

/**
* 链表实现
* @param <Item>
*/
public class LinkStack<Item> implements Stack<Item>, Iterable<Item> {
private Node<Item> top;
private int N; private class Node<Item>{ Item item;Node next;} /**
* 入栈
* @param item
*/
@Override
public void push(Item item) {
Node node = new Node();
node.item = item;
node.next = top;
top = node;
N++;
}
/**
* 出栈
* @return
*/
@Override
public Item pop() {
Item item = top.item;
top = top.next;
N--;
return item;
}
@Override
public boolean isEmpty() { return N==0; }
@Override
public int size() { return N; } //*********** 以下代码与算法实现无关,仅为方便测试使用 *************
@Override
public Iterator<Item> iterator() {
return new Iterator<Item>() {
Node<Item> temp = top;
@Override
public boolean hasNext() {
return temp != null;
}
@Override
public Item next() {
Item item = temp.item;
temp = temp.next;
return item;
}
};
}
public void print(){
System.out.print("当前元素(自栈顶至栈底):\t");
Iterator<Item> iterator = iterator();
while (iterator.hasNext()){
System.out.print(iterator.next() + "\t");
}
System.out.println();
}
}

四、测试结果

public class StackTest {
@Test
public void arrayStackTest(){
// ResizingArrayStack<String> stack = new ResizingArrayStack<>();
LinkStack<String> stack = new LinkStack<>();
System.out.print("初始化后, ");
stack.print();
String pop;
//压入元素to
System.out.print("入栈:to,");
stack.push("to");
stack.print();
//压入元素be
System.out.print("入栈:be,");
stack.push("be");
stack.print();
//压入元素or
System.out.print("入栈:or,");
stack.push("or");
stack.print();
//压入元素not
System.out.print("入栈:not,");
stack.push("not");
stack.print();
//压入元素to
System.out.print("入栈:to,");
stack.push("to");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素be
System.out.print("入栈:be,");
stack.push("be");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素that
System.out.print("入栈:that,");
stack.push("that");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//弹出元素
pop = stack.pop();
System.out.print("出栈:" + pop + ",");
stack.print();
//压入元素is
System.out.print("入栈:is,");
stack.push("is");
stack.print();
} @Test
public void test2(){
ResizingArrayStack<Integer> stack = new ResizingArrayStack<>(); for (int i = 0; i < 100; i++) {
stack.push(i);
}
}
}

测试结果如下:

test1

test2

五、多说一点

在网上搜索数组和链表的区别时,最常见的说法是:数组访问较快,插入操作较慢;链表访问操作慢,修改便捷。

在实现栈时,由于栈仅涉及在最后插入一个值、在最后删除一个值,所以在这里比较2种实现方式优劣时,不能按照上述方式描述。

个人认为,数组实现,劣势在于需要扩容操作;链表实现,需要额外维护一个指向next的链接,占用更多空间;总体来说,2种实现方式优劣不明显,均比较合适。

栈的Java实现-分别使用数组和链表的更多相关文章

  1. “全栈2019”Java第三十一章:二维数组和多维数组详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  2. “全栈2019”Java第三十章:数组详解(下篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. “全栈2019”Java第二十九章:数组详解(中篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. “全栈2019”Java第二十八章:数组详解(上篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 栈的数组和链表实现(Java实现)

    我以前用JavaScript写过栈和队列,这里初学Java,于是想来实现栈,基于数组和链表. 下面上代码: import java.io.*; //用接口来存放需要的所有操作 interface st ...

  6. Java之--Java语言基础组成—数组

    Java语言基础组成-数组 Java语言由8个模块构成,分别为:关键字.标识符(包名.类名.接口名.常量名.变量名等).注释.常量和变量.运算符.语句.函数.数组. 本片主要介绍Java中的数组,数组 ...

  7. 【老鸟学算法】包含 min函数的栈设计——java实现

    要求: 1. 定义栈的数据结构,要求添加一个 min函数,能够得到栈的最小元素. 2. 要求函数 min.push 以及 pop 的时间复杂度都是 O(1). 这是考验“栈”数据结构设计.众所周知,栈 ...

  8. 剑指Offer——栈的java实现和栈的应用举例

    剑指Offer--栈的java实现和栈的应用举例 栈是一种先进后出的数据结构, 栈的实现如下: 首先定义了栈需要实现的接口: public interface MyStack<T> { / ...

  9. Java基础--二维数组

    1.二维数组的定义 二维数组表示行列二维结构,在栈空间中的二维数组的地址指向堆空间中的一维数组,堆空间中的一维数组的地址又指向一维数组所在的内存空间. 2.二维数组的声明 二维数组声明有3种方式,推荐 ...

随机推荐

  1. java class类和object类

    Class类 介绍 Java的Class类是java反射机制的基础,通过Class类我们可以获得关于一个类的相关信息 Java.lang.Class是一个比较特殊的类,它用于封装被装入到JVM中的类( ...

  2. Webfunny知识分享:JS错误监控

    现在的前端开发已不再是刀耕火种的年代了,各种框架.编译工具层出不穷,前端监控系统也不甘其后,遍地开花. 前端正承受着越来越重的职责,前端的业务也变得越来越复杂,此时此刻我们就更需要一套完善的监控系统来 ...

  3. SpringBoot--- SpringSecurity进行注销,权限控制

    SpringBoot--- SpringSecurity进行注销,权限控制 环境 IDEA :2020.1 Maven:3.5.6 SpringBoot: 2.0.9 (与此前整合的版本2.3.3 不 ...

  4. 服务器基本配置(ubuntu)

    服务器基本配置(ubuntu) 学习目标: 修改初始服务器名字(ubuntu 16.04 ) 修改初始服务器名字(ubuntu 18.04 ) ubuntu换源 更改默认python版本 安装软件出现 ...

  5. secureCRT中sftp的使用

    securecrt 按下ALT+P就开启新的会话 进行ftp操作.输入:help命令,显示该FTP提供所有的命令pwd: 查询linux主机所在目录(也就是远程主机目录)lpwd: 查询本地目录(一般 ...

  6. 焦大:做SEO应该研究的用户需求的方向

    http://www.wocaoseo.com/thread-60-1-1.html 最近收到打击很大,收获也颇多,这一切都莫过于用户需求的问题.我曾经给我弟说过,我对检索排名特征识别.提取和计算自认 ...

  7. 【Go语言探险】线上奇怪日志问题的排查

    最近在日志中发现一些奇怪的日志,大致长这样: Error 2020-08-28 06:59:38.813+00:00 ... _msg=get immersion context, fetch tra ...

  8. 广度优先搜索(BFS)解题总结

    定义 广度优先搜索算法(Breadth-First-Search),是一种图形搜索算法. 简单的说,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点. 如果所有节点均被访问,则算法中止. B ...

  9. 冒泡排序的优化方案BubbleSort

    <?php /** * 冒泡排序 * * ------------------------------------------------------------- * 思路分析:就是像冒泡一样 ...

  10. 攻防世界——web新手练习区解题总结<2>(5-8题)

    第五题cookie: 所需工具:burpsuite(需自行下载) 老规矩看完题,先获取在线场景,得到如下网页 那么什么是cookie呢?大体上就是网站为了识别用户身份而储存在用户本地终端上的数据,类型 ...