前端資料結構(1)之棧及其應用

zhunny發表於2019-03-06

基礎

  棧是一種特殊的線性表,它可以用陣列或者連結串列來實現。只能在棧頂運算元據,具有先進後出的特點。
  假如有一個序列1,2,3,4,5。將它按順序加入棧中,它的結構如下圖:

前端資料結構(1)之棧及其應用

  現實生活中,我們也能看到很多關於棧的結構,比如說網球桶,你拿到的網球都是桶頂的,放回去的時候也會放到桶頂。
  在Js中,像執行上下文也使用棧這種資料結構來維護的,棧底是全域性作用域,當前執行程式碼的執行上下文依次加入棧中,棧頂的元素永遠是正在執行的上下文物件。基本資料型別的值也放在棧中。
  在canvas標籤中,有兩個方法save()和restore()也用到了棧這種資料結構,save方法將canvas當前繪圖環境的所有屬性壓棧,之後可以改變一些屬性做某些操作,當你想恢復之前儲存的繪圖環境時,呼叫restore方法將canvas狀態堆疊的頂部條目彈出,就實現了canvas繪圖環境的儲存和恢復。

棧的實現

  我用陣列實現了棧這種資料結構,因為js中陣列方法實現了push和pop的方法,再來實現棧會非常的簡單。
  操作棧的方法有:push(將元素加入棧頂),pop(將元素從棧頂刪除),top(返回棧頂元素),size(返回棧裡元素的個數),isEmpty(判斷棧是否為空),clear(清空棧中的元素)。

const Stack = function() {
    let items = [];
    this.push = function(item) {
        items.push(item);
    }
    this.pop = function() {
        return items.pop();
    }
    this.top = function() {
        return items[items.length - 1];
    }
    this.size = function() {
        return items.length;
    }
    this.isEmpty = function() {
        return items.length == 0;
    }
    this.clear = function() {
        items = [];
    }
}

複製程式碼

棧的應用題

  1. 括號匹配

  合法的括號字串應該是一個左括號就能匹配到一個對應的右括號。而且字串中還會出現除了括號以外的的字元。如果用陣列來解決這個問題的話,那麼我們遍歷到一個左括號,之後哪個右括號是它的右括號我們很難尋找。如果用棧來做思路就會非常簡單。
  遇到左括號,將其壓棧;遇到右括號,去棧頂元素,若棧為空,說明它沒有對應的左括號,返回false,若棧頂元素是與它匹配的左括號,則繼續遍歷字串;若遍歷完字串,棧不為空,則左括號多餘了,返回false。

const isMatch = function(string) {
    let stack = new Stack();
    let obj = {
        ")": "(",
        "}": "{",
        "]": "["
    }
    for (value of string) {
        if (value == "(" || value == "[" || value == "{") {
            stack.push(value);
        } else if (value == ")" || value == "]" || value == "}") {
            if (stack.size()) {
                let top = stack.pop();
                if (top !== obj[value]) {
                    return false;
                }
            } else {
                return false;
            }
        }
    }
    if (stack.size() !== 0) {
        return false;
    }
    return true;
}

複製程式碼

2.實現一個有求最小值方法的棧

  每次棧中的資料改變,那麼這個棧的最小值很可能會改變。我們可以考慮用棧來儲存最小值,這個最小值棧會在每次pop和pop時更新,它的棧頂的資料永遠是此時資料棧中的最小值。

const minStack = function() {
    let items = [];
    let min_stack = new Stack();
    this.push = function(item) {
        if (items.length == 0) {
            items.push(item);
            min_stack.push(item);
        } else {
            if (item < min_stack.top()) {
                min_stack.push(item);
            } else {
                min_stack.push(min_stack.top());
            }
            items.push(item);
        }
    }

    this.pop = function() {
        min_stack.pop();
        return items.pop();
    }

    this.min = function() {
        return min_stack.pop();
    }
}
複製程式碼

3.使用非遞迴的方法實現樹的前序遍歷
  前序遍歷的順序是先遍歷當前節點,然後遍歷它的左子樹,最後再遍歷它的的右子樹。當遍歷完左子樹怎麼找到右子樹是這個問題的關鍵,我們考慮用棧儲存右子樹,當遍歷完左子樹,則彈出棧頂的一個右子樹節點繼續遍歷。同理,迭代法如何實現樹的中序遍歷和後序遍歷是一樣的思路,可能實現的時候會比前序遍歷難一些。

let pre_order2 = function(node) {
    if(node == null){
        return;
    }
    let stack = new Stack();
    let curr_node = node;
    while (curr_node) {
        console.log(curr_node.data);
        if (curr_node.rightchild) {
            stack.push(curr_node.rightchild);
        }
        if (curr_node.leftchild) {
            curr_node = curr_node.leftchild;
        } else {
            curr_node = stack.pop();
        }
    }
}
複製程式碼

相關文章