- public interface MyStack<T> {
- /**
- * 判斷棧是否爲空
- */
- boolean isEmpty();
- /**
- * 清空棧
- */
- void clear();
- /**
- * 棧的長度
- */
- int length();
- /**
- * 數據入棧
- */
- boolean push(T data);
- /**
- * 數據出棧
- */
- T pop();
- }
棧的數組實現, 底層使用數組:javascript
Java代碼java
- 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();
- }
- }
棧的鏈表實現, 底層使用鏈表:node
Java代碼數組
- 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;
- }
- }
兩種實現的比較, 主要比較數據入棧和出棧的速度:app
Java代碼ide
- @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: 47ui
將MyArrayStack改成MyLinkedStack後入棧和出棧的時間:this
push time: 936
pop time: 126
可見二者的入棧速度差很少, 出棧速度MyArrayStack則有明顯的優點.
爲何測試結果是這樣的? 可能有些朋友的想法是數組實現的棧應該具備更快的遍歷速度, 但增刪速度應該比不上鍊表實現的棧纔對. 可是棧中數據的增刪具備特殊性: 只在棧頂入棧和出棧. 也就是說數組實現的棧在增長和刪除元素時並不須要移動大量的元素, 只是在數組擴容時須要進行復制. 而鏈表實現的棧入棧和出棧時都須要將數據包裝成Node或者從Node中取出數據, 還須要維護棧頂指針和前驅指針.
棧的應用舉例
1. 將10進制正整數num轉換爲n進制
Java代碼
- 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入棧. 當遍歷完成時棧爲空說明字符串是合法的.
Java代碼
- 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. 行編輯: 輸入行中字符'#'表示退格, '@'表示以前的輸入全都無效.
使用棧保存輸入的字符, 若是遇到'#'就將棧頂出棧, 若是遇到@就清空棧. 輸入完成時將棧中全部字符出棧後反轉就是輸入的結果:
Java代碼
- 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();
- }
或者
棧數組實現一:優勢:入棧和出棧速度快,缺點:長度有限(有時候這也不能算是個缺點)
[java] view plain copy
- public class Stack {
- private int top = -1;
- private Object[] objs;
-
- public Stack(int capacity) throws Exception{
- if(capacity < 0)
- throw new Exception("Illegal capacity:"+capacity);
- objs = new Object[capacity];
- }
-
- public void push(Object obj) throws Exception{
- if(top == objs.length - 1)
- throw new Exception("Stack is full!");
- objs[++top] = obj;
- }
-
- public Object pop() throws Exception{
- if(top == -1)
- throw new Exception("Stack is empty!");
- return objs[top--];
- }
-
- public void dispaly(){
- System.out.print("bottom -> top: | ");
- for(int i = 0 ; i <= top ; i++){
- System.out.print(objs[i]+" | ");
- }
- System.out.print("\n");
- }
-
- public static void main(String[] args) throws Exception{
- Stack s = new Stack(2);
- s.push(1);
- s.push(2);
- s.dispaly();
- System.out.println(s.pop());
- s.dispaly();
- s.push(99);
- s.dispaly();
- s.push(99);
- }
- }
[plain] view plain copy
- bottom -> top: | 1 | 2 |
- 2
- bottom -> top: | 1 |
- bottom -> top: | 1 | 99 |
- Exception in thread "main" java.lang.Exception: Stack is full!
- at Stack.push(Stack.java:17)
- at Stack.main(Stack.java:44)
數據項入棧和出棧的時間複雜度都爲常數O(1)
棧數組實現二:優勢:無長度限制,缺點:入棧慢
[java] view plain copy
- import java.util.Arrays;
-
- public class UnboundedStack {
- private int top = -1;
- private Object[] objs;
-
- public UnboundedStack() throws Exception{
- this(10);
- }
-
- public UnboundedStack(int capacity) throws Exception{
- if(capacity < 0)
- throw new Exception("Illegal capacity:"+capacity);
- objs = new Object[capacity];
- }
-
- public void push(Object obj){
- if(top == objs.length - 1){
- this.enlarge();
- }
- objs[++top] = obj;
- }
-
- public Object pop() throws Exception{
- if(top == -1)
- throw new Exception("Stack is empty!");
- return objs[top--];
- }
-
- private void enlarge(){
- int num = objs.length/3;
- if(num == 0)
- num = 1;
- objs = Arrays.copyOf(objs, objs.length + num);
- }
-
- public void dispaly(){
- System.out.print("bottom -> top: | ");
- for(int i = 0 ; i <= top ; i++){
- System.out.print(objs[i]+" | ");
- }
- System.out.print("\n");
- }
-
- public static void main(String[] args) throws Exception{
- UnboundedStack us = new UnboundedStack(2);
- us.push(1);
- us.push(2);
- us.dispaly();
- System.out.println(us.pop());
- us.dispaly();
- us.push(99);
- us.dispaly();
- us.push(99);
- us.dispaly();
- }
- }
[plain] view plain copy
- bottom -> top: | 1 | 2 |
- 2
- bottom -> top: | 1 |
- bottom -> top: | 1 | 99 |
- bottom -> top: | 1 | 99 | 99 |
因爲該棧是由數組實現的,數組的長度是固定的,當棧空間不足時,必須將原數組數據複製到一個更長的數組中,考慮到入棧時或許須要進行數組複製,平均須要複製N/2個數據項,故入棧的時間複雜度爲O(N),出棧的時間複雜度依然爲O(1)
棧單鏈表實現:沒有長度限制,而且出棧和入棧速度都很快
[java] view plain copy
- public class LinkedList {
- private class Data{
- private Object obj;
- private Data next = null;
-
- Data(Object obj){
- this.obj = obj;
- }
- }
-
- private Data first = null;
-
- public void insertFirst(Object obj){
- Data data = new Data(obj);
- data.next = first;
- first = data;
- }
-
- public Object deleteFirst() throws Exception{
- if(first == null)
- throw new Exception("empty!");
- Data temp = first;
- first = first.next;
- return temp.obj;
- }
-
- public void display(){
- if(first == null)
- System.out.println("empty");
- System.out.print("top -> bottom : | ");
- Data cur = first;
- while(cur != null){
- System.out.print(cur.obj.toString() + " | ");
- cur = cur.next;
- }
- System.out.print("\n");
- }
- }
[java] view plain copy
- public class LinkedListStack {
- private LinkedList ll = new LinkedList();
-
- public void push(Object obj){
- ll.insertFirst(obj);
- }
-
- public Object pop() throws Exception{
- return ll.deleteFirst();
- }
-
- public void display(){
- ll.display();
- }
-
- public static void main(String[] args) throws Exception{
- LinkedListStack lls = new LinkedListStack();
- lls.push(1);
- lls.push(2);
- lls.push(3);
- lls.display();
- System.out.println(lls.pop());
- lls.display();
- }
- }
[plain] view plain copy
- top -> bottom : | 3 | 2 | 1 |
- 3
- top -> bottom : | 2 | 1 |
數據項入棧和出棧的時間複雜度都爲常數O(1)
/**
* 基於數組實現的順序棧
* @param <E>
*/
public class Stack<E> {
private Object[] data = null;
private int maxSize=0; //棧容量
private int top =-1; //棧頂指針
/**
* 構造函數:根據給定的size初始化棧
*/
Stack(){
this(10); //默認棧大小爲10
}
Stack(int initialSize){
if(initialSize >=0){
this.maxSize = initialSize;
data = new Object[initialSize];
top = -1;
}else{
throw new RuntimeException("初始化大小不能小於0:" + initialSize);
}
}
//判空
public boolean empty(){
return top==-1 ? true : false;
}
//進棧,第一個元素top=0;
public boolean push(E e){
if(top == maxSize -1){
throw new RuntimeException("棧已滿,沒法將元素入棧!");
}else{
data[++top]=e;
return true;
}
}
//查看棧頂元素但不移除
public E peek(){
if(top == -1){
throw new RuntimeException("棧爲空!");
}else{
return (E)data[top];
}
}
//彈出棧頂元素
public E pop(){
if(top == -1){
throw new RuntimeException("棧爲空!");
}else{
return (E)data[top--];
}
}
//返回對象在堆棧中的位置,以 1 爲基數
public int search(E e){
int i=top;
while(top != -1){
if(peek() != e){
top --;
}else{
break;
}
}
int result = top+1;
top = i;
return result;
}
}
棧的鏈式存儲結構實現:
public class LinkStack<E> {
//鏈棧的節點
private class Node<E>{
E e;
Node<E> next;
public Node(){}
public Node(E e, Node next){
this.e = e;
this.next = next;
}
}
private Node<E> top; //棧頂元素
private int size; //當前棧大小
public LinkStack(){
top = null;
}
//當前棧大小
public int length(){
return size;
}
//判空
public boolean empty(){
return size==0;
}
//入棧:讓top指向新建立的元素,新元素的next引用指向原來的棧頂元素
public boolean push(E e){
top = new Node(e,top);
size ++;
return true;
}
//查看棧頂元素但不刪除
public Node<E> peek(){
if(empty()){
throw new RuntimeException("空棧異常!");
}else{
return top;
}
}
//出棧
public Node<E> pop(){
if(empty()){
throw new RuntimeException("空棧異常!");
}else{
Node<E> value = top; //獲得棧頂元素
top = top.next; //讓top引用指向原棧頂元素的下一個元素
value.next = null; //釋放原棧頂元素的next引用
size --;
return value;
}
}
}
基於LinkedList實現的棧結構:
import java.util.LinkedList;
/**
* 基於LinkedList實現棧
* 在LinkedList實力中只選擇部分基於棧實現的接口
*/
public class StackList<E> {
private LinkedList<E> ll = new LinkedList<E>();
//入棧
public void push(E e){
ll.addFirst(e);
}
//查看棧頂元素但不移除
public E peek(){
return ll.getFirst();
}
//出棧
public E pop(){
return ll.removeFirst();
}
//判空
public boolean empty(){
return ll.isEmpty();
}
//打印棧元素
public String toString(){
return ll.toString();
}
}