1.定义
栈(Stack)是限定仅在表尾进行插入或删除操作的线性表。因此。表尾端有其特殊含义,称为栈顶(top),相应地,表头端称为栈低。一般来说,栈主要有两个操作:一个是进栈(PUSH),另一个是出栈(POP)。
2. 栈的表示和实现
1) 顺序栈,即栈的顺序存储结构,利用一组地址连续的存储单元依次存放自栈低到栈顶的数据元素。
–java实现
public class ArrayStack<T> implements Serializable {
private static final long serialVersionUID = 74027006708386243L;
private Object[] elementData; // 定义一个数组用于保存顺序栈的元素
private int size = 0; // 保存顺序栈中元素的当前个数
private int capacity; // 当前数组的长度
public ArrayStack() {
elementData = new Object[10]; // 默认长度为10的栈
}
public ArrayStack(int initSize) {
elementData = new Object[initSize]; // 设置栈的长度
}
public ArrayStack(T element) {
this();
elementData[0] = element;
size++;
}
public ArrayStack(T element, int initSize) {
this.capacity = initSize;
elementData = new Object[capacity];
elementData[0] = equals(element);
size++;
}
/*
* @Description: 栈长度
*/
public int size() {
return size;
}
/**
* @description:入栈
*/
public void push(T element) {
// TODO Auto-generated method stub
ensureCapacity(size + 1);
elementData[size++] = element;
}
// 如果数组的原有长度小于目前所需的长度 ,进行扩容
private void ensureCapacity(int capacity) {
// TODO Auto-generated method stub
int oldcapacity = elementData.length;
if (capacity > oldcapacity) {
int newCapacity = (oldcapacity * 3) / 2 + 1;
if (newCapacity < capacity) {
newCapacity = capacity;
}
// minCapacity is usually close to size
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
/**
* @description: 出栈
*/
public T pop() {
if (!isEmpty()) {
T lastValue = (T) elementData[size - 1];
// 释放栈顶元素
elementData[--size] = null;
return lastValue;
} else {
return null;
}
}
/**
* @Description: 返回栈顶元素,但不删除栈顶元素
*/
public T peek() {
if (!isEmpty()) {
return (T) elementData[size - 1];
} else {
throw new IndexOutOfBoundsException("空栈异常");
}
}
public boolean isEmpty() {
// TODO Auto-generated method stub
return size == 0;
}
/**
* @Description: 清空顺序栈
*/
public void clear() {
// 将底层数组所有元素赋为null
Arrays.fill(elementData, null);
size = 0;
}
public String toString() {
if (size == 0) {
return "[]";
} else {
StringBuilder sbBuilder = new StringBuilder("[");
for (int i = size -1 ; i >= 0; i--) {
sbBuilder.append(elementData[i].toString() + ",");
}
int len = sbBuilder.length();
return sbBuilder.delete(len - 1, len).append("]").toString();
}
}
}
2)栈的链表表示
–java 实现
import java.io.Serializable;
public class LinkStack<T> implements Serializable {
private static final long serialVersionUID = -4378447264374701299L;
private class Node {
private T data; // 保存节点的数据
private Node next;
public Node() {
// TODO Auto-generated constructor stub
}
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
private Node top; // 保存该链表栈的栈顶元素
private int size = 0; // 保存该链栈中已包含的节点数,即栈的长度
public LinkStack() {
// TODO Auto-generated constructor stub
top = null;
}
public LinkStack(T element) {
top = new Node(element, null);
size++;
}
/**
* @Description: 栈的长度
*/
public int size() {
return size;
}
/**
* @despriction: 入栈
**/
public void push(T element) {
top = new Node(element, top);
size++;
}
/**
* @Description: 出栈
**/
public T pop() {
Node oldTop = top;
top = top.next;
oldTop.next = null;
size--;
return oldTop.data;
}
/**
* @Description: 访问栈顶元素
*/
public T peek() {
return top.data;
}
/**
* @Description: 判断顺序栈是否为空栈
*/
public boolean isEmpty() {
return size == 0;
}
/**
* @Description: 清空顺序栈
*/
public void clear() {
top = null;// 将栈所有元素赋为null
size = 0;
}
public String toString() {
if (isEmpty()) {
return "[]";
} else {
StringBuilder sb = new StringBuilder("[");
for (Node current = top; current != null; current = current.next) {
sb.append(current.data.toString() + ", ");
}
int len = sb.length();
return sb.delete(len - 2, len).append("]").toString();
}
}
}
3. 栈的应用
1) 进制转换
通过求余法可以将十进制转换为其他进制,比如要转为8 进制,则将原是进制除以8,记录余数,然后继续将商除以8,一直到商为 0为止,最后将余数倒序写出来即可。
–java实现
public class Conversion {
/**
* @Title: conversion
* @Description: 将10进制正整数num转换为n进制
* @param num: 10进制正整数
* @param n: n进制
* @return 转换后的值
*/
public String Conversion(int num, int n){
ArrayStack<Integer> myStack = new ArrayStack<Integer>();
Integer result = num;
while(true){
//将余数存入栈
myStack.push(result % n);
result = result / n;
if (result == 0) {
break;
}
}
StringBuilder sBuilder = new StringBuilder();
//按栈的 顺序倒序输出即可
while ((result = myStack.pop()) != null) {
sBuilder.append(result);
}
return sBuilder.toString();
}
}
2) 括号匹配检验
一般编程语言中括号都是成对出现的,比如:[],(),{}
凡是遇到括号的前半部分,即为入栈符号(PUSH);凡是遇到括号后半部分,就比对是否与栈顶元素相匹配(PEEK),如果相匹配,则出栈(POP),否者就匹配出错。
–Java实现
public class symbol_match {
public boolean isMatch(String str) {
ArrayStack<Character> myStack = new ArrayStack<Character>();
char[] arr = str.toCharArray();
for (char c : arr) {
Character temp = myStack.pop();
// 栈为空时只将c入栈
if (temp == null) {
myStack.push(c);
}
// 配对时c不入栈
else if (temp == '[' && c == ']') {
}
// 配对时c不入栈
else if (temp == '(' && c == ')') {
}
else {
myStack.push(temp);
myStack.push(c);
}
}
return myStack.isEmpty();
}
}
3)行编辑
–java实现
/**
* @ClassName: LineEdit
* @Description: 行编辑: 输入行中字符'#'表示退格, '@'表示之前的输入全都无效.
*/
public class lineEdit {
public String lineEdit(String input) {
ArrayStack<Character> myStack = new ArrayStack<Character>();
char[] arr = input.toCharArray();
for (char c : arr) {
if (c == '#') {
myStack.pop();
} else if (c == '@') {
myStack.clear();
}else {
myStack.push(c);
}
}
StringBuilder sBuilder = new StringBuilder();
Character temp = null;
while((temp = myStack.pop()) != null){
sBuilder.append(temp);
}
sBuilder.reverse();
return sBuilder.toString();
}
}
4)汉诺塔游戏,递归实现
汉诺塔问题的描述如下:有A、B和C 3跟柱子,在A上从下往上按照从小到大的顺序放着 n 个圆盘,以B为中介,把盘子全部移动到C上。移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比它大的盘子。本实例将演示如何求解3阶汉诺塔问题。
思路解析
为了将N个盘子从A移动到C,需要先将第N个盘子上面的N-1个盘子移动到B上,这样才能将第N个盘子移动到C上。同理,为了将第N-1个盘子从B移动到C上,需要将N-2个盘子移动到A上,这样才能将第N-1个盘子移动到C上。通过递归就可以实现汉诺塔问题的求解。
–java实现
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class hanoi {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
int n;
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入盘子数:");
n = Integer.parseInt(buf.readLine());
move(n, 'A','B','C');
}
private static void move(int n, char a, char b, char c) {
// TODO Auto-generated method stub
if (n == 1) {
System.out.println("盘 " + n + " 由 " + a + " 移至 " + c);
}else{
move(n-1, a, c, b);
System.out.println("盘 " + n + " 由 " + a + " 移至 " + c);
move(n-1, b, a, c);
}
}
}
运行输出
请输入盘子数:
3
盘 1 由 A 移至 C
盘 2 由 A 移至 B
盘 1 由 C 移至 B
盘 3 由 A 移至 C
盘 1 由 B 移至 A
盘 2 由 B 移至 C
盘 1 由 A 移至 C