• Java数据结构和算法(一)--栈


    栈:

      英文名stack,特点是只允许访问最后插入的那个元素,也就是LIFO(后进先出)

    jdk中的stack源码:

    public
    class Stack<E> extends Vector<E> {  //继承Vector,Vector和ArrayList几乎相同,都是通过数组保存数据,只不过方法有Synchronized修饰
    
        public Stack() {
        }
    
        public E push(E item) {  //push,也就是add,把数据保存得add到数组的末尾
            addElement(item);  
            return item;
        }
    
        public synchronized E pop() {  //pop,也就是removeLast(),并且返回末尾值
            E       obj;
            int     len = size();  //得到数组长度
            obj = peek();  //得到数组末尾值
            removeElementAt(len - 1);  //删除末尾元素
    
            return obj;
        }
    
        public synchronized E peek() {  //peek,返回末尾元素,只是查询,不会删除
            int     len = size();
    
            if (len == 0)
                throw new EmptyStackException();
            return elementAt(len - 1);  
        }
    
        public boolean empty() {  //empty,是否为空
            return size() == 0;
        }
    
        public synchronized int search(Object o) {
            int i = lastIndexOf(o);
            if (i >= 0) {
                return size() - i;
            }
            return -1;
        }
    
    }

    从上面源码看到:

      JDK源码中的stack通过集成Vector实现,通过数组保存数据,一般使用push()、pop()、peek()来实现stack基本功能LIFO

      stack有栈顶、栈底的概念,栈底是封闭的,数据只能从栈顶进出

    自定义实现stack:

    public class MyStack<E> {  
        private int maxDepth;  //栈深度
        private Object[] elementData;  //保存数据的数组
        private int top;  //栈顶的位置,空栈为-1
    
        public MyStack() {
            this.maxDepth = 10;
            this.elementData = new Object[10];  
            this.top = -1;
        }
    
        public MyStack(int initialCapacity) {
            this.maxDepth = initialCapacity;
            this.elementData = new Object[initialCapacity];
            this.top = -1;
        }
    
        public E push(E e) {
            ensureExplicitCapacity(top +1);
            elementData[++top] = e;
            return e;
        }
    
        public E pop(){
            E e = peek();
            elementData[top] = null;
            this.top--;
            return e;
        }
    
        public E peek() {
            return (E)elementData[top];
        }
    
        private void ensureExplicitCapacity(int minCapacity) {
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
        private void grow(int minCapacity) {  //扩容
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - Integer.MAX_VALUE > 0)
                newCapacity = Integer.MAX_VALUE;
            this.maxDepth = newCapacity;
            elementData = Arrays.copyOf(elementData, maxDepth);
        }
    
        public int size() {
            return top + 1;
        }
      public boolean isEmpty() {
        return top == -1;
      } }
    public static void main(String[] args) {
    	MyStack<Integer> stack = new MyStack<Integer>();
    	stack.push(1);
    	stack.push(2);
    	stack.push(3);
    	int size = stack.size();
    	for (int i = 0; i < size; i++) {
    		System.out.print(stack.peek() + " ");
    	}
    	System.out.println("");
    	for (int i = 0; i < size; i++) {
    		System.out.print(stack.pop() + " ");
    	}
    }
    输出结果:
    3 3 3 
    3 2 1 
    

    这是使用ArrayList的思路实现Stack:

    1、通过Object[]保存数据

    2、扩展支持泛型

    3、初始容量设置为10,扩容之后为原来容量的1.5倍

    4、实现了push()、pop()、peek()和grow()

    5、stack不适合使用for、foreach、Iterator,所以这里没实现Iterable接口,因为peek()永远只是栈顶的值,或者用pop()

    PS:for循环的size,如果for循环使用pop(),不要写成:

    for (int i = 0; i < stack.size(); i++) {
    	System.out.print(stack.pop() + " ");
    }
    

    因为弹出元素,size会变化,无法弹出所有数据,可以使用测试代码中那样,或者while循环:

    while (!stack.isEmpty()) {
    	System.out.println(stack.pop());;
    }

    到此为止一个stack的功能已经实现,从输出结果看使用时没问题的

    实践1:实现输入字符串,逆序输出

    public static void main(String[] args) {
    	MyStack<Character> stack = new MyStack<Character>();
    	char[] chars = getInput().toCharArray();
    	for (char c : chars) {
    		stack.push(c);
    	}
    	while (!stack.isEmpty()) {
    		System.out.print(stack.pop() + " ");;
    	}
    }
    
    public static String getInput() {
    	String s = null;
    	try {
    		InputStreamReader reader = new InputStreamReader(System.in);
    		BufferedReader bufferedReader = new BufferedReader(reader);
    		s = bufferedReader.readLine();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	return s;
    }
    

     输出结果:

    实践2:分隔符匹配

      栈通常用于解析xml标签或者HTML标签,例如'{'要有个'}'进行匹配,通过把读取字符,发现左分隔符push,发现右分隔符pop,然后对比是否

    匹配,不匹配就报错,如果栈中没有左分隔符和右分隔符匹配,或者有左分隔符一直没有被匹配,这些情况,也会报错。

    例如:a{12(u<d>1)f3}

     

    代码实现:

    public static boolean isValid(String s) {
    MyStack<Character> stack = new MyStack<>();
    char[] chars = s.toCharArray();
    for (char aChar : chars) {
    switch (aChar) {
    case '{':
    stack.push('}');
    continue;
    case '<':
    stack.push('>');
    continue;
    case '(':
    stack.push(')');
    continue;
    default:
    if (stack.isEmpty() || stack.pop() != aChar) {
    return false;
    }

    }
    }
    return stack.isEmpty();
    }

    总结:

    stack是概念上的工具,我们已经实现了两个功能,能够实现什么功能看个人实现,甚至Ctrl+Z撤销的实现就是用stack实现的

    stack入栈和出栈的时间复杂度都是O(1),栈的操作时间和栈中数据的个数没有关系,不需要移动和比较,只不过我们这里实现了扩容,如果

    发生扩容,会稍微影响一点

     

    内容参考:

    <Java数据结构和算法>

    Java数据结构和算法(四)——栈

  • 相关阅读:
    AssetBundleNote
    UNet笔记
    HololensAR开发设置
    Hololens真机使用Unity开发流程
    数据结构笔记
    解决粘包问题
    使用C#中的Socket进行通讯
    解决Sql注入,防止恶意数据
    110. 平衡二叉树
    104.二叉树的最大深度
  • 原文地址:https://www.cnblogs.com/huigelaile/p/11063990.html
Copyright © 2020-2023  润新知