手把手解決三道括號相關的算法題

Python實戰社羣php

Java實戰社羣程序員

長按識別下方二維碼,按需求添加web

掃碼關注添加客服面試

進Python社羣▲算法

掃碼關注添加客服c#

進Java社羣微信

做者丨labuladong
數據結構

來源丨labuladong編輯器

讀完本文,你能夠去力扣解決以下問題:
分佈式

20.有效的括號(Easy

921.使括號有效的最小插入(Medium

1541.平衡括號串的最少插入(Medium

判斷合法括號串

對括號的合法性判斷屢次在筆試中出現,現實中也很常見,好比說咱們寫的代碼,編輯器會檢查括號是否正確閉合。並且咱們的代碼可能會包含三種括號[](){},判斷起來有一點難度。

來看一看力扣第 20 題「有效的括號」,輸入一個字符串,其中包含[](){}六種括號,請你判斷這個字符串組成的括號是否合法。

舉幾個例子:

Input: "()[]{}"
Output: true

Input: "([)]"
Output: false

Input: "{[]}"
Output: true

解決這個問題以前,咱們先下降難度,思考一下,果只有一種括號(),應該如何判斷字符串組成的括號是否合法呢?

假設字符串中只有圓括號,若是想讓括號字符串合法,那麼必須作到:

每一個右括號)的左邊必須有一個左括號(和它匹配

好比說字符串()))((中,中間的兩個右括號左邊就沒有左括號匹配,因此這個括號組合是不合法的。

那麼根據這個思路,咱們能夠寫出算法:

bool isValid(string str) {
    // 待匹配的左括號數量
    int left = 0;
    for (int i = 0; i < str.size(); i++) {
        if (s[i] == '(') {
            left++;
        } else {
            // 遇到右括號
            left--;
        }

        // 右括號太多
        if (left == -1)
            return false;
    }
    // 是否全部的左括號都被匹配了
    return left == 0;
}

若是隻有圓括號,這樣就能正確判斷合法性。對於三種括號的狀況,我一開始想模仿這個思路,定義三個變量left1left2left3分別處理每種括號,雖然要多寫很多 if else 分支,可是彷佛能夠解決問題。

但實際上直接照搬這種思路是不行的,好比說只有一個括號的狀況下(())是合法的,可是多種括號的狀況下,[(])顯然是不合法的。

僅僅記錄每種左括號出現的次數已經不能作出正確判斷了,咱們要加大存儲的信息量,能夠利用棧來模仿相似的思路。棧是一種先進後出的數據結構,處理括號問題的時候尤爲有用。

咱們這道題就用一個名爲left的棧代替以前思路中的left變量,遇到左括號就入棧,遇到右括號就去棧中尋找最近的左括號,看是否匹配

bool isValid(string str) {
    stack<char> left;
    for (char c : str) {
        if (c == '(' || c == '{' || c == '[')
            left.push(c);
        else { // 字符 c 是右括號
            if (!left.empty() && leftOf(c) == left.top())
                left.pop();
            else
                // 和最近的左括號不匹配
                return false;
        }
    }
    // 是否全部的左括號都被匹配了
    return left.empty();
}

char leftOf(char c) {
    if (c == '}') return '{';
    if (c == ')') return '(';
    return '[';
}

接下來說另外兩個常見的問題,如何經過最小的插入次數將括號變成合法的?

平衡括號串(一)

先來個簡單的,力扣第 921 題「使括號有效的最少添加」:

給你輸入一個字符串s,你能夠在其中的任意位置插入左括號(或者右括號),請問你最少須要幾回插入才能使得s變成一個合法的括號串?

好比說輸入s = "())(",算法應該返回 2,由於咱們至少須要插入兩次把s變成"(())()",這樣每一個左括號都有一個右括號匹配,s是一個合法的括號串。

這其實和前文的判斷括號合法性很是相似,咱們直接看代碼:

int minAddToMakeValid(string s) {
    // res 記錄插入次數
    int res = 0;
    // need 變量記錄右括號的需求量
    int need = 0;

    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') {
            // 對右括號的需求 + 1
            need++;
        }

        if (s[i] == ')') {
            // 對右括號的需求 - 1
            need--;

            if (need == -1) {
                need = 0;
                // 需插入一個左括號
                res++;
            }
        }
    }

    return res + need;
}

這段代碼就是最終解法,核心思路是以左括號爲基準,經過維護對右括號的需求數need,來計算最小的插入次數。須要注意兩個地方:

一、當need == -1的時候意味着什麼

由於只有遇到右括號)的時候纔會need--need == -1意味着右括號太多了,因此須要插入左括號。

好比說s = "))"這種狀況,須要插入 2 個左括號,使得s變成"()()",纔是一個合法括號串。

二、算法爲何返回res + need

由於res記錄的左括號的插入次數,need記錄了右括號的需求,當 for 循環結束後,若need不爲 0,那麼就意味着右括號還不夠,須要插入。

好比說s = "))("這種狀況,插入 2 個左括號以後,還要再插入 1 個右括號,使得s變成"()()()",纔是一個合法括號串。

以上就是這道題的思路,接下來咱們看一道進階題目,若是左右括號不是 1:1 配對,會出現什麼問題呢?

平衡括號串(二)

這是力扣第 1541 題「平衡括號字符串的最少插入次數」:

如今假設 1 個左括號須要匹配 2 個右括號才叫作合法的括號組合,那麼給你輸入一個括號串s,請問你如何計算使得s合法的最小插入次數呢?

核心思路仍是和剛纔同樣,經過一個need變量記錄對右括號的需求數,根據need的變化來判斷是否須要插入

第一步,咱們按照剛纔的思路正確維護need變量:

int minInsertions(string s) {
    // need 記錄需右括號的需求量
    int res = 0, need = 0;

    for (int i = 0; i < s.size(); i++) {
        // 一個左括號對應兩個右括號
        if (s[i] == '(') {
            need += 2;
        }

        if (s[i] == ')') {
            need--;
        }
    }

    return res + need;
}

如今想想,當need爲何值的時候,咱們能夠肯定須要進行插入?

首先,相似第一題,當need == -1時,意味着咱們遇到一個多餘的右括號,顯然須要插入一個左括號

好比說當s = ")",咱們確定須要插入一個左括號讓s = "()",可是因爲一個左括號須要兩個右括號,因此對右括號的需求量變爲 1:

if (s[i] == ')') {
    need--;
    // 說明右括號太多了
    if (need == -1) {
        // 須要插入一個左括號
        res++;
        // 同時,對右括號的需求變爲 1
        need = 1;
    }
}

另外,當遇到左括號時,若對右括號的需求量爲奇數,須要插入 1 個右括號。由於一個左括號須要兩個右括號嘛,右括號的需求必須是偶數,這一點也是本題的難點。

因此遇到左括號時要作以下判斷:

if (s[i] == '(') {
    need += 2;
    if (need % 2 == 1) {
        // 插入一個右括號
        res++;
        // 對右括號的需求減一
        need--;
    }
}

綜上,咱們能夠寫出正確的代碼:

int minInsertions(string s) {
    int res = 0, need = 0;

    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') {
            need += 2;
            if (need % 2 == 1) {
                res++;
                need--;
            }
        }

        if (s[i] == ')') {
            need--;
            if (need == -1) {
                res++;
                need = 1;
            }
        }
    }

    return res + need;
}

綜上,三道括號相關的問題就解決了,其實咱們前文 合法括號生成算法 也是括號相關的問題,可是使用的回溯算法技巧,和本文的幾道題差異仍是蠻大的,有興趣的讀者能夠去看看。

程序員專欄 掃碼關注填加客服 長按識別下方二維碼進羣

近期精彩內容推薦:  

 剛剛用鴻蒙跑了個「hello world」!

 裸辭1天 vs 裸辭10天 vs 裸辭一個月

 分佈式id生成策略,我和麪試官扯了一個半小時

 Python中淺拷貝與深拷貝的騷操做

在看點這裏好文分享給更多人↓↓

相關文章
相關標籤/搜索