1、佇列: 先進先出(FIFO),例如超市的收銀臺、排隊買票的顧客。在Java中,它和List的區別在於,List可以在任意位置新增和刪除元素,而Queue只有兩個操作:
- 把元素新增到佇列末尾;
- 從佇列頭部取出元素。
2、棧: 下壓棧,後進先出(LIFO),例如你辦公桌上的一疊信件,新信件來時將它們放在最上面(push方法),當閱讀時從上到下取件(pop方法)。
3、雙棧算術表示式求值:
例如計算(1+((2+3)*(4*5)))
的值:用兩個棧,一個儲存運算子(運算子棧),一個儲存數字(運算元棧)。
從左到右逐個將實體送入棧處理:
1、遇到數字時,將數字壓入運算元棧,遇到運演算法時,壓入運算子棧;
2、遇到左括號時忽略;
3、遇到右括號,彈出一個運算子,彈出所需數量的數字,並將運算子和數字的運算結果壓入運算元棧。
雙棧算術表示式求值演算法(為了程式碼簡潔未考慮異常):
import java.util.Stack;
public class EvaluateTest {
public static void main(String[] args) {
// 需要計算的表示式
String str = "(1+((2+3)*(4*5)))".trim();
// 運算子棧和運算元棧
Stack<String> ops = new Stack<>();
Stack<Double> vals = new Stack<>();
for(int i=0;i < str.length();i++) {
String s = String.valueOf(str.charAt(i));
if(s.equals("(")){
// 左括號時忽略
}else if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/") || s.equals("sqrt")){
// 運算子時壓運算子棧
ops.push(s);
}else if(s.equals(")")){
// 右括號時,將兩個棧都pop,再計算、壓棧
String op = ops.pop();
Double v = vals.pop();
if(op.equals("+")){
v = vals.pop() + v;
}else if(op.equals("-")){
v = vals.pop() - v;
}if(op.equals("*")){
v = vals.pop() * v;
}if(op.equals("/")){
v = vals.pop() / v;
}if(op.equals("sqrt")){
v = Math.sqrt(v);
}
vals.push(v);
}else{
// 最後是數字時,轉為double壓入運算元棧
vals.push(Double.valueOf(s));
}
}
System.out.println("最終運算結果:" + vals.pop());
}
}
4、連結串列
連結串列是一種遞迴的資料結構,它可以為空(null),可以是指向一個節點(node)的引用。該節點包含一個元素(資料域,儲存節點含有的資訊)和一個指向另一條連結串列或節點的引用(引用域)。
連結串列的特點:
- 插入和刪除元素方便;
- 查詢資料時效率低,訪問某個位置的資料要從第一個節點開始訪問,根據第一個節點儲存的下一個節點的地址找到第二個節點,以此類推;
可以看完下面的流程再來理解它的特點。這裡主要介紹單向連結串列:
// 一個節點
public class Node {
public Object item;
public Node next;
}
虛擬碼構造一條連結串列:
建立好連結串列後,在表頭插入一個節點很容易,如下圖,如果在表頭插入字串"not",先將first儲存在oldfirst中,然後將一個新節點賦給first,並將它的item元素設為not,next設為oldfirst。
在表頭刪除一個節點,將first指向first.next即可。曾經的第一個節點物件變成了一個孤兒,Java的記憶體管理最終將回收它所佔用的記憶體:
請注意:當連結串列中只有一個節點時,它既是首節點又是尾節點,另外注意連結串列為空的情況。
那麼在表尾插入節點可以表示為:
以前連結串列操作只需要幾行賦值程式碼,所需時間和連結串列的長度無關。但如果需要刪除表尾節點,就要遍歷整條連結串列並找出指向last節點的節點,這樣所需的時間和連結串列的長度成正比。
要想實現任意插入和刪除操作,可以使用雙向連結串列,這裡不作介紹。
5、遍歷連結串列 我們知道遍歷陣列可以用for(int i = 0; i < N; i++){...}
;那麼遍歷連結串列也有一個對應方式:
for (Node x = first; x != null; x = x.next) {
// 處理 x.item
}
6、堆疊的連結串列實現
直接上程式碼:
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Stack<Item> implements Iterable<Item> {
private Node<Item> first; // 棧頂(最近新增的元素)
private int n; // 元素數量
private static class Node<Item> {
private Item item;
private Node<Item> next;// 定義了節點的巢狀類
}
/**
* Initializes an empty stack.
*/
public Stack() {
first = null;
n = 0;
}
/**
* 當first==null或者n==0時,棧是空的
*/
public boolean isEmpty() {
return first == null;
}
public int size() {
return n;
}
/**
* 向棧頂新增元素
*/
public void push(Item item) {
Node<Item> oldfirst = first;
first = new Node<Item>();
first.item = item;
first.next = oldfirst;
n++;
}
/**
* 從棧頂刪除元素
*/
public Item pop() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
Item item = first.item;
first = first.next;
n--;
return item;
}
/**
* 只取值,不刪除
*/
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Stack underflow");
return first.item;
}
/**
* 按照LIFO的順序,返回一個迭代器可以迭代此類
*/
public Iterator<Item> iterator() {
return new LinkedIterator(first);
}
private class LinkedIterator implements Iterator<Item> {
private Node<Item> current;
public LinkedIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
7、佇列的連結串列實現
Queue的實現使用的資料結構和Stack類相同,都是連結串列,但它實現了不同的新增和刪除演算法,這也是FIFO和LIFO的區別所在。
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Queue<Item> implements Iterable<Item> {
private Node<Item> first; // 指向最早新增的節點的引用
private Node<Item> last; // 隊尾,最近新增
private int n;
private static class Node<Item> {
private Item item;
private Node<Item> next;
}
public Queue() {
first = null;
last = null;
n = 0;
}
public boolean isEmpty() {
return first == null;
}
public int size() {
return n;
}
/**
* 向表尾新增元素
*/
public void enqueue(Item item) {
Node<Item> oldlast = last;
last = new Node<Item>();
last.item = item;
last.next = null;
if (isEmpty()){
first = last;
}else{
oldlast.next = last;
}
n++;
}
/**
* 從表頭刪除元素
*/
public Item dequeue() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
Item item = first.item;
first = first.next;
n--;
if (isEmpty()) last = null;
return item;
}
/**
* 從表頭獲取元素,不刪除
*/
public Item peek() {
if (isEmpty()) throw new NoSuchElementException("Queue underflow");
return first.item;
}
public Iterator<Item> iterator() {
return new LinkedIterator(first);
}
private class LinkedIterator implements Iterator<Item> {
private Node<Item> current;
public LinkedIterator(Node<Item> first) {
current = first;
}
public boolean hasNext() { return current != null; }
public void remove() { throw new UnsupportedOperationException(); }
public Item next() {
if (!hasNext()) throw new NoSuchElementException();
Item item = current.item;
current = current.next;
return item;
}
}
}
相關文獻:
Bags, Queues, and Stacks