N日一篇——Java实现栈

Posted 从零开始的智障生活

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了N日一篇——Java实现栈相关的知识,希望对你有一定的参考价值。

栈详解:https://blog.csdn.net/qq_34028001/article/details/119331089?spm=1001.2014.3001.5501

目录

一、顺序栈

1.1 用接口表示顺序栈抽象数据类型

1.2 带头结点的顺序栈实现

1.3 不带头结点的顺序表实现

1.4 测试带头结点与不带头结点的顺序栈

 二、链栈

2.1 用接口表示链栈抽象数据类型

2.2 用链表类型表示链栈的每个结点类型

2.3 不带头结点的链栈


一、顺序栈

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实现栈的主要内容,如果未能解决你的问题,请参考以下文章

N日一篇——二叉树

N日一篇——二叉树

N日一篇——二叉树

N日一篇——Java实现队列

N日一篇——Java实现队列

N日一篇——Java实现队列