栈的实现原理

目录介绍

  • 01.栈由简单数据实现php

    • 1.1 简单数组代码实现
    • 1.2 可能出现问题
    • 1.3 性能和局限性
  • 02.栈由动态数组实现node

    • 2.1 基于简单数组存在问题
    • 2.2 第一种解决办法
    • 2.3 第二种解决办法
    • 2.4 动态数组实现栈代码
    • 2.5 性能和局限性
  • 03.栈由链表实现git

    • 3.1 使用链表的优点
    • 3.2 链表实现栈代码
    • 3.3 性能和局限性
  • 04.Android栈Stack源码分析github

    • 4.1 源码展现
    • 4.2 为什么选用数组实现栈
  • 05.建立增强版自定义栈面试

    • 5.1 扩容和泛型

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深刻知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,固然也在工做之余收集了大量的面试题,长期更新维护而且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
  • 连接地址:https://github.com/yangchong2...
  • 若是以为好,能够star一下,谢谢!固然也欢迎提出建议,万事起于忽微,量变引发质变!

栈系列文章

01.栈由简单数组实现

1.1 简单数组代码实现

  • 首先看一下实现的代码

    • 用数组实现栈,最主要的是要在类的内部定义一个数组,而且这个数组要具备必定的大小,要在定义栈的时候定义好。
    public class ArrayStack{
        private static final String TAG = "ArrayStack";
        private Object[] contents;
        private int top = -1;
        private int bottom = -1;
        private int SIZE = 10;//有一个初始值大小
    
        public ArrayStack(){
            contents = new Object[SIZE];
            top = -1;
        }
    
        public int push(Object obj) throws Exception {
            if (top > SIZE) throw new Exception("小杨逗比,栈已经满了!");
            top++;
            contents[top] = obj;
            return top;
        }
    
        public Object pop() throws Exception{
            if (top == bottom) throw new Exception("小杨逗比,栈已经空了!");
            Object obj = contents[top];
            contents[top] = null;
            top--;
            return obj;
        }
    
        public boolean isEmpty(){
            return top == bottom;
        }
    
        public int getSize(){
            return top + 1;
        }
    
        public void display() throws Exception{
            if (getSize() == 0) throw new Exception("空栈!");
            for (int i=getSize()-1;i>=0;i--){
                System.out.print(contents[i].toString() + "->");
            }
            System.out.println("");
        }
    } 
    
    
    public void test{
        ArrayStack as = new ArrayStack();
        //as.display();
        as.push("小杨逗比");
        as.push("潇湘剑雨");
        as.push("yc");
        as.push("逗比");
        as.push("aaa");
        as.push("ertte");
        as.push("hello");
        as.display();
        as.pop();
        System.out.println(as.getSize());
        as.pop();
        as.display();
    }

1.2 可能出现问题

  • 可能会出现的问题

    • 当数组栈存满了元素的时候,若是执行插入数据,将会抛出栈满异常。
    • 当数组栈没有元素的时候,若是执行出栈数据,将会抛出栈空异常。

1.3 性能和局限性

  • 性能和局限性分析

    • 栈的最大空间必须提早声明,并且关键是大小还不能改变,这就蛋疼了。因此会出现执行入栈或者出栈操做时会出现异常。那么解决异常就是每次入栈判断栈是否存储满,每次出栈判断栈是否为空。
    • 假设栈中有m个元素,基于简单数组实现的栈。栈的出栈,入栈,判空,获取大小等时间复杂度都是O(1)。

02.栈由动态数组实现

2.1 基于简单数组存在问题

  • 基于简单数组的栈实现方法中,采用一个下标变量top,它始终指向栈中最新插入元素的位置。
  • 当插入元素时,会增长top值,而且会在数组该下标的位置存储新的元素。
  • 当删除元素时,先获取下标变量top位置的元素,而后减少变量top的值。
  • 当top下标变量为-1时,表示栈是空的。可是存在问题是:在固定大小的数组中,如何处理全部空间都已经保存栈元素这种状况???

2.2 第一种解决办法

  • 可能首先会想到,每次将数组大小增长1或者减少1,将会怎么样?

    • 插入元素,栈的空间大小增长1
    • 删除元素,栈的空间大小减去1
  • 这样作存在极大问题

    • 频繁增长数组大小的方法开销很大。为何这样说呢?
    • 当n=3时,执行push插入元素操做,当插入第四条元素时,会新建一个大小为4的数组,而后复制原数组中全部的元素到新的数组中,而后在新的数组中的末尾添加插入元素。以此类推,每次插入数据,都会从新建立一个新的数组对象,而后拷贝旧的数组数据到新的数组中来,而后在末尾添加新元素,这样作实在很差。

2.3 第二种解决办法

  • 在第一种解决办法中改造。好比咱们常常听到ArrayList集合动态扩容,先指定数组的长度,若是数组空间已满,则新建一个比原数据大一倍[或者n倍]的新数组,再而后复制元素。
  • 采用这种方式,插入元素操做,开销相对来讲要小不少。

2.4 动态数组实现栈代码

  • 基于动态数据实现栈的代码以下所示

    class DynArrayStack{
        private int top;
        private int capacity;
        private int[] array;
     
        private void doubleStack(){
            int[] newArray=new int[capacity*2];
            System.arraycopy(array,0,newArray,0,capacity);
            capacity=capacity*2;
            array=newArray;
        }
     
        public DynArrayStack(){
            top=-1;
            capacity=1;
            array=new int[capacity];
        }
     
        public boolean isEmpty(){
            return (top==-1);
        }
     
        public boolean isStackFull(){
            return (top==capacity-1);
        }
     
        public void push(int date){
            if(isStackFull()){
                doubleStack();
            }
            array[++top]=date;
        }
     
        public int pop(){
            if(isEmpty()){
                System.out.println("Stack Empty");
                return 0;
            }else {
                return array[top--];
            }
        }
     
        public void deleteStack(){
            top=-1;
        }
    }
     
    public class Main {
     
        public static void main(String[] args) {
            // write your code here
            DynArrayStack dynArrayStack=new DynArrayStack();
            dynArrayStack.push(1);
            dynArrayStack.push(2);
            dynArrayStack.push(3);
            System.out.println(dynArrayStack.pop());
            System.out.println(dynArrayStack.pop());
            System.out.println(dynArrayStack.pop());
        }
    }

2.5 性能和局限性

  • 性能

    • 假设有n个元素的栈,基于动态数组的栈实现中,关于栈插入数据,取出数据的时间复杂度都是O(1)。
    • 可能致使的性能问题:倍增太多可能致使内存溢出。
  • 存在局限性

    • 是用数组实现栈,在定义数组类型的时候,也就规定了存储在栈中的数据类型,那么同一个栈能不能存储不一样类型的数据呢?(声明为Object)?
    • 栈须要初始化容量,并且数组实现的栈元素都是连续存储的,那么能不能不初始化容量呢?(改成由链表实现)?

03.栈由链表实现

3.1 使用链表的优点

  • 栈规模的增长和减少都很简洁,并且每一个操做都是常数时间开销,每一个操做都要使用额外的空间和时间开销来处理指针。

3.2 链表实现栈代码

  • 入栈的顺序是:1 2 3 4 5,那么保证出栈的顺序是5 4 3 2 1,以此类推让head节点指向栈顶节点保证让其倒序输出。

    public class MyStack<T> {
        private T data;
        private MyStack<T> next;
     
        public MyStack(T data, MyStack<T> next) {
            this.data = data;
            this.next = next;
        }
     
        public T getData() {
            return data;
        }
     
        public void setData(T data) {
            this.data = data;
        }
     
        public MyStack<T> getNext() {
            return next;
        }
     
        public void setNext(MyStack<T> next) {
            this.next = next;
        }
    }
    
    public class LinkStack<N> {
     
        private MyStack<N> head;
        private MyStack<N> tail;
        private Integer size=0;
     
        public MyStack<N> getHead() {
            return head;
        }
     
        public void setHead(MyStack<N> head) {
            this.head = head;
        }
     
        public MyStack<N> getTail() {
            return tail;
        }
     
        public void setTail(MyStack<N> tail) {
            this.tail = tail;
        }
     
        public Integer getSize() {
            return size;
        }
     
        public void setSize(Integer size) {
            this.size = size;
        }
     
        public void addStack(N data){
            MyStack<N> node = new MyStack<>(data,null);
            if(headIsNull()){
                head = node;
                tail = node;
                size++;
            }else{
                //新加入的node是:(data,null) 让这个新的node的next指向初始的head节点 head变为(data,head))
                node.setNext(head);
                head = node;
                size++;
            }
        }
     
        public N outStack(){
            if(size>0){
                N outData = head.getData();
                head = head.getNext();
                return outData;
            }else{
                throw new RuntimeException("栈里无元素!");
            }
        }
     
        public boolean headIsNull(){
            if(head == null){
                return true;
            }
            return false;
        }
    }
  • 测试一下

    public void test() {
        LinkStack<Integer> linkStack = new LinkStack<>();
        linkStack.addStack(1);
        linkStack.addStack(2);
        linkStack.addStack(3);
        linkStack.addStack(4);
        linkStack.addStack(5);
    
        for(int i=0;i<linkStack.getSize();i++){
            System.out.println(linkStack.outStack());
        }
    }

3.3 性能和局限性

  • 假设栈中有n个元素,基于链表的栈实现中,关于栈插入元素和取出元素的时间复杂度是O(1)
  • 数据入栈和出栈的时间复杂度都为O(1),也就是说栈操做所耗的时间不依赖栈中数据项的个数,所以操做时间很短。并且须要注意的是栈不须要比较和移动操做。

04.栈Stack源码分析

  • 在Android中,对于activity使用栈stack进行管理的,下面看看栈源代码。

    • 能够看到栈stack是实现vector,其实底层也是用数组来实现的。
    public class Stack<E> extends Vector<E> {
        /**
         * 建立一个空的栈对象
         */
        public Stack() {
        }
    
        /**
         * 将对象推送到此堆栈的顶部。
         */
        public E push(E item) {
            addElement(item);
    
            return item;
        }
    
        /**
         * 移除此堆栈顶部的对象,并将该对象做为此函数的值返回。
         */
        public synchronized E pop() {
            E       obj;
            int     len = size();
            obj = peek();
            removeElementAt(len - 1);
    
            return obj;
        }
    
        /**
         * 查看此堆栈顶部的对象,而不将其从堆栈中移除。
         */
        public synchronized E peek() {
            int     len = size();
    
            if (len == 0)
                throw new EmptyStackException();
            return elementAt(len - 1);
        }
    
        /**
         * 判断是不是空栈
         */
        public boolean empty() {
            return size() == 0;
        }
    
        /**
         * 返回对象位于此堆栈上的基于1的位置。
         */
        public synchronized int search(Object o) {
            int i = lastIndexOf(o);
    
            if (i >= 0) {
                return size() - i;
            }
            return -1;
        }
    
        private static final long serialVersionUID = 1224463164541339165L;
    }

05.建立增强版自定义栈

  • 一个能自动扩容,第二个能存储各类不一样类型的数据,解决办法以下:

    public class ArrayStack {
        //存储元素的数组,声明为Object类型能存储任意类型的数据
        private Object[] elementData;
        //指向栈顶的指针
        private int top;
        //栈的总容量
        private int size;
         
         
        //默认构造一个容量为10的栈
        public ArrayStack(){
            this.elementData = new Object[10];
            this.top = -1;
            this.size = 10;
        }
         
        public ArrayStack(int initialCapacity){
            if(initialCapacity < 0){
                throw new IllegalArgumentException("栈初始容量不能小于0: "+initialCapacity);
            }
            this.elementData = new Object[initialCapacity];
            this.top = -1;
            this.size = initialCapacity;
        }
         
         
        //压入元素
        public Object push(Object item){
            //是否须要扩容
            isGrow(top+1);
            elementData[++top] = item;
            return item;
        }
         
        //弹出栈顶元素
        public Object pop(){
            Object obj = peek();
            remove(top);
            return obj;
        }
         
        //获取栈顶元素
        public Object peek(){
            if(top == -1){
                throw new EmptyStackException();
            }
            return elementData[top];
        }
        //判断栈是否为空
        public boolean isEmpty(){
            return (top == -1);
        }
         
        //删除栈顶元素
        public void remove(int top){
            //栈顶元素置为null
            elementData[top] = null;
            this.top--;
        }
         
        /**
         * 是否须要扩容,若是须要,则扩大一倍并返回true,不须要则返回false
         * @param minCapacity
*/
    public boolean isGrow(int minCapacity){
        int oldCapacity = size;
        //若是当前元素压入栈以后总容量大于前面定义的容量,则须要扩容
        if(minCapacity >= oldCapacity){
            //定义扩大以后栈的总容量
            int newCapacity = 0;
            //栈容量扩大两倍(左移一位)看是否超过int类型所表示的最大范围
            if((oldCapacity<<1) - Integer.MAX_VALUE >0){
                newCapacity = Integer.MAX_VALUE;
            }else{
                newCapacity = (oldCapacity<<1);//左移一位,至关于*2
            }
            this.size = newCapacity;
            int[] newArray = new int[size];
            elementData = Arrays.copyOf(elementData, size);
            return true;
        }else{
            return false;
        }
    }
}
```

其余介绍

01.关于博客汇总连接

02.关于个人博客

相关文章
相关标签/搜索