一、栈的基本概念
-
堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作。
-
堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底。堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈。
-
数据集合:堆栈的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类类型。
-
操作集合:入栈(push)、出栈(pop)、取栈顶元素(getTop)、判断是否为空(isEmpty)
二、栈的实现
1、接口代码
/**
* 栈、队列线性表接口
*/
public interface List<E> {
/**
* 入栈或入队
* @param o
* @return
*/
boolean push(E o);
/**
* 出栈或出队
* @return
*/
E pop();
/**
* 取栈顶或取队头
* @return
*/
E getTop();
/**
* 栈或队列是否为空
* @return
*/
boolean isEmpty();
}
2、顺序栈和链栈的实现代码
/**
* 栈的实现
* FILO 先进后出
*/
public interface Stack<E> extends List<E> { }
/**
* 顺序栈
* @param <E>
*/
class ArrayStack<E> implements Stack<E>{
// 顺序栈的存储空间
private E[] stack;
// 栈顶指针
private int heap = -1;
// 栈的大小
public int size;
// 栈的最大空间
private int MAX_SIZE = 50;
// 初始化栈
public ArrayStack(){
stack = (E[]) new Object[MAX_SIZE];
}
/**
* 入栈
* @param e 入栈元素
* @return
*/
@Override
public boolean push(E e) {
// 判断是否栈满
if(size >= MAX_SIZE){
return false;
}
//入栈
size++;
stack[++heap] = e;
return true;
}
/**
* 出栈
* @return
*/
@Override
public E pop() {
if(isEmpty()){
throw new StackOverflowError("栈已为空!!");
}
// 出栈
size--;
return stack[heap--];
}
/**
* 取栈顶元素但不出栈
* @return
*/
@Override
public E getTop() {
if(isEmpty()){
throw new StackOverflowError("栈已为空!!");
}
return stack[heap];
}
/**
* 判断栈是否为空
* @return
*/
@Override
public boolean isEmpty() {
return (heap == -1)?true:false;
}
}
/**
* 链栈
* @param <E>
*/
class LinkedStack<E> implements Stack<E>{
/**
* 元素节点类
* @param <E>
*/
class Node<E>{
E e;
Node pre;
Node next;
public Node(E e){
this.e = e;
}
}
// 栈顶指针
private Node<E> heap;
// 栈的大小
public int size;
/**
* 入栈
* @param o
* @return
*/
@Override
public boolean push(E e) {
// 创建节点
Node<E> node = new Node<>(e);
size++;
// 若栈顶为空,则给栈顶赋值
if(heap == null){
heap = node;
return true;
}
// 连接原栈顶与新节点
heap.next = node;
node.pre = heap;
heap = node; // 更新栈顶
return true;
}
/**
* 出栈
* @return
*/
@Override
public E pop() {
if(heap == null){
throw new StackOverflowError("栈已为空!!");
}
size--;
E e = heap.e; // 取栈顶元素值
heap = heap.pre; // 更新栈顶
return e;
}
/**
* 取栈顶元素但不出栈
* @return
*/
@Override
public E getTop() {
if(heap == null){
throw new StackOverflowError("栈已为空!!");
}
return heap.e;
}
@Override
public boolean isEmpty() {
return (size == 0)?true:false;
}
}
三、Java中栈与堆的区别
-
栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。在java中,所有基本类型和引用类型都在栈中存储。栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域)。
-
堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中。在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象。所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用。
四、队列的基本概念
-
队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。
-
队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。
-
数据集合:队列的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类型。
-
操作集合:入队(push)、出队(pop)、取队头元素(getTop)、判断是否为空(isEmpty)
五、队列的实现
顺序队列和链式队列的实现代码
/**
* 队列的实现
* FIFO 先进先出
* @param <E>
*/
public interface Queue<E> extends List<E> { }
/**
* 顺序队列(数组实现)
* @param <E>
*/
class ArrayQueue<E> implements Queue<E>{
// 顺序队列的存储空间
private E[] queue;
// 队头指针
public int head = 0;
// 队尾指针
public int tail = 0;
// 队列的大小
public int size = 0;
// 队列的最大空间
private int MAX_SIZE = 50;
// 初始化队列
public ArrayQueue(){
queue = (E[]) new Object[MAX_SIZE];
}
/**
* 入队
* @param e
* @return
*/
@Override
public boolean push(E e) {
// 判断是否队满
if(size == MAX_SIZE){
throw new IndexOutOfBoundsException("队列已满!!");
}
// 在队尾的后一位插入元素并更新队尾
queue[tail] = e;
if(tail >= MAX_SIZE - 1){
tail = 0;
}else {
tail ++;
}
size++;
return true;
}
/**
* 出队
* @return
*/
@Override
public E pop() {
if(isEmpty()){
throw new IndexOutOfBoundsException("队列已空!!");
}
// 取队头并更新队头
E e = queue[head];
if(head >= MAX_SIZE - 1){
head = 0;
}else {
head ++;
}
size --;
return e;
}
/**
* 取队头但不出队
* @return
*/
@Override
public E getTop() {
if(isEmpty()){
throw new IndexOutOfBoundsException("队列已空!!");
}
// 取队头
return queue[head];
}
@Override
public boolean isEmpty() {
return (size == 0)?true:false;
}
}
/**
* 链式队列(链表实现)
* @param <E>
*/
class LinkedQueue<E> implements Queue<E>{
/**
* 元素节点类
* @param <E>
*/
class Node<E>{
E e;
Node next;
public Node(E e){
this.e = e;
}
}
// 队头指针
private Node<E> head;
// 队尾指针
private Node<E> tail;
// 队列的大小
public int size;
/**
* 入队
* @param e
* @return
*/
@Override
public boolean push(E e) {
// 在队尾的后一位插入元素并更新队尾
Node node = new Node(e);
tail.next = node;
tail = node;
size++;
return true;
}
/**
* 出队
* @return
*/
@Override
public E pop() {
// 取队头并更新队头
E e = head.e;
head = head.next;
size --;
return e;
}
/**
* 取队头但不出队
* @return
*/
@Override
public E getTop() {
if(isEmpty()){
throw new IndexOutOfBoundsException("队列已空!!");
}
// 取队头
return head.e;
}
@Override
public boolean isEmpty() {
return (size == 0)?true:false;
}
}
六、中缀表达式转换后缀表达式算法
* 中缀转后缀算法
* 1、运算符入栈,数字入队
* 2、匹配到双 “()” 时,出栈 “()” 中的运算符
* 3、栈顶优先级低出栈
* 4、*、/ 号先入栈,待后一位数字入队后再直接出栈
* 5、若中缀表达式已入栈入队完毕,则陆续把栈中的运算符出栈然后入队
/**
* 利用栈实现中缀表达式转后缀表达式
*/
public class InfixToSuffix {
public static void main(String[] args) {
InfixToSuffix its = new InfixToSuffix();
// 中缀表达式
String infix = "9+(3-1)*3+10/2";
// 中缀转后缀 "9 3 1 - 3 * 10 2 / + +"
its.infixToSuffix(infix);
}
/**
* 中缀转后缀算法
* 1、运算符入栈,数字入队
* 2、匹配到双 “()” 时,出栈 “()” 中的运算符
* 3、栈顶优先级低出栈
* 4、*、/ 号先入栈,待后一位数字入队后再直接出栈
* 5、若中缀表达式已入栈入队完毕,则陆续把栈中的运算符出栈然后入队
* @param infix
* @return
*/
public String infixToSuffix(String infix){
// 创建一个栈
Stack<Character> stack = new ArrayStack<>();
// 创建一个队列
Queue<String> queue = new ArrayQueue<>();
// 中缀表达式转后缀表达式
int start = 0;
int end = 0;
String x = "";
// substring(int start, int end); end > >= start
for(int i = 0; i < infix.length(); i ++){
char c = infix.charAt(i);
// '*' '/' 待后一位数字入队后入队
if(!stack.isEmpty() && (stack.getTop().equals('*') || stack.getTop().equals('/'))){
x = stack.pop() + "";
}
int s1 = ((ArrayStack<Character>) stack).size; // '*' '/' 入队标志 1
switch (c){
// '+' '-' 直接入栈 , 前面的数字入队
case '+' : stack.push(c); queue.push(infix.substring(start,end)); start = ++end; break;
case '-' : stack.push(c); queue.push(infix.substring(start,end)); start = ++end; break;
// '*' '/' 符号入栈,数字入队后 符号出栈跟着入队
case '*' : stack.push(c); queue.push(infix.substring(start,end)); start = ++end; break;
case '/' : stack.push(c); queue.push(infix.substring(start,end)); start = ++end; break;
// '(' 入栈 ')' 入栈并匹配 '(' ,出栈 '()' 间的运算符并入队
case '(' : stack.push(c); queue.push(infix.substring(start,end)); start = ++end; break;
case ')' : stack.push(c); queue.push(infix.substring(start,end));
while (true){
Character pop = stack.pop();
if(pop.equals('(')){
start = ++end; break;
}else if(!pop.equals(')')){
queue.push(pop + "");
}
} break;
default: end++; break;
}
int s2 = ((ArrayStack<Character>) stack).size; // '*' '/' 入队标志 2
if(s1 != s2){ // 当标志位不等时表示 '*' '/' 的后一位数字已入队
queue.push(x);
x = "";
}
}
queue.push(infix.substring(start,end)); // 将最后一位运算符之后的数字入队
queue.push(x); // 若'*' '/' 之后无其他运算符,则把最后一位 入队
// 栈中的还未出栈符号全部出栈
int size = ((ArrayStack<Character>) stack).size;
for(int s = 0; s < size; s ++){
queue.push(stack.pop() + "");
}
// 打印后缀表达式
size = ((ArrayQueue<String>) queue).size;
StringBuilder suffix = new StringBuilder();
Queue<String> newQueue = new ArrayQueue();
for(int i = 0; i < size; i ++){
String c = queue.pop();
if(!c.equals("")){
System.out.print(c + " ");
suffix.append(c);
newQueue.push(c);
}
}
// 计算后缀表达式的值
int calc = calc(newQueue, ((ArrayQueue<String>) newQueue).size);
System.out.println("计算后缀表达式的值 = " + calc);
return suffix + "";
}
/**
* 计算后缀表达式的值
* @param queue 中缀表达式队列
* @param size 中缀表达式队列的长度
* @return
*/
public int calc(Queue<String> queue,int size){
if(size == 1){
return Integer.parseInt(queue.pop());
}
Queue<String> newQueue = new ArrayQueue();
int f1 = Integer.parseInt(queue.pop());
int f2 = Integer.parseInt(queue.pop());
String f;
boolean flag = true;
for(int i = 2; i < size; i++){
f = queue.pop();
if(flag){
switch (f){
// 匹配到运算符直接运算,否则给相应数字赋值
case "+" : f = (f1 + f2) + ""; newQueue.push(f); flag = false; break;
case "-" : f = (f1 - f2) + ""; newQueue.push(f); flag = false; break;
case "*" : f = (f1 * f2) + ""; newQueue.push(f); flag = false; break;
case "/" : f = (f1 / f2) + ""; newQueue.push(f); flag = false; break;
default: newQueue.push(f1 + ""); // 入队不用计算的数字
// 赋值出队的数字
f1 = f2;
f2 = Integer.parseInt(f);
}
}else {
newQueue.push(f);
}
}
return calc(newQueue,((ArrayQueue<String>) newQueue).size);
}
}