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

在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. HDFS的数据流读写数据 (面试开发重点)

    1 HDFS写数据流程 1.1 剖析文件写入 HDFS写数据流程,如图所示 1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是 ...

  2. Linux内核之 内存管理

    前面几篇介绍了进程的一些知识,从这篇开始介绍内存.文件.IO等知识,发现更不好写哈哈.但还是有必要记录下自己的所学所思.供后续翻阅,同时写作也是一个巩固的过程. 这些知识以前有文档涉及过,但是角度不同 ...

  3. cannot find package "cloud.google.com/go/compute/metadata"

    问题: cannot find package "cloud.google.com/go/compute/metadata" 解决: mkdir $GOPATH/src/cloud ...

  4. Spring Boot 集成 Memcached

    Memcached 介绍 Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站 ...

  5. python列表表达式

    [expression for i in iterable if condition] expression 就是对每一个元素的具体操作表达式;iterable是某个可迭代对象,如列表,元组或字符串等 ...

  6. 两台Linux服务器文件同步

    在给公司或者学校做系统部署的时候,为了数据安全往往我们至少需要两台服务器,接下来请看: 我们要实现的是把客户端(192.168.0.1)的复制到目标服务器(192.168.0.2) 一.目标服务器 1 ...

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

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

  8. py_选择排序

    # 选择排序 # 一趟排序记录最小值,放到第一个位置 #再一趟排序记录记录列表无序区最小的数,放到第二个位置 #.... # 关键点:有序区.无序区.无序区最小值 #方法一 def select_So ...

  9. Python 编程开发 实用经验和技巧

    文章目录 一.小数保留指定位小数 1.%f 方法 2.format函数 3.round()函数 4.直接截断 二.判断变量的数据类型的两种方法 1.type(): 2.isinstance() 三.p ...

  10. C# aspx 禁止文本款输入缓的几种方法

    1   在ASP.NET中可以在页面中加入以下内容: <%@ OutputCache Location="None" VaryByParam="None" ...