栈的Java实现-分别使用数组和链表
栈是非常重要的数据结构,栈具有后进先出的特点。
在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实现-分别使用数组和链表的更多相关文章
- “全栈2019”Java第三十一章:二维数组和多维数组详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第三十章:数组详解(下篇)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十九章:数组详解(中篇)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十八章:数组详解(上篇)
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 栈的数组和链表实现(Java实现)
我以前用JavaScript写过栈和队列,这里初学Java,于是想来实现栈,基于数组和链表. 下面上代码: import java.io.*; //用接口来存放需要的所有操作 interface st ...
- Java之--Java语言基础组成—数组
Java语言基础组成-数组 Java语言由8个模块构成,分别为:关键字.标识符(包名.类名.接口名.常量名.变量名等).注释.常量和变量.运算符.语句.函数.数组. 本片主要介绍Java中的数组,数组 ...
- 【老鸟学算法】包含 min函数的栈设计——java实现
要求: 1. 定义栈的数据结构,要求添加一个 min函数,能够得到栈的最小元素. 2. 要求函数 min.push 以及 pop 的时间复杂度都是 O(1). 这是考验“栈”数据结构设计.众所周知,栈 ...
- 剑指Offer——栈的java实现和栈的应用举例
剑指Offer--栈的java实现和栈的应用举例 栈是一种先进后出的数据结构, 栈的实现如下: 首先定义了栈需要实现的接口: public interface MyStack<T> { / ...
- Java基础--二维数组
1.二维数组的定义 二维数组表示行列二维结构,在栈空间中的二维数组的地址指向堆空间中的一维数组,堆空间中的一维数组的地址又指向一维数组所在的内存空间. 2.二维数组的声明 二维数组声明有3种方式,推荐 ...
随机推荐
- 用Python的Pandas和Matplotlib绘制股票唐奇安通道,布林带通道和鳄鱼组线
我最近出了一本书,<基于股票大数据分析的Python入门实战 视频教学版>,京东链接:https://item.jd.com/69241653952.html,在其中给出了MACD,KDJ ...
- C++ Templates (1.4 默认模板实参 Default Template Arguments)
返回完整目录 目录 1.4 默认模板实参 Default Template Arguments 1.4 默认模板实参 Default Template Arguments 可以为模板参数定义默认值,这 ...
- SpringBoot + SpringCloud Hystrix 实现服务熔断
什么是Hystrix 在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务,有的时候某些依赖服务出现故障也是很常见的. Hystrix是Netflix公司开源的一个项目,它提 ...
- MySQL引擎【转】
数据库引擎介绍MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外 ...
- C++ int与char[]的相互转换
C++ int与char[]的相互转换 一.itoa函数与atio函数①把int类型数字转成char类型,可以使用itoa函数. itoa函数原型: char*itoa(int value,char* ...
- 深入探究.Net Core Configuration读取配置的优先级
前言 在之前的文章.Net Core Configuration源码探究一文中我们曾解读过Configuration的工作原理,也.Net Core Configuration Etcd数据源 ...
- 力扣Leetcode 572. 另一个树的子树
另一个树的子树 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树.s 的一个子树包括 s 的一个节点和这个节点的所有子孙.s 也可以看做它自身的一棵子树. 示例 ...
- Flink从入门到入土(详细教程)
和其他所有的计算框架一样,flink也有一些基础的开发步骤以及基础,核心的API,从开发步骤的角度来讲,主要分为四大部分 1.Environment Flink Job在提交执行计算时,需要首先建立和 ...
- Unity 打AssetBundle和加载方案
一.如何组织assetBundle: unity5以前,打包需要自己去找依赖,然后需要按照拓扑图顺序压入AB栈,这样在最后打AB时才能有效利用依赖(栈内已有的AB才能作为依赖). unity5.x后, ...
- java返回树形结构的正确姿势
业务场景 通常我们前端需要一个树形的导航菜单或者分类菜单,如后台权限管理中的权限树,亦或者下面例子中商城系统的商品分类多级菜单(一般为三级菜单) 数据库设计 数据库设计,采用parentId来指向自己 ...