杠上数据结构 - 栈
Posted 星火燎原2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了杠上数据结构 - 栈相关的知识,希望对你有一定的参考价值。
介绍
栈 : 是 一种只允许在一端进行插入,删除的线性表,具有先进后出的特性。
通常,栈的操作端称为 栈顶,另一端称为 栈底。栈的插入称为 进栈(push), 栈的删除操作称为 出栈(pop)。
栈的存储结构
既然栈的本质是一种线性表,那么栈的存储结构也有两种:
- 顺序存储结构(顺序栈)
- 链式存储结构(链式栈)
栈顺序存储结构
栈的顺序存储结构一般使用 数组 实现。数组中的第一个元素作为栈底,最后一个元素作为栈顶。
public class ArrayStack<E>
private int defaultCapacity = 10;
/**
* 存储元素的容器
*/
private Object[] elements;
/**
* 栈中元素个数
*/
private int size;
/**
* 标示栈顶的变量
*/
private int top;
public ArrayStack()
elements = new Object[defaultCapacity];
top = -1;
public ArrayStack(int capacity)
elements = new Object[capacity];
top = -1;
/**
* 进栈
*
* @param element
* @return
*/
public E push(E element)
ensureCapacity(size + 1);
elements[size] = element;
size++;
return element;
/**
* 出栈
*
* @return
*/
public E pop()
if (size > 0)
E element = (E) elements[size - 1];
size--;
return element;
throw new IllegalArgumentException("the stack is empty");
public boolean empty()
return size == 0;
public int size()
return size;
/**
* 确保容器大小是否可用,是否扩容
*
* @param newSize
*/
private void ensureCapacity(int newSize)
if (newSize > elements.length)
increaseCapacity(newSize);
/**
* 扩大容器大小, 1.5 倍扩容
*/
private void increaseCapacity(int newSize)
int increasedSize = newSize;
increasedSize = increasedSize + increasedSize >> 1;
try
elements = Arrays.copyOf(elements, increasedSize);
catch (OutOfMemoryError error)
// 扩容失败
error.printStackTrace();
public Object[] toArray()
return Arrays.copyOf(elements, size);
栈链式存储结构
栈的链式结构是在 第一个节点处 插入,删除节点。因为如果在最后一个节点处进行插入,删除,则需要一个一个遍历获取到最后一个节点才行。
public class LinkedStack<E>
private Node<E> head;
private int size;
public LinkedStack()
head = new Node<>();
public E push(E element)
Node<E> node = new Node<>(element);
node.next = head.next;
head.next = node;
size++;
return element;
public boolean empty()
return size == 0;
public E pop()
if (size > 0)
Node<E> topNode = head.next;
head.next = topNode.next;
size--;
return topNode.element;
throw new IllegalArgumentException("the stack is empty");
public int size()
return size;
public Object[] toArray()
Object[] objects = new Object[size];
Node<E> iterator = head.next;
if (iterator != null)
int index = 0;
objects[index] = iterator;
while (iterator.next != null)
iterator = iterator.next;
index++;
objects[index] = iterator;
return objects;
栈的应用
进制转换
十进制数转换成 N 进制的过程,其实就是把十进制数 除 N 得到的余数, 余数是从低位到高位产生,然后从高位到低位输出即是转换结果。
比如 十进制 13 转换成二进制位: 1101
实现代码:
/**
* 进制转换
*
* @param number 要转换的数
* @param n 要转换称的进制
* @return
*/
private String numberConvert(int number, int n)
ArrayStack<String> stack = new ArrayStack<>();
StringBuilder sb = new StringBuilder();
while (number > 0)
int mod = number % n;
if (mod > 10)
char c = (char) ('a' + (mod - 10));
String temp = String.valueOf(c);
stack.push(temp);
else
stack.push(String.valueOf(mod));
number = number / n;
while (!stack.empty())
String num = stack.pop();
sb.append(num);
return sb.toString();
括号匹配校验
表达式中每一个左括号都期待一个相应的右括号与之匹配,表达式中越迟出现,并且没有得到匹配的左括号期待匹配的程度越高,如果出现的右括号不是期待出现的,则表明匹配不正确。
需要一个栈,在读入字符过程中,如果是左括号,则直接入栈,如果是右括号且与当前栈顶左括号匹配,则将栈顶左括号出栈; 如果不匹配,则属于不合法的情况; 如果碰到右括号时,而栈为空,则说明没有左括号与之匹配,同样是非法的。读入完所有字符,如果栈为空的,则表达式是合法的。
/**
* 校验表达式括号匹配是否合法
*
* @param statement
* @return
*/
private boolean checkBrackets(String statement)
if (statement == null || statement.length() == 0)
return false;
ArrayStack<Character> stack = new ArrayStack<>();
for (int i = 0; i < statement.length(); i++)
Character character = statement.charAt(i);
switch (character)
case ')':
if (!stack.empty() && stack.pop() == '(')
continue;
else
return false;
case ']':
if (!stack.empty() && stack.pop() == '[')
continue;
else
return false;
case '':
if (!stack.empty() && stack.pop() == '')
continue;
else
return false;
default: // 左括号直接进栈
stack.push(character);
return stack.empty();
中序转后序表达式
前序( prefix )
中序( infix )
后序( postfix )
思想:
- 当读取字符是操作数时,直接输出到后序表达式中;
- 当读取字符是开括号,如 ( [ , 直接压栈;
- 当读取字符是闭括号时, 判断栈是否为空。如果栈为空,则抛出异常,如果不为空,则把栈中元素依次出栈输出到后序表达式中,直到首次遇到开括号,如果没有遇到开括号,则抛出异常;
- 当读取字符是运算符时,如果栈非空,并且栈顶不是开括号,并且栈顶运算符的优先级不低于读取的运算符优先级,循环弹出栈顶元素并输出到后序表达式中,最后把读取的运算符压栈;
- 当中序表达式全部读取完毕后,如果栈中仍有元素,则依次把他们弹出并输出到后序表达式中;
/**
* 中序表达式转换成后序表达式
*
* @param statement
* @return
*/
private String infix2Postfix(String statement)
if (statement == null || statement.length() == 0)
return null;
// 保存运算符优先级
Map<String, Integer> map = new HashMap<>();
map.put("*", 2);
map.put("/", 2);
map.put("+", 1);
map.put("-", 1);
Stack<String> stack = new Stack<>();
StringBuilder sb = new StringBuilder();
// 为了兼容元素为多位数字时的转换,先转换成字符串数组
String[] statementArray = statement.split(" ");
if (statementArray.length == 0)
return null;
for (int i = 0; i < statementArray.length; i++)
String str = statementArray[i];
if (isOperatorNumber(str))
sb.append(str + " ");
continue;
switch (str)
case "(":
case "[":
case "":
stack.push(str);
break;
case ")":
case "]":
case "":
if (stack.isEmpty())
throw new IllegalArgumentException("error");
else
while (!"(".equals(stack.peek()) && !"[".equals(stack.peek()) && !"".equals(stack.peek()))
if (!stack.isEmpty())
sb.append(stack.pop() + " ");
else
throw new IllegalArgumentException("error");
if ("(".equals(stack.peek()) || "[".equals(stack.peek()) || "".equals(stack.peek()))
// 后序表达式中没有括号,直接出栈,不输出
stack.pop();
break;
case "+":
case "-":
case "*":
case "/":
while (!stack.isEmpty() && !"(".equals(stack.peek()) && !"[".equals(stack.peek()) && !"".equals(stack.peek()) && map.get(stack.peek()) >= map.get(str))
sb.append(stack.pop() + " ");
stack.push(str);
break;
while (!stack.isEmpty())
sb.append(stack.pop() + " ");
return sb.toString();
/*
* 判断是否是操作数
*/
private boolean isOperatorNumber(String str)
Pattern pattern = Pattern.compile("^[a-z0-9]+$");
return pattern.matcher(str).matches();
调用
System.out.print(main.infix2Postfix("a + b * c + ( d * e + f ) * g"));
System.out.println();
System.out.print(main.infix2Postfix("( 23 + 34 * 45 / ( 5 + 6 + 7 ) )"));
输出结果:
a b c * + d e * f + g * +
23 34 45 * 5 6 + 7 + / +
后序表达式求值
/**
* 计算后序表达式值
*
* @param statement
* @return
*/
public String calculatePostfixValue(String statement)
if (statement == null || statement.length() == 0)
throw new IllegalArgumentException("the statement is illegal");
String[] array = statement.split(" ");
if (array.length == 0)
throw new IllegalArgumentException("the statement is illegal");
Stack<String> stack = new Stack<>();
for (int i = 0; i < array.length; i++)
String str = array[i];
// 如果是操作数,则入栈
if (isOperatorNumber(str))
stack.push(str);
else
double top1 = 0;
double top2 = 0;
if (!stack.isEmpty())
top1 = Double.parseDouble(stack.pop());
if (!stack.isEmpty())
top2 = Double.parseDouble(stack.pop());
double temp = 0;
switch (str)
case "+":
temp = top1 + top2;
break;
case "-":
temp = top1 - top2;
break;
case "*":
temp = top1 * top2;
break;
case "/":
temp = top2 * 1.0f / top1;
break;
stack.push(String.valueOf(temp));
if (!stack.isEmpty())
return stack.pop();
return null;
调用
String res = main.infix2Postfix(Ultra Edit中的数据对齐