美文网首页
大话数据结构 - 栈

大话数据结构 - 栈

作者: HikariCP | 来源:发表于2018-04-19 08:50 被阅读8次

    代码GitHub地址


    栈的分类

    • 栈是特殊的线性表
    • 栈的典型应用递归,四则运算表达式求值。

    栈的分类有两种:

    • 静态栈(数组实现)
    • 动态栈(链表实现)

    我们接下来会分别实现这两种栈。

    栈的操作

    数组方式

    代码GitHub地址 - 欢迎star

    栈节点

    public class Node {
    
        private int value;
    
        public Node() {
    
        }
    
        public Node(int value) {
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    }
    
    

    由于是数组方式存储的,每个节点都能方便的存取它的前驱后继节点。所以我们声明的栈节点只需要存在一个存值的value属性即可。

    public class Stacks {
    
        private static final int MAX_SIZE = 100;
        private static final int MIN_SIZE = -1;
    
        /**
         * 空栈默认 top = 1
         */
        private int top = -1;
        /**
         * 顺序栈默认最大 100
         */
        private Node[] nodes = new Node[MAX_SIZE];
    
        public Stacks() {
    
        }
    
        public Stacks(int top) {
            this.top = top;
        }
    
        public Stacks(Node[] nodes) {
            this.nodes = nodes;
        }
    
        public Stacks(int top, Node[] nodes) {
            this.top = top;
            this.nodes = nodes;
        }
    
    }
    

    由于是数组形式声明的栈,所以我们需要规定栈最大容量,并且设立top变量来定位栈顶元素。

    入栈

    /**
     * 入栈
     *
     * @param stacks
     * @param value
     * @return
     */
    public static int push(Stacks stacks, int value) {
        Node node = new Node(value);
        if (stacks.top == MAX_SIZE) {
            return 0;
        }
        stacks.top++;
        stacks.nodes[stacks.top] = node;
        return 1;
    }
    

    很简单,只需要注意代码执行顺序即可,需要明白栈顶指针指向栈顶元素。每次入栈操作执行完top必然会++那么它又需要在入栈后指向最新插入的新元素,就必然在赋值前++,同时赋值的时候找数组的top索引处给就是最方便快捷的。

    出栈

    /**
     * 出栈
     *
     * @param stacks
     * @return
     */
    public static int pop(Stacks stacks) {
        if (stacks.top == MIN_SIZE) {
            return 0;
        }
        stacks.nodes[stacks.top] = null; //
        stacks.top--;
        return 1;
    }
    

    和入栈操作相反,需要把栈的top索引处的元素置空,在top还在指向该节点的时候置空,然后top--即可。

    遍历

    /**
     * 遍历
     *
     * @param stacks
     */
    public static void traverse(Stacks stacks) {
        int top = stacks.top;
        while (top > MIN_SIZE) {
            System.out.println(stacks.nodes[top--].getValue());
        }
    }
    

    根据top从顶到底,从最大值到0遍历即可。
    需要注意的是:不要直接操作Stack.top变量,而是把他取出来换其他局部变量操作。否则在你遍历完之后,栈中就真的没有元素了。因为Stack.top变量已经被你指向了栈底。

    判断是否为空

    /**
     * 判断是否为空
     *
     * @param stacks
     * @return
     */
    public static boolean isEmpty(Stacks stacks) {
        return stacks.top == MIN_SIZE;
    }
    

    清空栈

    /**
     * 清空栈
     *
     * @param stacks
     */
    public static void clean(Stacks stacks) {
        while (stacks.top > MIN_SIZE) {
            stacks.nodes[stacks.top] = null;
            stacks.top--;
        }
    }
    

    也可以循环调用出栈(pop)方法。通过while循环来判断top变量是否大于0来判断是否空栈。

    共享栈

    共享栈一般的使用场景是两个栈的空间有相反的需求关系的时候。也就是一个栈增长的时候另一个栈在缩短的情况。比如购买股票,有人买入就一定有人卖出,总量放在那里不变。不可能两面都有人一直买入,那么栈很快就会溢出了。所以在使用共享栈的时候考虑好使用场景是否符合。

    public class SharedStack {
    
        private static final int MAX_SIZE = 100;
        private static final int MIN_SIZE = -1;
    
        public Node[] stackElement = new Node[MAX_SIZE];
        /**
         * 栈1的栈顶指针初始为-1
         */
        public int top_1 = MIN_SIZE;
        /**
         * 栈2的栈顶指针 初始为n
         */
        public int top_2 = MAX_SIZE;
    
        public SharedStack() {
    
        }
        
    }
    

    共享栈 - 入栈

    /**
     * 入栈
     *
     * @param stack       栈
     * @param value       插入的元素值
     * @param stackNumber 栈序号,栈1,还是栈2
     * @return 成功与否,失败0,成功返回插入的值
     */
    public static int push(SharedStack stack, int value, int stackNumber) {
        Node newNode = new Node(value);
        if (stack.top_1 + 1 == stack.top_2) {
            return 0;
        }
        if (stackNumber == 1) {
            // 栈1插入元素
            stack.stackElement[++stack.top_1] = newNode;
        } else if (stackNumber == 2) {
            // 栈2插入元素
            stack.top_2--;
            stack.stackElement[--stack.top_2] = newNode;
        }
        return value;
    }
    

    共享栈 - 出栈

    /**
     * 出栈
     *
     * @param stack
     * @param stackNumber
     * @return 删除的栈顶元素的值
     */
    public static int pop(SharedStack stack, int stackNumber) {
        int value = 0;
        if (stackNumber == 1) {
            if (stack.top_1 == MIN_SIZE) {
                return 0;
            }
            value = stack.stackElement[stack.top_1--].getValue();
        } else if (stackNumber == 2) {
            if (stack.top_2 == MAX_SIZE) {
                return 0;
            }
            value = stack.stackElement[stack.top_2++].getValue();
        }
        return value;
    }
    

    链表方式

    代码GitHub地址 - 欢迎star

    栈节点

    public class Node {
    
        private int value;
        private Node nextNode;
    
        public Node() {
        }
    
        public Node(int value) {
            this.value = value;
        }
    
        public Node(Node nextNode) {
            this.nextNode = nextNode;
        }
    
        public Node(int value, Node nextNode) {
            this.value = value;
            this.nextNode = nextNode;
        }
        
        getter/setter
    

    可以看出相比于数据方式,栈节点多了一个nextNode指针域。指向它的后继节点

    public class Stacks {
    
        /**
         * 栈顶结点
         */
        private Node topElement;
        /**
         * 栈底节点
         */
        private Node bottmElement;
    
        public Stacks() {
    
        }
    
        public Stacks(Node topElement, Node bottmElement) {
            this.topElement = topElement;
            this.bottmElement = bottmElement;
        }
    
    }
    

    此时栈如果是空栈的话topElement = bottmElement

    入栈

    /**
     * 入栈
     *
     * @param stacks
     * @param value
     * @return
     */
    public static int push(Stacks stacks, int value) {
        Node node = new Node(value);
        // 下一节点为根节点
        node.setNextNode(stacks.topElement);
        stacks.topElement = node;
        return 1;
    }
    

    需要记住的一个思路就是,新插入节点的后继节点是当前栈的栈顶元素。插入之后才能移动该栈顶元素的指针topElement

    出栈

    /**
     * 出栈
     *
     * @param stacks
     * @return
     */
    public static int pop(Stacks stacks) {
        if (stacks.topElement == stacks.bottmElement) {
            return 0;
        }
        stacks.topElement = stacks.topElement.getNextNode();
        return 1;
    }
    

    栈顶指针指向其后继节点即可。

    遍历

    /**
     * 遍历
     *
     * @param stacks
     */
    public static void traverse(Stacks stacks) {
        Node node = stacks.topElement;
        while (node != stacks.bottmElement) {
            System.out.println(node.getValue());
            node = node.getNextNode();
        }
    }
    

    换一个节点指向当前栈顶节点的节点即可。然后一直遍历输出到栈底节点为止。

    判断是否为空

    /**
     * 判断是否为空
     *
     * @param stacks
     * @return
     */
    public static boolean isEmpty(Stacks stacks) {
        return stacks.topElement == stacks.bottmElement;
    }
    

    清空

    /**
     * 清空
     *
     * @param stacks
     */
    public static void clean(Stacks stacks) {
        stacks.bottmElement = null;
        stacks.topElement = stacks.bottmElement;
    }
    

    相关文章

      网友评论

          本文标题:大话数据结构 - 栈

          本文链接:https://www.haomeiwen.com/subject/gapikftx.html