栈的定义
栈是限定仅在一段进行插入和删除的线性表。虽然这个限制减少了栈的灵活性,但也使得栈更有效,更容易实现。栈也被叫做LIFO线性表(即后进先出表),习惯上称栈的可访问元素为栈顶元素,元素的插入称为入栈(push),元素的删除称为出栈(pop)。栈可分为顺序栈和链式栈,顺序栈底层是一个数组,栈顶元素就是数组尾部元素;而链式栈是以链表为基础。
栈的链式实现
栈的链式实现实质是对于链表的简化实现,由于栈只需要在一端进行删除和插入操作,因此只需要一个头部节点指向栈顶元素即可。栈的实现如下
import java.util.EmptyStackException;
public class Stack<T> {
static class Node<T> {
T t;
Node<T> next;
private Node(T t) {
this.t = t;
next =
null;
}
}
private Node<T> top;
private int size;
public Stack() {
size =
0;
}
public boolean
isEmpty() {
return size >
0 ?
false :
true;
}
public void push(T t) {
if (size ==
0) {
top =
new Node<T>(t);
size++;
return;
}
Node<T> tmp = top;
top =
new Node<T>(t);
top.next = tmp;
size++;
}
public T
pop() {
if (size ==
0) {
throw new EmptyStackException();
}
T tmp = top.t;
top = top.next;
size--;
return tmp;
}
public T
peek() {
if (size ==
0) {
throw new EmptyStackException();
}
return top.t;
}
}
算术表达式的计算
平时我们所接触的算术表达式从形式上看都是中缀表达式,这是一种我们容易接受的形式,但这种形式对于计算机来说就不是那么一回事了。通常我们都需要将中缀表达式转为后缀或前缀表达式然后再进行求值。下面我以中缀转后缀为例。
中缀表达式转后缀表达式
这个过程需要用到两个,一个是运算符栈,另一个是后缀表达式栈(用于存放结果),具体操作如下 1. 从左往右扫描中缀表达式 2. 如果遇到运算符(+,-,*,/),如果运算符栈为空则直接将运算符压人,如果不为空将该运算符和运算符栈中的栈顶元素进行优先级比较,如果该运算符优先级较大,那么直接将该运算符压人运算符栈;否则,依次从运算符栈中弹出栈顶元素并压人后缀表达式栈,直到运算符栈栈顶元素优先级小于我们扫描到的元素。(其实也就一句话,保证运算符栈的优先级有序,使栈顶元素优先级最大) 3. 如果遇到“(”,那么直接压人运算符栈 4. 如果遇到“)”,那么需要弹出运算符栈中的元素并压入后缀表达式栈直到遇到“(”,然后将“(”弹出 5. “(”只有“)”能使它弹出,其他运算符均不能使其弹出 6. 如果遇到数字直接压人后缀表达式栈 7. 扫描完毕时依次将运算符栈中的符号弹出压人后缀表达式栈
这里举个例子来说明该过程(栈顶元素在最右边) 假设现在有一个算术表达式为:(4-3)*2+2/2
运算符栈后缀表达式栈字符
(((44(-4-(-43343-)*43-*43-22+43-2*++43-2*22+/43-2*2/+/43-2*22243-2*22/+
代码实现
public class InfixToPostfixConverter {
public String convert(
char[] infix) {
Stack<Character> stack1 =
new Stack<>();
Stack<String> stack2 =
new Stack<>();
StringBuilder result =
new StringBuilder();
int count =
0;、
int operatorPos =
0;
for(
int i=
0;i<infix.length;i++){
if(isNum(infix[i])){
operatorPos = i-
1;
break;
}
}
for (
int i =
0; i < infix.length; i++) {
if (isOperator(infix[i])) {
if (
count >
0) {
StringBuilder tmp =
new StringBuilder();
tmp.append(
new String(infix, operatorPos +
1,
count));
stack2.push(tmp.
reverse().toString());
count =
0;
}
operatorPos = i;
if (infix[i] ==
'(') {
stack1.push(infix[i]);
}
else if (infix[i] ==
'*' || infix[i] ==
'/') {
while (!stack1.isEmpty() && (stack1.peek() ==
'*' || stack1.peek() ==
'/')) {
stack2.push(stack1.pop() +
"");
}
stack1.push(infix[i]);
}
else if (infix[i] ==
'+' || infix[i] ==
'-') {
while (!stack1.isEmpty() && (stack1.peek() ==
'*' || stack1.peek() ==
'/' || stack1.peek() ==
'+'
|| stack1.peek() ==
'-')) {
stack2.push(stack1.pop() +
"");
}
stack1.push(infix[i]);
}
else if (infix[i] ==
')') {
while (!stack1.isEmpty() && stack1.peek() !=
'(') {
stack2.push(stack1.pop() +
"");
}
stack1.pop();
}
}
else if (isNum(infix[i])) {
count++;
}
}
if (
count >
0) {
stack2.push(
new String(infix, operatorPos +
1,
count));
stack2.push(stack1.pop() +
"");
}
while(!stack1.isEmpty()){
stack2.push(stack1.pop()+
"");
}
while (!stack2.isEmpty()) {
result.append(stack2.pop());
result.append(
',');
}
return result.
reverse().toString();
}
private boolean isOperator(
char c) {
return c ==
'+' || c ==
'-' || c ==
'*' || c ==
'/' || c ==
'(' || c ==
')';
}
private boolean isNum(
char c) {
return c >=
'0' && c <=
'9' ||c==
'.';
}
}
利用后缀表达式进行计算
当我们将中缀表达式转化为后缀表达式时,可以利用一个栈完成计算过程,具体过程如下 1. 从左往右扫描后缀表达式 2. 遇到数字直接压人栈中 3. 遇到运算符则从栈中弹出出两个数根据该运算符进行计算并将结果压回栈中 4. 扫描结束后,从栈顶弹出的元素就是我们的计算结果了 利用上面得到的后缀表达式(43-2*22/+)举个实例
数字栈字符
44331-222*222222221/3+
实现代码及一个测试样例
import java.util.Scanner;
import java.text.DecimalFormat;
public class PostfixEvaluator {
public int evaluate(
char[] postfix){
StringBuilder sb =
new StringBuilder();
for(
int i=
0;i<postfix.length;i++){
sb.append(postfix[i]);
}
String result = sb.toString();
String[] strings = result.split(
",");
Stack<Integer> stack =
new Stack<>();
int n1,n2;
for(
int i=
0;i<strings.length;i++){
if(isANum(strings[i])){
stack.push(Integer.parseInt(strings[i]));
}
else if(strings[i].equals(
"+")){
n2 = stack.pop();
n1 = stack.pop();
stack.push(n1+n2);
}
else if(strings[i].equals(
"-")){
n2 = stack.pop();
n1 = stack.pop();
stack.push(n1-n2);
}
else if(strings[i].equals(
"*")){
n2 = stack.pop();
n1 = stack.pop();
stack.push(n1*n2);
}
else if(strings[i].equals(
"/")){
n2 = stack.pop();
n1 = stack.pop();
stack.push(n1/n2);
}
}
return stack.pop();
}
private boolean
isANum(String s){
try{
int n = Integer.parseInt(s);
}
catch(Exception e){
return false;
}
return true;
}
public static void main(String[] args){
DecimalFormat format =
new DecimalFormat(
"#.000");
System.
out.print(
"Input the infix expression:");
Scanner scanner =
new Scanner(System.
in);
String line = scanner.nextLine();
while(!line.equals(
"Q")&&!line.equals(
"q")){
InfixToPostfixConverter converter =
new InfixToPostfixConverter();
String postfix = converter.convert(line.toCharArray());
String[] result = postfix.split(
",");
System.
out.print(
"Postfix expression:");
for(String s:result){
System.
out.print(s);
}
System.
out.println();
PostfixEvaluator evaluator =
new PostfixEvaluator();
System.
out.print(
"Final value:");
System.
out.println(format.format(evaluator.evaluate(postfix.toCharArray())));
System.
out.print(
"Input the infix expression:");
line = scanner.nextLine();
}
}
}
Java中栈的源码分析
那么Java中是如何实现栈这个数据结构的呢?
public class Stack<E> extends Vector<E> {
public Stack() {
}
public E
push(E item) {
addElement(item);
return item;
}
public synchronized E
pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len -
1);
return obj;
}
public synchronized E
peek() {
int len = size();
if (len ==
0)
throw new EmptyStackException();
return elementAt(len -
1);
}
public boolean
empty() {
return size() ==
0;
}
public synchronized
int search(Object o) {
int i = lastIndexOf(o);
if (i >=
0) {
return size() - i;
}
return -
1;
}
private static final
long serialVersionUID =
1224463164541339165L;
}
从上面源码可以看到,Stack是直接继承Vector这个类的(我们知道Vector的底层是一个数组),可见Java中的栈是一个顺序栈,它是以数组作为基础的,其中的search(Object)可以在栈中查找某个元素的位置,它是通过调用父类Vector方法lastIndexOf(Object)获取查找元素在Vector中的下标位置,然后返回size()-1,如果找不到则返回-1。 源码下载地址:http://download.csdn.net/detail/mynameis121/9806031