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數據結構和算法(四)——棧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM