java 佇列與棧實現(連結串列與陣列)
我們經常會問到java資料結構可以怎麼實現,看過了演算法之後得到很大啟發,這裡整理如下。
佇列是一種先進先出(FIFO)的集合模型,而棧則是後進先出(LIFO)的集合模型,我們經常使用它們用來儲存元素的相對順序。Java中在java.util.LinkedList類中實現了關於佇列和棧的實現,但是這是一個寬介面的例子,這裡我們自己實現它以研究它的思路。
有兩種基礎可以用來實現:陣列和連結串列
我們實現棧的基本要素如下:
int N 代表棧的容量
boolean isEmpty()用來判斷棧是否為空
int size()返回棧的容量
void push()進棧操作,每次棧頂新增元素
T pop()出棧操作,由於出棧後棧中元素會得到釋放,因此我們需要一個返回值紀錄出棧元素以便操作。這裡返回值為一個範型,可以把範型理解為佔位符,即開始時不規定型別是什麼,到後來用到時再定義,這裡範型可以為任何類。
陣列實現:
由於陣列在初始化時必須設定儲存容量,而我們並不知道我們的棧與佇列需要多大的儲存空間,因此我們對思路是在對元素進行進入操作時與容量大小進行比較,關鍵思路是如果超過了則對容量進行“擴容”,即複製一份陣列並將原來那份廢棄。在這裡對元素使用率進行了判斷,使其不回低於1/4.
public class StackInArray<T> implements Iterable<T> { private T[] a = (T[]) new Object[1]; private int N = 0; public boolean isEmpty() { return N == 0; } public int size() { return N; } private void resize(int max) { T[] temp = (T[]) new Object[max]; for (int i = 0; i < N; i++) { temp[i] = a[i]; } a = temp; } public void push(T t) { if (N == a.length) resize(a.length << 1);//移位操作,相當於*2,感覺更能理解擴容一倍的意思。 a[N++] = t; } public T pop() { T t = a[--N]; a[N] = null; if (N > 0 && N == a.length / 4) resize(a.length >> 1); return t; } @Override public Iterator<T> iterator() { return new ArrayIterator(); } private class ArrayIterator implements Iterator<T> { private int i = N; @Override public boolean hasNext() { return i > 0; } @Override public T next() { return a[--i]; } } }
這裡棧的基本元素為一個範型陣列。這裡新增了一個void resize(int max)方法用來對陣列進行大小調整。
當我們進棧和出棧時,實際上是對陣列中最後一個標記
看完了陣列實現的棧我們接下來看由連結串列實現的棧,首先複習一下連結串列。
連結串列定義如下:連結串列時一種遞迴的資料結構,它或者為空,或者是指向一個結點的引用,該結點含有一個範型的元素和一個指向另一條連結串列的引用。
在java中,我們可以用一個巢狀類定義結點實現連結串列
private class Node {
T t;
Node next;
}
用連結串列實現的棧基本操作與用陣列實現棧的操作一致,只不過我們這裡用Node資料型別替代陣列,因此在初始化的時候不需要定義棧的大小。
public class StackInChain<T> implements Iterable<T> {
private Node first;// 棧頂
private int N;// 容量
private class Node {
T t;
Node next;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return N;
}
public void push(T t) {
Node oldFirst = first;
first = new Node();
first.t = t;
first.next = oldFirst;
N++;
}
public T pop(){
T t=first.t;
first=first.next;
N--;
return t;
}
}
連結串列實現的棧思路很明顯,每當push時就在頭節點前新增一個節點,使其作為新的頭節點,並使容量自增即可。出棧時將頭節點向後移一位即可。
資料結構 | 優點 | 缺點 |
陣列 | 通過索引可以直接訪問任意元素 | 在初始化時就需要知道元素的數量 |
連結串列 | 使用的空間大小與元素數量正比 | 需要通過索引訪問任意元素 |