主要學習知識點
- 棧的概念及其抽象資料型別描述
- 順序棧類和鏈棧的描述和實現
- 佇列的概念及其抽象資料型別描述
- 順序迴圈佇列類和鏈佇列類的描述與實現
一、棧
1. 概念:
- 棧的定義: 棧(Stack)是限制僅在表的一端進行插入和刪除運算的線性表。
- 通常稱插入、刪除的這一端為棧頂(Top),另一端稱為棧底(Bottom)。
- 當表中沒有元素時稱為空棧。
- 棧為後進先出(Last In First Out)的線性表,簡稱為LIFO表。
2. 棧的抽象資料介面
public interface IStack<E> {
public void clear();//清空棧
public boolean isEmpty();//是否空棧
public int size();//棧資料元素的個數
public E peek();//讀取棧頂元素
public void push(E e);//進棧
public E pop();//出棧,並返回該元素。
}
複製程式碼
3. 順序儲存結構及實現
- 入棧操作
-
注意:
- 順序棧為空的條件是 top == 0
- 順序棧滿的條件尾 top == stackElem.length
- 棧的長度為top
- 棧頂元素的下標為 top -1
-
入棧示意圖
-
具體實現
public void push(E e) {
//檢查棧是否已滿
if (top == elementData.length)
throw new RuntimeException("棧已滿!");
//儲存元素
elementData[top] = e;
//棧頂指標 加一
top++;
}
複製程式碼
- 出棧操作
- 示意圖
- 具體實現
// 獲取棧頂元素
public E peek() {
//判斷是否為空棧
if (isEmpty())
throw new RuntimeException("棧為空!");
return (E) elementData[top - 1]; //返回棧頂元素;
}
//出棧
public E pop() {
E peek = peek();
//置空出棧元素
elementData[top - 1] = null;
//棧頂指標減一
top--;
return peek;
}
複製程式碼
4. 鏈式棧及其實現
- 入棧操作
-
示意圖
-
程式碼實現
public void push(E e) {
//建立節點
LNode node = new LNode(e);
//修改棧頂指標
node.next = top;
top = node;
//棧長度加一
length++;
}
複製程式碼
- 出棧操作
-
示意圖
-
程式碼實現
//獲取棧頂元素
public E peek() {
if (isEmpty())
throw new RuntimeException("棧為空!");
return top.data;
}
//出棧
public E pop() {
//獲取棧頂元素
E pop = peek();
//修改棧頂指標
top = top.next;
//棧長度減一
length--;
return pop;
}
複製程式碼
二、佇列
1. 定義:
- 佇列(Queue)是隻允許在一端進行插入,而在另一端進行刪除的運算受限的線性表
- 佇列亦稱作先進先出(First In First Out)的線性表,簡稱為FIFO表。
2. 佇列抽象介面定義
/**
* 佇列介面
*/
public interface IQueue<T> {
void clear();//清空佇列
boolean isEmpty();//判斷空佇列
int size();//佇列長度
T peek(); //讀取隊首元素
void offer(T t);//入佇列
T poll();//出佇列
}
複製程式碼
3. 順序佇列及其實現
- 出入佇列示意圖
- 假溢現象
出現原因:
- 如出入佇列的示意圖
- 在初始化一個長度為6的佇列
- 先入佇列A、B、C,然後A、B又出佇列,造成對頭有兩個位置空置
- 接著E、F、G入隊後,如果想H入隊,這時必定會引起陣列越界異常
- 佇列有空間,但不能入隊的溢位現象稱為假溢現象
解決方式:
- 採用首尾相連的迴圈順序佇列,避免出現出隊後空出儲存空間
迴圈順序佇列的四種狀態圖
- 解決無法區分隊空和隊滿的狀態問題(front: 隊首指標,rear:隊尾指標, maxSize:容量大小)
方法一、採用少存一個儲存單元的方法
- 隊空判斷條件為: front == rear
- 隊滿判斷條件:front == (rear+1) % maxSize
設定一個標誌變數:flag, 其初始值為flag = 0, 入隊flag = 1, 出佇列flag = 0
- 隊空判斷條件為: front == rear && flag == 0
- 隊滿判斷條件:front == rear && flag == 1
設定一個計數器: length , 入隊length ++; 出隊length--
- 隊空判斷條件為: length == 0
- 隊滿判斷條件:length >0 && front == rear
4. 鏈式佇列及其實現
-
示意圖
-
入佇列操作
- 程式碼實現
public void offer(T t) {
// 建立節點
LNode<T> node = new LNode<T>(t);
//判斷佇列是否為空,空則賦值為隊首和隊尾指標
if (isEmpty()) {
front = rear = node;
} else {
//不為空,則插入隊尾
//佇列的尾節點的指標指向新節點
rear.next = node;
//尾指標指向新節點,作為新尾節點4
rear = node;
}
//長度加一
++length;
}
複製程式碼
- 出佇列
//獲取隊首元素
public T peek() {
//判斷是否為空佇列
if (isEmpty())
throw new RuntimeException("佇列為空!");
return front.data;
}
//出佇列
public T poll() {
//獲取隊首元素
T poll = peek();
//修改隊首指標
this.front = this.front.next;
//長度減一
--length;
//返回資料
return poll;
}
複製程式碼
三、 棧和佇列的區別
相同點
- 都是線性結構,元素之間具有“一對一”的邏輯關係
- 插入操作都是再表尾進行
- 都可以使用順序儲存結構或鏈式儲存結構實現
- 在時間代價上,插入刪除操作的時間複雜度都是O(1), 在空間待見上也相同
不同點
- 刪除操作的位置不同,棧只在表尾操作,佇列在表頭操作
- 棧是後進先出(LIFO),佇列是先進先出(FIFO),應用場景不同
- 順序棧可以多棧空間共享,而順序佇列不同