數據結構之——棧

數據結構學習—— 棧(stack)java

  • 什麼是棧
  • 無處不在的棧——棧的應用
  • 使用數組實現ArrayStack及時間複雜度分析
  • Leetcode20題:有效的括號

什麼是棧(stack)

棧(stack)是一種運算受限的線性數據結構,運算受限指的是棧這種數據結構僅容許在一端添加元素,刪除元素,這一端被稱做棧頂,相對應的另外一端則稱爲棧底。如圖所示:
git

stack.jpg

當前棧,若是想要添加元素「D」只能從棧頂部添加,從棧中取出元素則仍是從棧頂開始取元素,因此棧是一種後進先出的數據結構即:LIFO(Last In First Out)。

無處不在的棧——棧的應用

一:Undo(撤銷操做)

當咱們在文檔編輯器中輸入文字,當發現輸入錯誤時,想要撤銷到前一步,這個操做就是Undo。撤銷的原理實際上就是棧這種數據結構來設計實現的。例如:李雷在某個文檔編輯器上輸入文字「我愛韓梅梅」,結果,因爲李雷滿腦子想的都是韓梅梅的音容笑貌,不當心將內容輸入成了「我愛含梅梅」。李雷想將內容恢復到「我愛」 這一步,因此他按了三次「Ctrl+z」,而後又依次將「韓」,「梅」,「梅」三個字輸入了進去。
github

Undo1

Undo2

Undo3

Undo(撤銷)看似很高級的操做,背後的原理就是棧。

二:C語言printf()函數

來看一個C語言的問題:算法

#include<stdio.h>
int main(void){
    int i=1;
    printf("%d%d%d",i,i++,i++);
    return 0;
}
複製代碼

這個程序的運行結果是什麼?若是隻是知道i++與++i的區別是不足以解決這道問題的。先公佈答案,這個程序的運行結果爲:321,這與printf的底層原理有關,由於printf的底層實現就是棧。仍是拿李雷韓梅梅來舉例說明。數組

printf("我愛韓梅梅");
複製代碼

printf函數首先會將字符串內容從右至左 push到棧中。bash

printf

而後,再將棧裏面的元素依次pop出來,這樣咱們就能看到"我愛韓梅梅"這個字符串被打印出來了。這道題也是同樣,首先將最右邊的%d即「i++」 push到棧中,i==1,1入棧後,執行++操做,i的值變成了2。按照上述思路依次將全部元素推入棧中,棧的狀況爲:

printf2

將全部的元素出棧,出棧的順序就是咱們看到的打印結果即:321。

三:程序調用系統棧

有以下程序:數據結構

A();
function A(){
1    ...
2    B();
3    ...
4    end
}
function B(){
1    ...
2    C();
3    ...
4    end
}
function C(){
1    ...
2    ...
3    ...
4    end
}
複製代碼

程序從A方法開始調用,執行到 A方法的第二行,計算機發現須要執行B方法,這時就會將執行到哪一步這樣一個信息壓入到系統棧中。例如,定義A2爲A方法的第二行,計算機此時將A2壓入系統棧,代表執行到了A方法的第二行。
編輯器

system

計算機在系統棧壓入這樣一個信息後,開始執行B方法,執行到B方法的第二行,發現須要執行C方法,因而計算機將B2壓入系統棧中。

system

計算機開始執行C方法,C方法中沒有調用其餘的函數,執行結束後,計算機發現系統棧中有殘留的任務,因而pop stack 發現須要回去執行完B方法,且執行到了B方法的第二行。B方法執行完畢後,計算機又去看了看系統棧,發現仍有殘留的任務須要執行,因而乎又 pop stack 發現原來A方法尚未執行完畢,且執行到了第二行,因此計算機又將A方法執行完畢。這時系統棧爲空,計算機終於鬆了一口氣,知道全部的任務已經執行完畢了~

使用數組實現ArrayStack及時間複雜度分析

本文中ArrayStack的底層實現數組爲動態數組:動態數組,DobbyKim's Blog
函數

public interface Stack<E> {
    void push(E e);
    E pop();
    E peek();
    int getSize();
    boolean isEmpty();
}
public class ArrayStack<E> implements Stack<E>{
    Array<E> array;
    public ArrayStack(int capacity){
        array = new Array<>(capacity);
    }
    public ArrayStack(){
        array = new Array<>();
    }
    public void push(E e){...}
    public E pop(){...}
    public int getSize(){...}
    public int getCapacity(){...}
    public boolean isEmpty(){...}
    public E peek(){...}
    public String toString(){...}
}
複製代碼

點擊查看源碼
ArrayStack的方法push 與 pop 的均攤時間複雜度爲O(1),由於這裏面涉及到底層實現Array爲動態數組,resize()擴容操做爲一個O(n)的算法。getSize()方法,peek()方法,isEmpty()方法的時間複雜度均爲O(1)。學習

Leetcode20題:有效的括號

給定一個只包括 '(',')','{','}','[',']' 的字符串,判斷字符串是否有效。
有效字符串需知足:
左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。

  • 示例 1:
輸入: "()"
輸出: true
複製代碼
  • 示例 2:
輸入: "()[]{}"
輸出: true
複製代碼
  • 示例 3:
輸入: "(]"
輸出: false
複製代碼
  • 示例 4:
輸入: "([)]"
輸出: false
複製代碼
  • 示例 5:
輸入: "{[]}"
輸出: true
複製代碼

問題解決思路:棧。只要是左側的括號爲'(','[','{'就push到棧中,遇到與之匹配的右側括號則pop,最後棧若是爲空則說明匹配成功。Java代碼以下:

import java.util.Stack
class Solution {
	public boolean isValid(String s) {
        Stack<Character>stack = new Stack<>();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)=='(' || s.charAt(i)=='[' || s.charAt(i)=='{'){
                stack.push(s.charAt(i));
            }else{
                if(stack.isEmpty())
                    return false;
                char c = stack.pop;
                if(s.charAt(i)==')' && c!='(')
                    return false;
                if(s.charAt(i)==']' && c!='[')
                    return false;
                if(s.charAt(i)=='}' && c!='{')
                    return false;
            }
        }
        return stack.isEmpty();
    }
}
複製代碼
相關文章
相關標籤/搜索