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

2021-03-06 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;
}

如果只有圓括號,這樣就能正確判斷合法性。對於三種括號的情況,我一開始想模仿這個思路,定義三個變量left1,left2,left3分別處理每種括號,雖然要多寫不少 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,來計算最小的插入次數。需要注意兩個地方:

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

因為只有遇到右括號)的時候才會need--,need == -1意味著右括號太多了,所以需要插入左括號。

比如說s = "))"這種情況,需要插入 2 個左括號,使得s變成"()()",才是一個合法括號串。

2、算法為什麼返回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;
}

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

相關焦點

  • 幾道和「廣度優先搜索」有關的算法面試題
    1 二叉樹的層次遍歷二叉樹的層次遍歷是最直接了當的使用廣度優先遍歷就能解決的一題。題目解析這道題目很有意思。大部分文章給出的答案都是依託於一個定理:四平方定理。四平方定理講的就是任何一個正整數都可以表示成不超過四個整數的平方之和。也就是說,這道題的答案只有 1,2 ,3,4 這四種可能。
  • 四年級下冊三單元判斷題與計算題解析,這道判斷題孩子次次錯
    215-37+33=215-(37+33)(×)這道題中37和33之間是加號,所以不能-這兩個數的和,沒有簡便算法,計算時,要先算減法再算加法。2.第四個題中換成了減法,依然按乘法分配律計算。這五道題用的是乘法交換律和乘法結合律。第一個題是最簡單的題型,把4和29交換位置後,直接計算。第二個題是4個數字相乘,把合適的兩個數放在一起乘更簡單,因為兩個乘法算式同時算,所以必須加括號。
  • JavaScript中的算法(附10道面試常見算法題解決方法和思路)
    算法挑戰是有用的,因為解決它們的方法不止一種。這為決策的制定和決策的計算提供了可能性。在解決算法問題時,我們應該挑戰自己從多個角度來看待問題的定義,然後權衡各種方法的優缺點。通過足夠的嘗試後,我們甚至可能看到一個普遍的真理:不存在「完美」的解決方案。要真正掌握算法,就必須了解它們與數據結構的關係。
  • 算法題就像搭樂高:手把手帶你拆解 LFU 算法
    上篇文章 算法題就像搭樂高:手把手帶你拆解 LRU 算法 寫了 LRU 緩存淘汰算法的實現方法,本文來寫另一個著名的緩存淘汰算法:LFU 算法。好的,我們希望能夠在 O(1) 的時間內解決這些需求,可以使用基本數據結構來逐個擊破:1、使用一個HashMap存儲key到val的映射,就可以快速計算get(key)。
  • 分治算法詳解:表達式的不同優先級
    下面來看一道具體的算法題。添加括號的所有方式我來借力扣第 241 題講講什麼是分治算法,先看看題目:簡單說,就是給你輸入一個算式,你可以給它隨意加括號,請你窮舉出所有可能的加括號方式,並計算出對應的結果。
  • 經典leetcode算法題分享(字符串)
    解題思路:這道題可以應用於校驗JSON格式的括號是否正確。從題目上可以知道有效的括號是有左括號,也會有相同類型的有括號,並且按照正確的順序閉合。那麼應該採取什麼方法校驗呢?執行用時2ms,還不錯,那麼這題就算pass了!當然除了我說的這種方式之外,題解裡有大佬是使用棧來解決,大家有興趣可以看看。
  • 棧:如何實現有效括號的判斷?
    有效括號,刷過LeetCode的也許對這道題很熟悉。假如現在要你來解這道題,你會想到怎樣的解法了?這就要用到我們今天要講的「棧」這種數據結構。帶著這個問題,我們來學習今天的內容。2.如何理解「棧」?    在算法中,經常會使用的一個思想就是遞歸思想。
  • 從屌絲到高手,三道Python編程題,九種解題算法,看看你屬於哪一類
    今天,小編就帶領大家來進行三道簡單問題的解決,從屌絲解法到進階解法再到高手解法,一步步的帶領大家來提升自己的解題技巧和編程思路。2).屌絲解法對於這道題來說,屌絲解法該如何解決呢?2).進階解法對於進階解法,可以採用遞歸算法來解決該題,例如對於
  • 從經典算法題看時間複雜度
    經常有同學在 LeetCode 的題解中問解法的複雜度是多少。作為一個懶人,我一直在「逃避」這個問題,畢竟這東西聽起來就這麼「複雜」。但本著對題解認真負責的態度(心虛),我想趁此機會做一個總結。下面我將通過一些較為經典的算法題聊一聊幾種常見的時間複雜度。什麼是時間複雜度?
  • 經典算法題:懂二進位(小米筆試題)
    輸入例子:1999 2299輸出例子:7經典算法題:《算法題 :貪心算法(大眾點評筆試題)》《算法題 :所有String2的字母在String1裡是否存在(大眾點評筆試題)》《算法題 :順序表插入新元素(迅雷筆試題)》
  • 【微課堂】三年級數學下冊4.3《帶有小括號的混合運算》視頻講解
    (添上小括號)3.揭題:本節課我們就來學習含有括號的混合運算。二、交流共享1.教學例3。(1)理解題意。出示教材第38頁例3情境圖及問題。提問:要求這個問題,應該先求什麼?(買1個書包後還剩多少元)引導:怎樣列式?
  • 阿里P7工程師耗時兩天整理的292道python大廠面試題,內含解析!
    小編為了大家整理兩天,今天它來了python大廠292道面試題。希望大家能夠希望!292道python大廠面試題學習python的優點是什麼?c、本地應用程式的庫導入在表達式中避免無關的空格在括號或者大括號內在尾隨逗號和後面的右括號之間在逗號,分號或者冒號前面.
  • 刷算法刷到頭禿,才知道:這些題不考!
    開個玩笑,這裡科普一個冷知識:帶人名的算法都不在面試考察範圍內。除了「閱讀全文並背誦」,這種算法考不出什麼東西。大廠面試考察目的,是看你的code寫的好不好,邏輯思維能力怎麼樣,而不是考你的背誦能力。算法面試中最「虛」的部分:不知道的算法那麼多,你根本不知道可能會考到什麼樣的問題。
  • FAANG導師手把手教你速成LeetCode面試題
    LeetCode,它是一個編程實踐網站,主要注重於培養使用者的編程技巧,去解決一些巧妙的算法題。這個網站的神奇之處不僅在於題目本身,還在與其相關的大佬們的故事。馬化騰想必大家都再熟悉不過了吧。前段時間,有一張廣為流傳卻又真假難辨的截圖,把LeetCode與馬化騰「鎖」了。截圖中顯示,馬化騰幾乎每天都會在Leetcode上提交代碼。
  • BAT七年經驗,卻抵不過外企面試的兩道算法題?
    打開APP BAT七年經驗,卻抵不過外企面試的兩道算法題? 面試外企,卻被兩道算法題難住? 近日,一位網友在脈脈上吐槽,稱自己工作經驗豐富,去面試 Hulu(打馬賽克),結果卻是:「我就鬱悶了,在阿里工作五年,去面試 Hulu,上來啥都不問,就兩道算法題我沒有第一時間給出最優解,想了一會兒才做出來,結果就把我掛了,工作那麼多年了,還這樣面試也是令人醉了。」
  • 刷完這70道力扣,你就可以走出新手村啦(文字版)
    之前根據刷題經驗,給大家羅列了70道力扣新手題。後來,我在B站錄製了《手把手帶你刷力扣》的視頻後,又發現了更加適合大家走出新手村的力扣題。這些題目包括了《手把手帶你刷力扣》視頻的全部題目,並且添加了一些對應數據結構和算法的經典題目。
  • 華為面試三道題
    作者:watermelon_learn 來源:CSDNhttps://blog.csdn.net/watermelon_learn/article/details/889021173月27日做了華為筆試,3道題
  • 每天吃透一考點,初一上《去括號與添括號》高頻易錯題,學霸總結
    一.選擇題(共10小題)【點評】本題主要考查去括號和添括號,熟練掌握添括號和去括號法則是解題的關鍵.【點評】本題考查了去括號.能夠熟練掌握去括號的法則是解題的關鍵.【點評】此題主要考查了去括號法則,能夠正確去括號是解題的關鍵.【點評】本題考查了去括號的法則,屬於基礎題,去括號的法則需要我們熟練記憶.
  • 幫你認識「小括號」的神奇,快快收藏吧
    圖片展示1.認識小括號在北師大版三年級上冊數學第一單元第三課時過河中重點講到了小括號,孩子們也是從這裡認識了這個新的符號「( )」,知道了在解決問題的時候,我們在進行混合運算時,不能解決我們所需要的問題時,智慧老人幫我們請來了「救兵」(幫忙改變運算順序)。
  • 經典算法題:猜數(360軟體測試工程師筆試題)
    又假設學生A和B的結論是正確的,則這兩個數字是:()經典算法題:《算法題 :貪心算法(大眾點評筆試題)》《算法題 :所有String2的字母在String1裡是否存在(大眾點評筆試題)》《算法題 :順序表插入新元素(迅雷筆試題)》《算法題 :迅雷2016研發工程師5道筆試題