N日一篇——Java实现栈
Posted 从零开始的智障生活
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了N日一篇——Java实现栈相关的知识,希望对你有一定的参考价值。
栈详解:https://blog.csdn.net/qq_34028001/article/details/119331089?spm=1001.2014.3001.5501
目录
一、顺序栈
1.1 用接口表示顺序栈抽象数据类型
public interface SequentialStack {
public void setup(int size);// 创建指定大小顺序栈
public void push(Object ob); //压栈
public Object pop(); // 出栈并返回出栈值
public Object topValue();// 返回栈顶值
public boolean isEmpty(); // 判断栈是否为空
}
1.2 带头结点的顺序栈实现
// 把索引为1的位置作为栈尾,顺序栈中索引为0的位置的数据无效,即初始化的时候,第一个结点作为无效结点
public class SStackwithHead implements SequentialStack{
private static final int DEFAULTSIZE = 10;
private int size;
private int topIndex;
private Object[] stack;
public SStackwithHead() {
setup(DEFAULTSIZE);
}
public SStackwithHead(int sz){
setup(sz);
}
@Override
public void setup(int sz) { // 初始化链式表
size = sz; // 容量
topIndex = 0;// 第一个结点,即索引为0的结点作为无效结点,作为头结点,第一个有效节点索引是1
stack = new Object[sz]; // 为栈创建内存空间
}
@Override
public void push(Object ob) {
if(topIndex<size)// topIndex代表第topIndex个有效数据
stack[++topIndex] = ob; // 先执行++topIndex
}
@Override
public Object pop() {
if(!isEmpty())
return stack[topIndex--];// 先执行stack[topIndex]
return null;
}
@Override
public Object topValue() {
if(!isEmpty())
return stack[topIndex];// 返回栈顶值
return null;
}
@Override
public boolean isEmpty() {
if(topIndex==0)
return true;
return false;
}
}
1.3 不带头结点的顺序表实现
// 把索引为0的位置作为栈尾,即栈中所有数据都是有效数据,属于不带头结点,即没有一个无效数据点
// 对于顺序栈而言,不会有线性表的插入删除操作需要花费O(n)的时间复杂度的问题,因为只会在栈顶插入
public class SStackwithoutHead implements SequentialStack{
private final static int DEFAULTSIZE = 10;
private int size; // 栈容量
private Object[] stack;
private int topIndex; // 栈顶位置,从0索引,同时topIndex+1表示栈大小
public SStackwithoutHead() {
setup(DEFAULTSIZE);
}
public SStackwithoutHead(int size) {
setup(size);
}
@Override
public void setup(int sz) { // 初始化链式表
size = sz;
topIndex = -1; // 不带头结点,所以初始化为topIndex为-1
stack = new Object[sz];
}
@Override
public void push(Object ob) {
if(topIndex<size)
stack[++topIndex] = ob;// 先执行++topIndex
}
@Override
public Object pop() {
if(!isEmpty())
return stack[topIndex--];// 先执行stack[topIndex]
else
return null;
}
@Override
public Object topValue() {
if(!isEmpty())
return stack[topIndex];
else
return null;
}
@Override
public boolean isEmpty() {
if(topIndex<0)
return true;
else
return false;
}
}
1.4 测试带头结点与不带头结点的顺序栈
public class TestSS {
public static void main(String[] args) {
char[] cArray = {'a','b','c','d'};
// 创建两个顺序表对象
SStackwithoutHead sswithoutHead = new SStackwithoutHead();
SStackwithHead sswithHead = new SStackwithHead();
// 对两个顺序栈压栈
for(char c:cArray) {
sswithoutHead.push(c);
sswithHead.push(c);
System.out.println("压栈"+c);
}
// 对不带头结点的顺序栈压栈
while(!sswithoutHead.isEmpty()) {
System.out.println("不带头结点出栈"+sswithoutHead.pop());
}
while(!sswithHead.isEmpty()) {
System.out.println("带头结点出栈"+sswithHead.pop());
}
}
}
运行结果:
压栈a
压栈b
压栈c
压栈d
不带头结点出栈d
不带头结点出栈c
不带头结点出栈b
不带头结点出栈a
带头结点出栈d
带头结点出栈c
带头结点出栈b
带头结点出栈a
二、链栈
2.1 用接口表示链栈抽象数据类型
public interface LinkedStack {
public void setup(); // 初始化链式表
public void push(Object ob); // 压栈
public Object pop(); // 出栈
public Object topValue(); // 栈顶值
public void clear(); // 清除栈
public boolean isEmpty(); // 判断是否为空
}
2.2 用简单链表类型(无功能)表示链栈的每个结点类型
// 用单链表来实现链栈的基本功能
public class Link {
private Object element; // 数据元素
private Link next; // 下一节点指针
public Object getElement() {
return element;
}
public void setElement(Object element) {
this.element = element;
}
public Link getNext() {
return next;
}
public void setNext(Link next) {
this.next = next;
}
// 设置下一节点
public Link(Object it,Link nextNode) {
element = it; // 定义数据元素
next = nextNode; // 设置下一节点
}
}
2.3 不带头结点的链栈(带头结点的链栈毫无意义)
// 链栈可以选择在链式表的表头或表尾进行插入删除,如果在表尾时间复杂度为O(n),故选择在表头,即头插法实现链栈
// 不带头结点的链式表实现。即头结点为无效数据。
public class LStackwithoutHead implements LinkedStack{
private Link top; // Link有两个数据项,element和指向下一节点的指针
public LStackwithoutHead() { // 创建链栈
setup();
}
public LStackwithoutHead(int sz) {// 创建链栈,忽略sz
setup();
}
@Override
public void setup() {// 初始化不带头结点的链栈
top = null; // 由于不带头结点,所以链栈一个开始不占任何空间
}
@Override
public void push(Object ob) {
// 将旧top作为新top的下一节点,并为新top的数据域赋值
top = new Link(ob,top);// 这个方式可以好好参考
}
@Override
public Object pop() {
if(!isEmpty()) {
Object it = top.getElement();// 获取出栈值
top = top.getNext();//出栈
return it;
}
return null;
}
@Override
public Object topValue() {
return top.getElement();
}
@Override
// 这与C语言C++不同,java有独特的垃圾收集器GC,所以不要程序员特定去释放内存
public void clear() {
top = null;
}
@Override
public boolean isEmpty() {
return top == null;
}
}
2.4 测试不带头结点的链栈
public class TestLS {
public static void main(String[] args) {
LStackwithoutHead stack = new LStackwithoutHead();
char[] cAarry = {'a','b','c','d'};
for(char c:cAarry) {
stack.push(c);
System.out.println("压栈:\\t"+c);
}
while(!stack.isEmpty()) {
System.out.println("栈顶值:\\t"+stack.topValue());
System.out.println("出栈:\\t"+stack.pop());
}
}
}
运行结果:
压栈: a
压栈: b
压栈: c
压栈: d
栈顶值: d
出栈: d
栈顶值: c
出栈: c
栈顶值: b
出栈: b
栈顶值: a
出栈: a
三、递归的实现
大多数的程序设计语言都有子程序调用。子程序调用通过把关于子程序的必要信息(包括返回地址、参数、局部变量)存储到一个栈中来实现。详细内容可以参考上面给出的链接。
即递归会有两个问题:
1. 递归太多很容易导致栈溢出;
2. 递归容易造成重复调用,如Fibonacci数列f(n)=f(n-1)+f(n-2)。
3.1 阶乘的非递归实现
这里用栈来代替递归实现阶乘,实际上,阶乘函数的一般迭代实现比栈迭代实现简单快捷。
import com.zyx.stack.linkedstack.LStackwithoutHead;
public class Example {
// 用栈的迭代代替递归的迭代实现阶乘
public static long factorial(int n) { // 要求 0<=n<21
if(n<21 && n>=0) {
LStackwithoutHead stack = new LStackwithoutHead();
while(n>1)
stack.push(new Integer(n--));// 栈顶到栈尾:2、3、...、n-1、n
long result = 1;
while(!stack.isEmpty())
result = result * ((Integer)stack.pop()).longValue();
return result;
}
return -1;
}
public static void main(String[] args) {
System.out.println("用栈的迭代实现借阶乘:");
for(int i=1;i<21;i++)
System.out.println("n="+i+"\\t结果:\\t"+factorial(i));
}
}
运行结果:
用栈的迭代实现借阶乘:
n=1 结果: 1
n=2 结果: 2
n=3 结果: 6
n=4 结果: 24
n=5 结果: 120
n=6 结果: 720
n=7 结果: 5040
n=8 结果: 40320
n=9 结果: 362880
n=10 结果: 3628800
n=11 结果: 39916800
n=12 结果: 479001600
n=13 结果: 6227020800
n=14 结果: 87178291200
n=15 结果: 1307674368000
n=16 结果: 20922789888000
n=17 结果: 355687428096000
n=18 结果: 6402373705728000
n=19 结果: 121645100408832000
n=20 结果: 2432902008176640000
3.2 汉诺塔的非递归实现
以上是关于N日一篇——Java实现栈的主要内容,如果未能解决你的问题,请参考以下文章