刷爆 LeetCode 周賽 339,貪心 / 排序 / 拓?fù)渑判?/ 平衡二叉樹

大家好咆畏,我是小彭南捂。

上周末是 LeetCode 第 339 場周賽,你參加了嗎旧找?這場周賽覆蓋的知識點(diǎn)比較少溺健,前三題很簡單,第四題上難度钮蛛。


周賽大綱

  1. 最長平衡子字符串(Easy)
  • 模擬:O(n)
  1. 轉(zhuǎn)換二維數(shù)組(Medium)
  • 貪心:O(n)
  1. 老鼠和奶酪(Medium)
  • 排序 + 貪心:O(nlgn)
  1. 最少翻轉(zhuǎn)操作數(shù)(Hard)
  • 題解一:拓?fù)渑判?· 超出時(shí)間限制 O(nk)
  • 題解二:BFS + 平衡二叉樹 O(nlgn)

2609. 最長平衡子字符串(Easy)

題目地址

https://leetcode.cn/problems/find-the-longest-balanced-substring-of-a-binary-string/

題目描述

給你一個(gè)僅由 01 組成的二進(jìn)制字符串 s 鞭缭。

如果子字符串中 所有的 0 都在 1 之前 且其中 0 的數(shù)量等于 1 的數(shù)量,則認(rèn)為 s 的這個(gè)子字符串是平衡子字符串魏颓。請注意岭辣,空子字符串也視作平衡子字符串。

返回 s 中最長的平衡子字符串長度甸饱。

子字符串是字符串中的一個(gè)連續(xù)字符序列沦童。

題解(模擬)

簡單模擬題。

維護(hù)連續(xù) 0 的計(jì)數(shù) cnt0 和連續(xù) 1 的計(jì)數(shù) cnt1叹话,并在 cnt0 == cnt1 時(shí)更新最長平衡子串長度為 2 * cnt1偷遗。另外,在每段 0 的起始位置重新計(jì)數(shù)渣刷。

class Solution {
    fun findTheLongestBalancedSubstring(s: String): Int {
        var index = 0
        var cnt0 = 0
        var cnt1 = 0
        var ret = 0
        while (index < s.length) {
            if (s[index] == '0') {
                // 每段 0 的起始位置清零
                if (index > 0 && s[index - 1] == '1') {
                    cnt0 = 0
                    cnt1 = 0
                }
                cnt0++
            } else {
                cnt1++
            }
            if (cnt1 <= cnt0) ret = Math.max(ret, cnt1 * 2)
            index++
        }
        return ret
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(n) 其中 nnums 數(shù)組的長度鹦肿;
  • 空間復(fù)雜度:O(1) 僅使用常數(shù)級別變量。

2610. 轉(zhuǎn)換二維數(shù)組(Medium)

題目地址

https://leetcode.cn/problems/convert-an-array-into-a-2d-array-with-conditions/

題目描述

給你一個(gè)整數(shù)數(shù)組 nums 辅柴。請你創(chuàng)建一個(gè)滿足以下條件的二維數(shù)組:

  • 二維數(shù)組應(yīng)該 包含數(shù)組 nums 中的元素箩溃。
  • 二維數(shù)組中的每一行都包含 不同 的整數(shù)。
  • 二維數(shù)組的行數(shù)應(yīng)盡可能 碌嘀。

返回結(jié)果數(shù)組涣旨。如果存在多種答案,則返回其中任何一種股冗。

請注意霹陡,二維數(shù)組的每一行上可以存在不同數(shù)量的元素。

題解(貪心)

貪心思路:首先計(jì)算每個(gè)元素的出現(xiàn)次數(shù),為了避免同一行的重復(fù)烹棉,將重復(fù)元素從上到下排列到不同行中攒霹。

優(yōu)化:可以在一次遍歷中完成,在出現(xiàn)更大出現(xiàn)次數(shù)時(shí)增加一行浆洗,在更新元素技術(shù) cnt 后插入到第 cnt - 1 行催束。

class Solution {
    fun findMatrix(nums: IntArray): List<List<Int>> {
        val cnts = IntArray(201)
        val ret = LinkedList<LinkedList<Int>>()
        var maxCnt = 0
        // 計(jì)數(shù)
        for (num in nums) {
            // 累加
            val curCnt = ++cnts[num]
            // 創(chuàng)建新行
            if (curCnt > maxCnt) {
                maxCnt = curCnt
                ret.add(LinkedList<Int>())
            }
            // 分布
            ret[curCnt - 1].add(num)
        }
        return ret
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(n) 其中 nnums 數(shù)組的長度,每個(gè)元素訪問一次伏社;
  • 空間復(fù)雜度:O(U) 計(jì)數(shù)數(shù)組空間抠刺。

2611. 老鼠和奶酪(Medium)

題目地址

https://leetcode.cn/problems/mice-and-cheese/

題目描述

有兩只老鼠和 n 塊不同類型的奶酪,每塊奶酪都只能被其中一只老鼠吃掉摘昌。

下標(biāo)為 i 處的奶酪被吃掉的得分為:

  • 如果第一只老鼠吃掉速妖,則得分為 reward1[i]
  • 如果第二只老鼠吃掉聪黎,則得分為 reward2[i] 排作。

給你一個(gè)正整數(shù)數(shù)組 reward1 诽嘉,一個(gè)正整數(shù)數(shù)組 reward2 枕稀,和一個(gè)非負(fù)整數(shù) k 辨萍。

請你返回第一只老鼠恰好吃掉 k 塊奶酪的情況下烘跺,最大 得分為多少湘纵。

題解(排序 + 貪心)

容易理解:為了使最終得分最大,應(yīng)該讓每只老鼠吃到盡可能大的奶酪滤淳。

由于兩只老鼠吃的奶酪是互斥關(guān)系梧喷,因此我們可以先假設(shè)所有奶酪被第一只老鼠食得,然后再挑選 n - k 個(gè)奶酪還給第二只老鼠脖咐。

那么铺敌,對于每個(gè)位置 i,將奶酪從第一只老鼠還給第二只老鼠存在差值 diff = reward2[i] - reward1[i]屁擅,表示得分的差值為 diff偿凭。差值為正得分變大,差值為負(fù)得分降低派歌,顯然降低越少越好弯囊。

因此,我們的算法是對 diff 排序胶果,將得分降低越大的位置保留給第一只老鼠匾嘱,其他還給第二只老鼠。

class Solution {
    fun miceAndCheese(reward1: IntArray, reward2: IntArray, k: Int): Int {
        // 貪心:優(yōu)先選擇差值最大的位置
        val n = reward1.size
        var ret = 0
        val indexs = Array(n) { it }
        // 升序
        Arrays.sort(indexs) { i1, i2 ->
            (reward2[i1] - reward1[i1]) - (reward2[i2] - reward1[i2])
        }
        for (i in 0 until n) {
            ret += if (i < k) {
                reward1[indexs[i]]
            } else {
                reward2[indexs[i]]
            }
        }
        return ret
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(nlgn + n) 其中 nnums 數(shù)組的長度早抠;
  • 空間復(fù)雜度:O(n + lgn) 索引數(shù)組和遞歸楒樱空間。

2612. 最少翻轉(zhuǎn)操作數(shù)(Hard)

題目地址

https://leetcode.cn/problems/minimum-reverse-operations/

題目描述

給你一個(gè)整數(shù) n 和一個(gè)在范圍 [0, n - 1] 以內(nèi)的整數(shù) p ,它們表示一個(gè)長度為 n 且下標(biāo)從 0 開始的數(shù)組 arr 悬垃,數(shù)組中除了下標(biāo)為 p 處是 1 以外游昼,其他所有數(shù)都是 0

同時(shí)給你一個(gè)整數(shù)數(shù)組 banned 尝蠕,它包含數(shù)組中的一些位置酱床。banned 中第 i 個(gè)位置表示 arr[banned[i]] = 0 ,題目保證 banned[i] != p 趟佃。

你可以對 arr 進(jìn)行 若干次 操作扇谣。一次操作中,你選擇大小為 k 的一個(gè) 子數(shù)組 闲昭,并將它 翻轉(zhuǎn) 罐寨。在任何一次翻轉(zhuǎn)操作后,你都需要確保 arr 中唯一的 1 不會到達(dá)任何 banned 中的位置序矩。換句話說鸯绿,arr[banned[i]] 始終 保持 0

請你返回一個(gè)數(shù)組 ans 簸淀,對于 **[0, n - 1] 之間的任意下標(biāo) i 瓶蝴,ans[i] 是將 1 放到位置 i 處的 最少 翻轉(zhuǎn)操作次數(shù),如果無法放到位置 i 處租幕,此數(shù)為 -1 舷手。

  • 子數(shù)組 指的是一個(gè)數(shù)組里一段連續(xù) 非空 的元素序列。
  • 對于所有的 i 劲绪,ans[i] 相互之間獨(dú)立計(jì)算男窟。
  • 將一個(gè)數(shù)組中的元素 翻轉(zhuǎn) 指的是將數(shù)組中的值變成 相反順序

題解一(拓?fù)渑判?· 超出時(shí)間限制)

分析 1:對于翻轉(zhuǎn)窗口 [L, R] 中的位置 i贾富,翻轉(zhuǎn)后的下標(biāo)為 \frac{L+R}{2} + (\frac{L+R}{2} - i) = L + R - i

分析 2:首先位置 p 的翻轉(zhuǎn)次數(shù)恒等于 0歉眷,而 banned 數(shù)組表示的位置翻轉(zhuǎn)次數(shù)恒等于 -1。

分析 3:當(dāng)位置 i 位于翻轉(zhuǎn)窗口的左半部分時(shí)颤枪,將翻轉(zhuǎn)到更大位置汗捡;當(dāng)位置 i 位于翻轉(zhuǎn)窗口的右半部分時(shí),將翻轉(zhuǎn)到更小位置畏纲;

分析 4:現(xiàn)在我們需要分析位置 i (初始 i 為 0 )可以翻轉(zhuǎn)到的位置:

  • 情況 1:如果將 i 作為翻轉(zhuǎn)窗口的左右邊界扇住,則有:
    • 位于左邊界時(shí),翻轉(zhuǎn)后的下標(biāo)為 i + k - 1霍骄;
    • 位于有邊界時(shí)台囱,翻轉(zhuǎn)后的下標(biāo)為 i - k + 1
  • 情況 2:如果將 i 放在翻轉(zhuǎn)窗口內(nèi)部读整,則所有翻轉(zhuǎn)后的下標(biāo)正好構(gòu)成差值為 2 的等差數(shù)列簿训。

因此,i 可以翻轉(zhuǎn)的區(qū)間為 [i - k + 1, i + k - 1] 中間隔 2 的位置(排除 banned 數(shù)組),或者理解為奇偶性相同的下標(biāo)强品。

分析 5:由于翻轉(zhuǎn)窗口有位置限制膘侮,會限制翻轉(zhuǎn):

  • 窗口左邊界在位置 0 時(shí),且 i 位于翻轉(zhuǎn)窗口的右半部分時(shí)(準(zhǔn)備向左翻)的榛,則翻轉(zhuǎn)后的位置是 0 + (k - 1) - i = k - 1 - i琼了。由于窗口無法繼續(xù)左移,所以小于 k - i - 1 的位置都不可達(dá)夫晌;
  • 同理雕薪,窗口右邊界位于 n - 1 時(shí),且 i 位于翻轉(zhuǎn)窗口的左邊部分時(shí)(準(zhǔn)備向右翻)晓淀,則翻轉(zhuǎn)后的位置是 (n - k) + (n - 1) - i = 2n - k - i - 1所袁。由于窗口無法繼續(xù)右移,所以大于 2n - k - i - 1 的位置都不可達(dá)凶掰。

綜上燥爷,可得翻轉(zhuǎn)后區(qū)間為 [max(i - k + 1, k - i - 1), min(i + k - 1, 2n - k - i - 1)] 中與 i 奇偶性相同的位置。

至此懦窘,容易發(fā)現(xiàn)問題可以用拓?fù)渑判颍˙FS 寫法)解決:初始時(shí)將 p 位置入隊(duì)前翎,隨后每一輪的翻轉(zhuǎn)次數(shù) + 1,并將該位置入隊(duì)畅涂。

class Solution {
    fun minReverseOperations(n: Int, p: Int, banned: IntArray, k: Int): IntArray {
        val ret = IntArray(n) { -1 }
        // 初始位
        ret[p] = 0
        // 禁止位
        val bannedSet = banned.toHashSet()
        // BFS(最小跳轉(zhuǎn)索引)
        val queue = LinkedList<Int>()
        queue.offer(p)
        while (!queue.isEmpty()) {
            val i = queue.poll()!!
            val min = Math.max(i - k + 1, k - i - 1)
            val max = Math.min(i + k - 1, 2 * n - k - i - 1)
            val curStep = ret[i] + 1
            for (j in min..max step 2) {
                // 不可達(dá)
                if (bannedSet.contains(j)) continue
                // 已訪問
                if (ret[j] != -1) continue
                // 可達(dá)
                ret[j] = curStep
                // 入隊(duì)
                queue.offer(j)
            }
        }
        return ret
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(n·k) 每個(gè)元素最多訪問 1 次港华,且每輪最多需要訪問 k 個(gè)元素。
  • 空間復(fù)雜度:O(n) 隊(duì)列的長度最大為 n毅戈。

題解二(BFS + 平衡二叉樹)

在題解一中苹丸,當(dāng) k 比較大時(shí)每輪 BFS 中會重復(fù)判斷已經(jīng)被標(biāo)記過的位置,如何避免呢苇经?我們可以提前將所有下標(biāo)加入到散列表中,在每次標(biāo)記后將下標(biāo)從散列表移除宦言,這樣能避免重復(fù)訪問已經(jīng)標(biāo)記過的位置扇单。

其次,由于每輪中需要標(biāo)記的區(qū)間位于 [min, max]奠旺,那么我們可以將散列表升級為基于平衡二叉樹的 TreeSet蜘澜,以便在 O(lgn) 時(shí)間內(nèi)找到區(qū)間中的元素。具體方式是尋找樹中大于等于 min 的最小元素(且小于等于 max)响疚,將其標(biāo)記和移除鄙信。

最后,由于偶數(shù)下標(biāo)和奇數(shù)下標(biāo)是分開的忿晕,所以需要建立兩個(gè)平衡二叉樹装诡。

class Solution {
    fun minReverseOperations(n: Int, p: Int, banned: IntArray, k: Int): IntArray {
        val ret = IntArray(n) { -1 }
        // 初始位
        ret[p] = 0
        // 禁止位
        val bannedSet = banned.toHashSet()
        // 平衡二叉樹
        val sets = Array(2) { TreeSet<Int>() }
        for (i in 0 until n) {
            if (i != p && !bannedSet.contains(i)) sets[i % 2].add(i)
        }
        // BFS(最小跳轉(zhuǎn)索引)
        val queue = LinkedList<Int>()
        queue.offer(p)
        while (!queue.isEmpty()) {
            val i = queue.poll()!!
            val min = Math.max(i - k + 1, k - i - 1)
            val max = Math.min(i + k - 1, 2 * n - k - i - 1)
            val curStep = ret[i] + 1
            // 根據(jù)左端點(diǎn)確定奇偶性(右端點(diǎn)也行)
            val set = sets[min % 2]
            // 枚舉平衡樹中的 [min,max] 區(qū)間
            while (true) {
                val index = set.ceiling(min) ?: break // 大于等于 min 的最小鍵值
                if (index > max) break
                // 標(biāo)記并刪除
                set.remove(index)
                ret[index] = curStep
                // 入隊(duì)
                queue.offer(index)
            }
        }
        return ret
    }
}

復(fù)雜度分析:

  • 時(shí)間復(fù)雜度:O(nlgn + nlgn) 建平衡樹為 O(nlgn),BFS 中每個(gè)元素最多刪除一次,每輪需要 O(lgn) 時(shí)間找到左邊界鸦采,整體是 O(nlgn)宾巍;
  • 空間復(fù)雜度:O(n) 平衡二叉樹空間。

<span style="display:block;text-align:center;color:black;font-weight:bold;font-size:16px;">點(diǎn)擊上方按鈕關(guān)注</span>
<span style="display:block;text-align:center;color:black;font-weight:bold;font-size:16px;">每周持續(xù)原創(chuàng)更新</span>
<span style="display:block;text-align:center;color:black;font-weight:bold;font-size:16px;">與你一起深度思考</span>




<span style="display:block;text-align:center;color:black;font-weight:bold;font-size:18px;">The End</span>

<span style="display:block;text-align:center;color:black;font-size:12px;">—— 我 們 下 次 見 ——</span>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渔伯,一起剝皮案震驚了整個(gè)濱河市顶霞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锣吼,老刑警劉巖选浑,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異玄叠,居然都是意外死亡鲜侥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門诸典,熙熙樓的掌柜王于貴愁眉苦臉地迎上來描函,“玉大人,你說我怎么就攤上這事狐粱∫ㄔⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵肌蜻,是天一觀的道長互墓。 經(jīng)常有香客問我,道長蒋搜,這世上最難降的妖魔是什么篡撵? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮豆挽,結(jié)果婚禮上育谬,老公的妹妹穿的比我還像新娘。我一直安慰自己帮哈,他們只是感情好膛檀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娘侍,像睡著了一般咖刃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上憾筏,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天嚎杨,我揣著相機(jī)與錄音,去河邊找鬼氧腰。 笑死枫浙,一個(gè)胖子當(dāng)著我的面吹牛刨肃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播自脯,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼之景,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膏潮?” 一聲冷哼從身側(cè)響起锻狗,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焕参,沒想到半個(gè)月后轻纪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡叠纷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年刻帚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩嚣。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡崇众,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出航厚,到底是詐尸還是另有隱情顷歌,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布幔睬,位于F島的核電站眯漩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏麻顶。R本人自食惡果不足惜赦抖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辅肾。 院中可真熱鬧队萤,春花似錦、人聲如沸宛瞄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽份汗。三九已至,卻和暖如春蝴簇,著一層夾襖步出監(jiān)牢的瞬間杯活,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工熬词, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旁钧,地道東北人吸重。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像歪今,于是被迫代替她去往敵國和親嚎幸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容