1. 程式人生 > >資料結構與演算法學習筆記之後進先出的“桶”

資料結構與演算法學習筆記之後進先出的“桶”

前言

棧最為一種的常用的資料結構,用“桶”來形容最合適不過;今天我們就來學習一下

正文

一、棧的定義?


1.“後進先出,先進後出”的資料結構。
2.從操作特性來看,是一種“操作受限”的線性表,只可以在一端插入和刪除資料。

 

二、為什麼需要棧?

 

1.任何資料結構都是對特定應用場景的抽象,棧是一種操作受限的資料結構,其操作特性用陣列和連結串列均可實現,但卻暴露太多的操作介面,使用時容易出錯;


2.當某個資料集合只涉及在一端插入和刪除資料,且滿足後進者先出,先進者後出的操作特性時,我們應該首選棧這種資料結構

 

三、如何實現棧?

 棧可以用陣列,連結串列來實現

1.以陣列為例

空間複雜度為O(1)

時間複雜度大多數為O(1),特殊情況自動擴容拷貝原陣列時為O(n);

均攤時間複雜度接近於O(1);

// 基於陣列實現的順序棧
public class ArrayStack {
  private String[] items;  // 陣列
  private int count;       // 棧中元素個數
  private int n;           // 棧的大小

  // 初始化陣列,申請一個大小為 n 的陣列空間
  public ArrayStack(int n) {
    this.items = new String[n];
    this.n = n;
    this.count = 0;
  }

  // 入棧操作
  public boolean push(String item) {
    // 陣列空間不夠了,直接返回 false,入棧失敗。
    if (count == n) return false;
    // 將 item 放到下標為 count 的位置,並且 count 加一
    items[count] = item;
    ++count;
    return true;
  }
  
  // 出棧操作
  public String pop() {
    // 棧為空,則直接返回 null
    if (count == 0) return null;
    // 返回下標為 count-1 的陣列元素,並且棧中元素個數 count 減一
    String tmp = items[count-1];
    --count;
    return tmp;
  }
}

 (程式碼來自於網路,如有侵權請告知我刪除)

2.以連結串列為例(網上找的)

空間複雜度為O(1)

時間複雜度大多數為O(1)

public class StackOfLinked<Item> implements Iterable<Item> {
//定義一個內部類,就可以直接使用型別引數
private class Node{
Item item;
Node next;
}
private Node first;
private int N;
//構造器
public StackOfLinked(){}
//新增
public void push(Item item){
Node oldfirst = first;
first = new Node();
first.item = item;
first.next = oldfirst;
N++;
}
//刪除
public Item pop(){
Item item = first.item;
first = first.next;
N--;
return item;
}
//是否為空
public boolean isEmpty(){
return N == 0;
}
//元素數量
public int size(){
return N;
}
//返回棧中最近新增的元素而不刪除它
public Item peek(){
return first.item;
}
@Override
public Iterator<Item> iterator() {
return new LinkedIterator();
}
//內部類:迭代器
class LinkedIterator implements Iterator{
int i = N;
Node t = first;
@Override
public boolean hasNext() {
return i > 0;
}
@Override
public Item next() {
Item item = (Item) t.item;
t = t.next;
i--;
return item;
} 
}
}

(程式碼來自於網路,如有侵權請告知我刪除)

四、棧的應用

1.函式呼叫中的應用

作業系統給每個執行緒分配了一塊獨立的記憶體空間,這塊記憶體被組織成“棧”這種結構,用來儲存函式呼叫時的臨時變數。每進入一個函式,就會將其中的臨時變數作為棧幀入棧,當被呼叫函式執行完成,返回之後,將這個函式對應的棧幀出棧。

 

2.在表示式求值中的應用(比如:34+13*9+44-12/3)

利用兩個棧,一個用來儲存運算元,一個用來儲存運算子。我們從左向右遍歷表示式,當遇到數字,我們就直接壓入運算元棧;當遇到運算子,就與運算子棧的棧頂元素進行比較,若比運算子棧頂元素優先順序高,就將當前運算子壓入棧,若比運算子棧頂元素的優先順序低或者相同,從運算子棧中取出棧頂運算子,從運算元棧頂取出2個運算元,然後進行計算,把計算完的結果壓入運算元棧,繼續比較。

  (圖片來自於王爭)

3.棧在括號匹配中的應用(比如:{}{[()]()})

用棧儲存為匹配的左括號,從左到右一次掃描字串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,如果能匹配上,則繼續掃描剩下的字串。如果掃描過程中,遇到不能配對的右括號,或者棧中沒有資料,則說明為非法格式。
當所有的括號都掃描完成之後,如果棧為空,則說明字串為合法格式;否則,說明未匹配的左括號為非法格式。

 

4.如何實現瀏覽器的前進後退功能?


我們使用兩個棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當點選後退按鈕時,再依次從棧X中出棧,並將出棧的資料一次放入Y棧。當點選前進按鈕時,我們依次從棧Y中取出資料,放入棧X中。當棧X中沒有資料時,說明沒有頁面可以繼續後退瀏覽了。當Y棧沒有資料,那就說明沒有頁面可以點選前進瀏覽了。

(圖片來自於王爭)

 

五、兩個問題

1. 我們在講棧的應用時,講到用函式呼叫棧來儲存臨時變數,為什麼函式呼叫要用“棧”來儲存臨時變數呢?用其他資料結構不行嗎?


答:因為函式呼叫的執行順序符合後進者先出,先進者後出的特點。

函式呼叫中經常巢狀,栗子:A呼叫B,B又呼叫C,那麼就需要先把C執行完,結果賦值給B中的臨時變數,B的執行結果再賦值給A的臨時變數,巢狀越深的函式越需要被先執行,這樣剛好符合棧的特點,因此每次遇到函式呼叫,只需要壓棧,最後依次從棧頂彈出依次執行即可,根據資料結構是特定應用場景的抽象的原則,我們優先考慮棧結構。

 

2.我們都知道,JVM 記憶體管理中有個“堆疊”的概念。棧記憶體用來儲存區域性變數和方法呼叫,堆記憶體用來儲存 Java 中的物件。那 JVM 裡面的“棧”跟我們這裡說的“棧”是不是一回事呢?如果不是,那它為什麼又叫作“棧”呢?

 

答:記憶體中的堆疊和資料結構堆疊不是一個概念,可以說記憶體中的堆疊是真實存在的物理區,資料結構中的堆疊是抽象的資料儲存結構。
記憶體空間在邏輯上分為三部分:程式碼區、靜態資料區和動態資料區,動態資料區又分為棧區和堆區。
程式碼區:儲存方法體的二進位制程式碼。高階排程(作業排程)、中級排程(記憶體排程)、低階排程(程序排程)控制程式碼區執行程式碼的切換。
靜態資料區:儲存全域性變數、靜態變數、常量,常量包括final修飾的常量和String常量。系統自動分配和回收。
棧區:儲存執行方法的形參、區域性變數、返回值。由系統自動分配和回收。
堆區:new一個物件的引用或地址儲存在棧區,指向該物件儲存在堆區中的真實資料。

作者:Dawnzhang 
出處:https://www.cnblogs.com/clwydjgs/p/9792256.html

 

鄭州檢查婦科去哪個醫院

鄭州婦科醫院

鄭州男科醫院哪裡好

鄭州婦科醫院