【算法修炼之道】——栈

版权声明:希望各位能遵守各自内心的原则 https://blog.csdn.net/weixin_40918067/article/details/82142402

栈(stack)是什么???——》是一种线性存储结构,也是存储数据的简单数据结构(简称为“数据的存储结构”);

那有什么特色之处???——》

特点:

  1. 栈中数据是按照"后进先出(LIFO, Last In First Out)或先进先出(FILO)"方式进出栈的;
  2. 向栈中添加/删除数据时,只能从栈顶进行操作;

那实现栈的定义需要什么操作???——》栈通常包括的三种操作:push、peek、pop。

push ——》向栈中添加元素(入栈);
peek——》返回栈顶元素;
pop  ——》返回并删除栈顶元素的操作(出栈);

当然栈的基本操作不止这么少!!!

——》还有:top(栈顶)、size(元素个数)、isEmpty(判断是否为空)、isStackFull(是否存满元素);

要是不符合了栈,那会有什么异常???——》

溢出:试图对一个满栈执行入栈操作;

下溢:试图对一个空栈执行出栈操作;

如何实现栈的操作的图文解释???——》

1、栈的示意图

栈中的数据依次是 30 --> 20 --> 10

2、 出栈

出栈前:栈顶元素是30。此时,栈中的元素依次是 30 --> 20 --> 10 
出栈后:30出栈之后,栈顶元素变成20。此时,栈中的元素依次是 20 --> 10

3、入栈

入栈前:栈顶元素是20。此时,栈中的元素依次是 20 --> 10 
入栈后:40入栈之后,栈顶元素变成40。此时,栈中的元素依次是 40 --> 20 --> 10

一般考试都是从栈的入栈顺序来考的;入栈顺序和出栈顺序的规律也就是其特点:后进先出或先进先出;

既然我们讲解了栈是什么,栈的特点以及栈的具有哪些操作等;那么,现在我们就来实现栈吧!

栈的实现

和C++一样,JDK包中也提供了"栈"的实现,它就是集合框架中的Stack类;

关于Stack类的原理,这里有详细的解释:"Java 集合系列07之 Stack详细介绍(源码解析)和使用示例"中。这里给出2种实现方式:

一、数组实现的栈,能存储任意类型的数据;
二、Java的 Collection集合 中自带的"栈"(stack)的示例;

1、数组实现的栈,能存储任意类型的数据方式的代码实现

/**
 * Java : 数组实现的栈,能存储任意类型的数据
 *
 * @author skywang
 * @date 2013/11/07
 */
import java.lang.reflect.Array;

public class GeneralArrayStack<T> {

    private static final int DEFAULT_SIZE = 12;
    private T[] mArray;
    private int count;

    public GeneralArrayStack(Class<T> type) {
        this(type, DEFAULT_SIZE);
    }

    public GeneralArrayStack(Class<T> type, int size) {
        // 不能直接使用mArray = new T[DEFAULT_SIZE];
        mArray = (T[]) Array.newInstance(type, size);
        count = 0;
    }

    // 将val添加到栈中
    public void push(T val) {
        mArray[count++] = val;
    }

    // 返回“栈顶元素值”
    public T peek() {
        return mArray[count-1];
    }

    // 返回“栈顶元素值”,并删除“栈顶元素”
    public T pop() {
        T ret = mArray[count-1];
        count--;
        return ret;
    }

    // 返回“栈”的大小
    public int size() {
        return count;
    }

    // 返回“栈”是否为空
    public boolean isEmpty() {
        return size()==0;
    }

    // 打印“栈”
    public void PrintArrayStack() {
        if (isEmpty()) {
            System.out.printf("stack is Empty\n");
        }

        System.out.printf("stack size()=%d\n", size());

        int i=size()-1;
        while (i>=0) {
            System.out.println(mArray[i]);
            i--;
        }
    }

    public static void main(String[] args) {
        String tmp;
        GeneralArrayStack<String> astack = new GeneralArrayStack<String>(String.class);

        // 将10, 20, 30 依次推入栈中
        astack.push("10");
        astack.push("20");
        astack.push("30");

        // 将“栈顶元素”赋值给tmp,并删除“栈顶元素”
        tmp = astack.pop();
        System.out.println("tmp="+tmp);

        // 只将“栈顶”赋值给tmp,不删除该元素.
        tmp = astack.peek();
        System.out.println("tmp="+tmp);

        astack.push("40");
        astack.PrintArrayStack();    // 打印栈
    }
}

测试代码

运行结果:


1 tmp=30
2 tmp=20
3 stack size()=3
4 40
5 20
6 10

结果说明:GeneralArrayStack是通过数组实现的栈,而且GeneralArrayStack中使用到了泛型。

2、Java的 Collection集合 中自带的"栈"(stack)的代码实现

import java.util.Stack;

/**
 * Java : java集合包中的Stack的演示程序
 *
 * @author skywang
 * @date 2013/11/07
 */
public class StackTest {

    public static void main(String[] args) {
        int tmp=0;
        Stack<Integer> astack = new Stack<Integer>();

        // 将10, 20, 30 依次推入栈中
        astack.push(10);
        astack.push(20);
        astack.push(30);

        // 将“栈顶元素”赋值给tmp,并删除“栈顶元素”
        tmp = astack.pop();
        //System.out.printf("tmp=%d\n", tmp);

        // 只将“栈顶”赋值给tmp,不删除该元素.
        tmp = (int)astack.peek();
        //System.out.printf("tmp=%d\n", tmp);

        astack.push(40);
        while(!astack.empty()) {
            tmp = (int)astack.pop();
            System.out.printf("tmp=%d\n", tmp);
        }
    }
}

测试结果

tmp=40
tmp=20
tmp=10

既然我们对栈的代码实现了;那么,接下来就是对栈的算法分析了;

性能和局限性

1、简单数组

2、动态数组

动态数组的实现方式有两种:

1、复制原数组中所有元素到新数组中,在新数组末端添加新元素;新数组也就比原数组多一个空间;

其执行n次push操作后,总时间开销T(n)(复制操作的数量)为1+2+......+n≈O(n^2);

2、重复倍增:也就是新建一个比原数组空间大一倍的数组,然后复制原数组的元素;

执行n次push操作的时间开销为n;是不是惊到啦!怎么回事???竟然比上一种实现方式减少了这么多时间开销;

其计算过程:

T(n)为O(n),一次push操作的平摊时间也就是O(1);

性能

3、链表实现

栈的各种方法比较

1、递增策略和倍增策略比较

2、基于数组实现和基于链表实现的比较

基于数组实现的栈:

基于链表实现的栈:

通过上面的总结,相信大家已经对于栈这类算法的实现,以及思路、特点、题型有了一定的了解;

那么,接下来我们就要了解该算法用于何处了;

应用

直接应用

1、符号匹配、中缀表达式转换为后缀表达式、计算后缀表达式、实现函数调用(包括递归)、求范围误差(极差)

2、网页浏览器中已访问页面的历史记录(后退按钮)、文本编辑器中的撤销序列

3、HTML和XML文件中的标签匹配

间接应用

1、作为一个算法的辅助数据结构(例如,树遍历算法);

2、其他数据结构的组件(模拟队列)

也就到这里了,下次我会把这些应用等实践的问题一一展开!!!有什么不对请多多指教;

猜你喜欢

转载自blog.csdn.net/weixin_40918067/article/details/82142402