栈是一种后进先出的结构
通常有两种实现方式
1.静态数组或者动态数组的实现方式
2.链表的实现方式。
运用到栈的算法有哪些呢:括号匹配问题,中缀表达式转后缀表达式问题,迷宫路径搜索问题,非递归遍历问题,回溯搜索问题,递归转非递归。
1.栈的实现
1.1 java数组实现
/*
* 用java数组实现简单的栈
* */
public class StackX {
private int maxSize;
private Object[] stackArray;
private int top;
public StackX(int s) {
maxSize = s;
stackArray = new Object[maxSize];
top = -1;
}
// 入栈
public void push(Object i) {
if (top != maxSize - 1) {
stackArray[++top] = i;
}
}
// 出栈
public Object pop() {
if (top != -1) {
return stackArray[top--];
} else {
return 0;
}
}
// 查看栈顶元素
public Object peek() {
return stackArray[top];
}
// 判断栈是否为空
public boolean isEmpty() {
return top == -1;
}
}
入栈操作数组不够时可考虑数组扩容
// 数组容量的扩容,当空间(.length)不够的时候,增加1.5倍
private static int[] arrycopy(int[] ss) {
int[] copy = new int[ss.length * 3 / 2];
System.arraycopy(ss, 0, copy, 0, ss.length);
return copy;
}
1.2 c++数组实现
#define DataType int
#define MAX 1024
typedef struct
{
DataType data[MAX];
int top;
}stack, *pstack;
pstack *init_stack()
{
pstack ps;
ps=(pstack)malloc(sizeof(stack));
if(!ps)
{
printf("Error. fail malloc.../n");
return NULL;
}
ps->top=-1;
return ps;
}
int empty_stack(pstack ps)
{
if(-1 == ps->top)
return 1;
else
return 0;
}
int push(pstack ps, DataType data)
{
if(ps->top == MAX-1)
{
printf("Stack is full.../n");
return 0;
}
ps->top++;
ps->data[ps->top]=data;
return 1;
}
int pop(pstack ps, DataType *data)
{
if(empty_stack(ps))
{
printf("Stack is empty.../n");
return 0;
}
*data=ps->data[ps->top];
ps->top--;
return 1;
}
2.关于栈的算法
2.1十进制转二进制
/**
* 利用堆栈后进先出的效果 写十进制转二进制 如果转八进制 就把2换成8
*
* @param i
*/
private static void binary(int i) {
String str = "";
Stack s = new Stack();
while (i > 0) {
s.push(i % 2);
i = i / 2;
}
while (!s.isEmpty()) {
str += s.pop();
}
System.out.println(str);
}
}
2.2用两个栈实现一个队列(剑指Offer-7)
思路:
假设两个栈A和B,且都为空。
可以认为栈 A为提供入队列的功能,栈B提供出队列的功能。
入队列:入栈A
出队列:
1 如果栈B不为空,直接弹出栈B的数据。
2 如果栈 B为空,则依次弹出栈A的数据,放入栈B中(栈B占空间比栈A要大),再弹出栈B的数据。
public class stackImQueue {
// s1:实现入队,s2:实现出队
private Stack<Object> s1;
private Stack<Object> s2;
stackImQueue(){
s1=new Stack<Object>();
s2=new Stack<Object>();
}
// 入队
public void enQueue(Object obj) {
s1.push(obj);
}
// 出队
public Object DeQueue() {
Object obj;
if (s2.isEmpty()) {
while (!s1.isEmpty()) {
obj = s1.pop();
s2.push(obj);
}
}
return s2.pop();
}
// 判断队列是否为空
public boolean isEmpty() {
if (s1.isEmpty() && s2.isEmpty())
return true;
return false;
}
}
2.3包含min方法的栈(剑指Offer-21)
题目:定义栈的数据结构,要求添加一个min函数,能够得到栈的最小元素。要求函数min、push以及pop的时间复杂度都是O(1)。
方法一:
题目要求的栈A,用来记录最小值的栈B。
push:栈A直接push新元素data。data与B栈中的栈顶元素比较,如果新值较小,则在B栈中push新值;否则再次pushB栈顶元素到B栈。
pop:栈Apop,栈B也跟着pop。
public class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
// 入栈
public void push(int i) {
stack.push(i);
if (minStack.isEmpty() || i <= minStack.peek()) {
minStack.push(i);
}else{
minStack.push(minStack.peek());
}
}
// 出栈
public int pop() {
int popNum;
if (stack.isEmpty()||minStack.isEmpty()) {
return 0;
}
popNum = stack.pop();
minStack.pop();
return popNum;
}
//返回栈中最小值
public int minNum(){
if (stack.isEmpty()||minStack.isEmpty()) {
return 0;
}
return minStack.peek();
}
}
方法二:
常规解空间上的一个优化:一般说来,最小值不会每次都需要更新,因此最小值栈里面会有很多重复元素。
题目要求的栈A,用来记录最小值的栈B。
push:栈A直接push新元素data。当data<=原最小值(即是B.top)B栈中push新值data;注意这个==的条件是不可少的,这是为了防止在pop的时候错误的pop最小值。
pop:栈A pop,当A.pop==B.top时,栈B pop。其他时候不对栈B进行pop。
只需对上面代码的push、pop做修改
// 入栈
public void push(int i) {
stack.push(i);
if (minStack.isEmpty() || i <= minStack.peek()) {
minStack.push(i);
}
}
// 出栈
public int pop() {
int popNum;
if (stack.isEmpty() || minStack.isEmpty()) {
return 0;
}
popNum = stack.pop();
if (popNum == minStack.peek())
minStack.pop();
return popNum;
}
方法三:
只需要额外开一个用于存放当前最小值的变量min即可.因此下面提到的push和pop操作都是对于题目中要求的栈来操作的,当然,这也是这个算法里唯一的栈.
设push的参数为v_push,pop的返回值为v_pop。
push:首先push (v_push-min),如果v_push < min,更新min为v_push.
pop:(栈顶元素为top)
如果top >= 0, v_pop = min+top;
如果top < 0, v_pop = min,然后更新min为min-top.
min函数:只需要返回min空间的内容即可.
public class MinStack {
private Stack<Integer> stack;
private int min;
public MinStack() {
stack = new Stack<Integer>();
}
// 入栈
public void push(int v_push) {
if (stack.isEmpty()) {
min = v_push;
}
stack.push(v_push - min);
if (v_push < min) {
min = v_push;
}
}
// 出栈
public int pop() {
int v_pop;
int top = stack.pop();
if (top >= 0) {
v_pop = min + top;
} else {
v_pop = min;
min = min - top;
}
return v_pop;
}
// 返回栈中最小值
public int minNum() {
return min;
}
// 判断栈空
public boolean isEmpty() {
if (stack.isEmpty())
return true;
return false;
}
}
2.4 打印堆栈
在不破坏栈结构的情况下打印堆栈,只允许用push、pop操作
// 打印堆栈
public void printStack(Stack<Integer> s) {
if (isEmpty())
return;
int top = (int) s.peek();
System.out.println(top + " ");
s.pop();
printStack(s);
s.push(top);
}
2.5 栈的压入、弹出序列(剑指Offer-22)
题目:输入两个整数序列。其中一个序列表示栈的push顺序,判断另一个序列有没有可能是对应的pop顺序。为了简单起见,我们假设push序列的任意两个整数都是不相等的。
比如输入的push序列是1、2、3、4、5,那么4、5、3、2、1就有可能是一个pop系列。因为可以有如下的push和pop序列:push 1,push 2,push 3,push 4,pop,push 5,pop,pop,pop,pop,这样得到的pop序列就是4、5、3、2、1。但序列4、3、5、1、2就不可能是push序列1、2、3、4、5的pop序列。
解析:这道题的一个很直观的想法就是建立一个辅助栈,每次push的时候就把一个整数push进入这个辅助栈,同样需要pop的时候就把该栈的栈顶整数pop出来。
public static void main(String[] args) {
int[] push = { 1, 2, 3, 4, 5 };
int[] pop = { 4, 5, 3, 2, 1 };
System.out.print("输出序列是否为合法的pop序列:" + IsPossiblePopOrder(push, pop));
}
public static boolean IsPossiblePopOrder(int[] push, int[] pop) {
if (push.length != pop.length) {
System.out.println("输入输出序列长度不一样!");
return false;
}
int length = push.length;
boolean isPossible = false;
Stack<Integer> list = new Stack<Integer>();
int pushindex = 0;
int popindex = 0;
if (push != null && pop != null && length > 0) {
// 检查pop序列中每一个字符
while (popindex < length) {
// 当栈顶字符和pop中要访问的字符不一样时候,将push中未入栈的字符入栈
while (list.size() == 0 || list.peek() != pop[popindex]) {
if (pushindex >= length)
break;
list.add(push[pushindex]);
pushindex++;
}
// 入栈完毕后,如果栈顶元素和pop中要访问的字符仍然不一样,则此pop序列不是合法的pop序列
if (list.peek() != pop[popindex]) {
break;
}
// 检查pop序列中的下一个字符
list.pop();
popindex++;
}
// 如果pop序列中所有的字符都被检查过,则该序列是合法的
if (list.size() == 0 && popindex == length) {
isPossible = true;
}
}
return isPossible;
}