棧的java實現和棧的應用舉例

本文地址:http://coolxing.iteye.com/blog/1468674

[例子和習題出自數據結構(嚴蔚敏版), 本人使用java進行實現.  轉載請註明作者和出處,  如有謬誤, 歡迎在評論中指正. ]

棧的實現

棧是一種先進後出的數據結構, 首先定義了棧需要實現的接口:

public interface MyStack<T> {
	/**
	 * 判斷棧是否爲空
	 */
	boolean isEmpty();
	/**
	 * 清空棧
	 */
	void clear();
	/**
	 * 棧的長度
	 */
	int length();
	/**
	 * 數據入棧
	 */
	boolean push(T data);
	/**
	 * 數據出棧
	 */
	T pop();
}

棧的數組實現, 底層使用數組:

public class MyArrayStack<T> implements MyStack<T> {
	private Object[] objs = new Object[16];
	private int size = 0;

	@Override
	public boolean isEmpty() {
		return size == 0;
	}

	@Override
	public void clear() {
		// 將數組中的數據置爲null, 方便GC進行回收
		for (int i = 0; i < size; i++) {
			objs[size] = null;
		}
		size = 0;
	}

	@Override
	public int length() {
		return size;
	}

	@Override
	public boolean push(T data) {
		// 判斷是否需要進行數組擴容
		if (size >= objs.length) {
			resize();
		}
		objs[size++] = data;
		return true;
	}

	/**
	 * 數組擴容
	 */
	private void resize() {
		Object[] temp = new Object[objs.length * 3 / 2 + 1];
		for (int i = 0; i < size; i++) {
			temp[i] = objs[i];
			objs[i] = null;
		}
		objs = temp;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T pop() {
		if (size == 0) {
			return null;
		}
		return (T) objs[--size];
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("MyArrayStack: [");
		for (int i = 0; i < size; i++) {
			sb.append(objs[i].toString());
			if (i != size - 1) {
				sb.append(", ");
			}
		}
		sb.append("]");
		return sb.toString();
	}
}  
棧的鏈表實現, 底層使用鏈表:

public class MyLinkedStack<T> implements MyStack<T> {
	/**
	 * 棧頂指針
	 */
	private Node top;
	/**
	 * 棧的長度
	 */
	private int size;
	
	public MyLinkedStack() {
		top = null;
		size = 0;
	}
	
	@Override
	public boolean isEmpty() {
		return size == 0;
	}
	
	@Override
	public void clear() {
		top = null;
		size = 0;
	}
	
	@Override
	public int length() {
		return size;
	}
	
	@Override
	public boolean push(T data) {
		Node node = new Node();
		node.data = data;
		node.pre = top;
		// 改變棧頂指針
		top = node;
		size++;
		return true;
	}
	
	@Override
	public T pop() {
		if (top != null) {
			Node node = top;
			// 改變棧頂指針
			top = top.pre;
			size--;
			return node.data;
		}
		return null;
	}
	
	/**
	 * 將數據封裝成結點
	 */
	private final class Node {
		private Node pre;
		private T data;
	}
}
兩種實現的比較, 主要比較數據入棧和出棧的速度:

@Test
public void testSpeed() {
	MyStack<Person> stack = new MyArrayStack<Person>();
	int num = 10000000;
	long start = System.currentTimeMillis();
	for (int i = 0; i < num; i++) {
		stack.push(new Person("xing", 25));
	}
	long temp = System.currentTimeMillis();
	System.out.println("push time: " + (temp - start));
	while (stack.pop() != null)
		;
	System.out.println("pop time: " + (System.currentTimeMillis() - temp));
}

MyArrayStack中入棧和出棧10,000,000條數據的時間:

push time: 936
pop time: 47

將MyArrayStack改爲MyLinkedStack後入棧和出棧的時間:

push time: 936

pop time: 126

可見兩者的入棧速度差不多, 出棧速度MyArrayStack則有明顯的優勢.

爲什麼測試結果是這樣的? 可能有些朋友的想法是數組實現的棧應該具有更快的遍歷速度, 但增刪速度應該比不上鍊表實現的棧纔對. 但是棧中數據的增刪具有特殊性: 只在棧頂入棧和出棧. 也就是說數組實現的棧在增加和刪除元素時並不需要移動大量的元素, 只是在數組擴容時需要進行復制. 而鏈表實現的棧入棧和出棧時都需要將數據包裝成Node或者從Node中取出數據, 還需要維護棧頂指針和前驅指針. 

 

棧的應用舉例

1. 將10進制正整數num轉換爲n進制

private String conversion(int num, int n) {
	MyStack<Integer> myStack = new MyArrayStack<Integer>();
	Integer result = num;
	while (true) {
		// 將餘數入棧
		myStack.push(result % n);
		result = result / n;
		if (result == 0) {
			break;
		}
	}
	StringBuilder sb = new StringBuilder();
	// 按出棧的順序倒序排列即可
	while ((result = myStack.pop()) != null) {
		sb.append(result);
	}
	return sb.toString();
}

2. 檢驗符號是否匹配. '['和']', '('和')'成對出現時字符串合法. 例如"[][]()", "[[([]([])()[])]]"是合法的; "([(])", "[())"是不合法的.

遍歷字符串的每一個char, 將char與棧頂元素比較. 如果char和棧頂元素配對, 則char不入棧, 否則將char入棧. 當遍歷完成時棧爲空說明字符串是合法的.

public boolean isMatch(String str) {
	MyStack<Character> myStack = new MyArrayStack<Character>();
	char[] arr = str.toCharArray();
	for (char c : arr) {
		Character temp = myStack.pop();
		// 棧爲空時只將c入棧
		if (temp == null) {
			myStack.push(c);
		}
		// 配對時c不入棧
		else if (temp == '[' && c == ']') {
		} 
		// 配對時c不入棧
		else if (temp == '(' && c == ')') {
		} 
		// 不配對時c入棧
		else {
			myStack.push(temp);
			myStack.push(c);
		}
	}
	return myStack.isEmpty();
}

3. 行編輯: 輸入行中字符'#'表示退格, '@'表示之前的輸入全都無效.

使用棧保存輸入的字符, 如果遇到'#'就將棧頂出棧, 如果遇到@就清空棧. 輸入完成時將棧中所有字符出棧後反轉就是輸入的結果:

private String lineEdit(String input) {
	MyStack<Character> myStack = new MyArrayStack<Character>();
	char[] arr = input.toCharArray();
	for (char c : arr) {
		if (c == '#') {
			myStack.pop();
		} else if (c == '@') {
			myStack.clear();
		} else {
			myStack.push(c);
		}
	}
	
	StringBuilder sb = new StringBuilder();
	Character temp = null;
	while ((temp = myStack.pop()) != null) {
		sb.append(temp);
	}
	// 反轉字符串
	sb.reverse();
	return sb.toString();
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章