死磕算法第二弹——栈、队列、链表(1)

本文整理来源 《轻松学算法——互联网算法面试宝典》/赵烨 编著面试

什么是栈

栈是有着特殊规则的数据结构,栈有着重要的一个特色——后进先出(LIFO, Last In First Out),也能够叫作先进后出(FILO, First In Last Out),咱们不管如何只可以从一端去操做元素。算法

栈又叫作堆栈(Stack), 这里说明一下不要将它和堆混淆。实际上堆和栈是两种不一样的概念,栈是一种只能在一段进行插入和删除的线性数据结构。编程

通常来讲,栈主要有两个操做:一个是进栈(PUSH),又叫做入栈、压栈;另外一个是出栈(POP),或者叫做退栈。数组

栈是一种比较简单的数据结构。数据结构

栈的存储结构

栈通常使用一段连续的空间进行存储,一般预先分配一个长度,能够简单地使用数组去实现。编程语言

栈的存储结构

栈只有一个方向能够对栈内的元素进行操做,而从栈中最下面的一个元素称为栈底,通常是数组的第0个元素,而栈顶是栈内最后放入的元素。函数

通常而言,定义一个栈须要初始的大小,这就是栈的初始容量。当须要放入的元素大于这个容量,就须要扩容。测试

栈出入元素的操做以下。例如咱们初始化一个长度为10的数组,并想其中放入元素,根据栈的定义,只能从数组的一端放入元素,咱们设定这一段为数组中较大下标方向。咱们放入第一个元素,因为栈内没有元素,咱们第一个元素落到数组的第0个下标的位置上;接着放入第二个元素,第二个元素该放入到下标为1的位置上;以此类推,以后咱们放入第五个元素时,放入栈中的下标第4个位置上。如今进行出栈操做,出栈智能从一端操做,咱们以前设定只能从下标较大的方向操做,所以须要肯定数组中下标最大的方向中存在栈元素的位置下标是多少。咱们通常会在栈中作个计数器来记录这个值。如今栈中有5个元素,因此讲数组中的第五个位置也就是下标为4的元素出栈。此时数组中只剩下4个元素了。this

public class Stack<T> {

    private int size = 0;

    private static final int DEF_INITIAL = 10;

    private Object[] array;

    public Stack() {
        this(DEF_INITIAL);
    }

    public Stack(int init) {
        if (init <= 0) {
            init = DEF_INITIAL;
        }
        array = new Object[init];
    }

    /**
     * 入栈
     *
     * @param item 入栈元素
     */
    public void push(T item) {
        if (size == array.length) {
            array = Arrays.copyOf(array, size * 2);
        }
        array[size++] = item;
    }

    /**
     * 获取栈顶元素,可是没有出栈
     * @return 栈顶元素
     */
    @SuppressWarnings("unchecked")
    public T peek(){
        if (size == 0){
            throw new IndexOutOfBoundsException("栈顶已经空");
        }
        return (T) array[size - 1];
    }

    /**
     * 出栈,同时获取栈顶元素
     * @return 栈顶元素
     */
    public T pop(){
        T item = peek();
        //直接使元素个数减1,不须要真的清除,下次入栈会覆盖旧元素的值
        size--;
        return item;
    }

    /**
     * 栈是否满了
     * @return 栈是否满了
     */
    public boolean isFull(){
        return size == array.length;
    }

    /**
     * 栈是否为空栈
     * @return 是否为空栈
     */
    public boolean isEmpty(){
        return size == 0;
    }
    
    public int size(){
        return size;
    }
}

在实现思考当中,第一步是写出来,第二步是扩容,第三步是考虑到异常状况。一个数据结构的实现或多或少总会有些东西能够扩展的,想到多少写多少,在面试或笔试必定会有所加分的。code

测试代码:

public class StackTest {

    @Test
    public void main(){
        //为了查看效果,初始数组长度设置为1
        Stack<Integer> stack = new Stack<>(1);
        stack.push(1);
        stack.push(2);
        //栈内的元素为2,数组长度也为2
        Assert.assertEquals(2,stack.size());
        stack.push(3);
        //栈内元素个数为3,当前数组长度为4
        Assert.assertEquals(3,stack.size());
        Assert.assertEquals(4,stack.getLength());
        //获取栈顶元素,为3,可是没有出栈
        Assert.assertEquals(3,(int)stack.peek());
        Assert.assertEquals(3,stack.size());

        //栈顶元素出栈,返回3
        Assert.assertEquals(3,(int)stack.pop());
        //栈顶元素出栈,返回2
        Assert.assertEquals(2,(int)stack.pop());
        //出栈2次,栈内元素为1
        Assert.assertEquals(1,stack.size());

    }

}

栈的特色

栈的特色显而易见,只能在一段操做,遵循先进后出或者后者先出的原则。

栈的使用场景

逆序输出

因为栈具有先进后出的特色,因此逆序输出是其中一个很是简单的应用。首先把全部的元素按照元素入栈,而后把全部的元素出栈并输出,轻松实现逆序输出。

语法检查,符号成对出现

在编程语言中,通常括号都是成对出现的,好比“[”和“]”“{”和“}”“(”和“)”“<”和“>”(这里排除大于小于号的做用).

凡是遇到括号的前半部分,即为入栈符号(push);凡是遇到括号的后半部分,就比对是否与栈顶元素相匹配(peek),若是相匹配则出栈(pop),不然就是匹配出错。

数制转换(将十进制的数转换为2-9的任意进制数)

经过求余法,能够将十进制数转换为其余进制,好比转为8进制,则将原十进制数除以8,记录余数,而后继续将商除以8,一直到商等于0为止,最后将余数倒着写出来就好了。

常据说到编程语言调用中的“函数栈”,就是在咱们调用方法时计算机会执行PUSH方法,记录调用,在return时也就是方法结束以后,执行POP方法,完成先后对应

相关文章
相关标签/搜索