這幾道經典例題幫你輕鬆搞透貪心算法

2021-02-20 五分鐘學算法

點擊上方「五分鐘學算法」,選擇「星標」公眾號

重磅乾貨,第一時間送達

貪心算法概念敘述

運用貪心算法求解問題時,會將問題分為若干個子問題,可以將其想像成俄羅斯套娃,利用貪心的原則從內向外依次求出當前子問題的最優解,也就是該算法不會直接從整體考慮問題,而是想要達到局部最優。只有內部的子問題求得最優解,才能繼續解決包含該子問題的下一個子問題,所以前一個子問題的最優解會是下一個子問題最優解的一部分,重複這個操作直到堆疊出該問題的最優解。

貪心算法最關鍵的部分在於貪心策略的選擇,貪心選擇的意思是對於所求問題的整體最優解可以通過一系列的局部最優選擇求得。而必須注意的是,貪心選擇必須具備無後效性,也就是某個狀態不會影響之前求得的局部最優解。

運動貪心算法解決相應問題時會比較簡單和高效,省去了尋找全局最優解很多不必要的窮舉操作,由於貪心算法問題並沒有固定的貪心策略,所以唯一的難點就是找到帶求解問題的貪心策略,但畢竟熟能生巧嘛,算法的基本思想總是固定不變的。

貪心算法求解步驟

將問題分解為若干個子問題

找出適合的貪心策略

求解每一個子問題的最優解

將局部最優解堆疊成全局最優解

下面通過利用貪心算法解決四道LeetCode題目,加深一下對貪心算法思想的掌握,其中第一道為easy,其餘三道為medium,會標註出相應的題號。

連結:https://leetcode-cn.com455.分發餅乾

問題描述:假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。但是,每個孩子最多只能給一塊餅乾。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅乾的最小尺寸;並且每塊餅乾 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅乾 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是儘可能滿足越多數量的孩子,並輸出這個最大數值。

注意:

你可以假設胃口值為正。

一個小朋友最多只能擁有一塊餅乾

這道題的思路主要包括兩個點:

儘量先滿足胃口值小的孩子,因為這樣的孩子容易滿足。

進行條件1時,儘可能選用尺寸小的,這樣大尺寸餅乾可以用來滿足胃口值大的孩子。

這道題的貪心思想非常明顯,就是要儘可能地滿足更多的孩子,而胃口值小的孩子是容易滿足的,反之胃口值大的孩子很難滿足,所以在抉擇上儘可能滿足前者、餓著後者。

這個思想可以類比於賽馬,我們假設贏或者平作為滿足條件。如果A的3贏了B的1,那麼剩下兩匹的結果可能就是一平一負或者兩負,那麼此時至多才是1滿足;而如果A的馬和B的馬都按照順序比,則可以達到3平,那麼此時可以達到3滿足。

所以綜上可以得到解題思路,首先需要將胃口值和餅乾尺寸由小至大排序。設定一個計數器child,用來記得到滿足的孩子個數,再維護一個餅乾指針cookies。如果餅乾尺寸可以滿足孩子胃口值,即g[child]<=s[cookies],就將child、cookies分別加一(向後移動一位),否則只將cookies向後移動一位。因為孩子的胃口值是由小到大的,若不滿足當前的胃口值更不會滿足之後的。

55. 跳躍遊戲

題目描述:給定一個非負整數數組,你最初位於數組的第一個位置。數組中的每個元素代表你在該位置可以跳躍的最大長度。判斷你是否能夠到達最後一個位置。

首先明確一下解題目標,若要能夠到達最後一個位置,那麼就需要最後一跳的最大距離加上該位置下標一定要大於等於數組長度,即nums[i]+i>=length(nums),而當前元素又一定處於之前元素最遠可以達到範圍之內,這樣層層嵌套不就是貪心算法思想中的子問題的形式嘛。

我們要從數組的第一個元素開始遍歷,並且維護一個最遠可以到達的位置(max_i),當遍歷到數組中的某一個位置i時,如果i在max_i範圍之內,並且此時最遠可以達到位置大於max_i,那麼就通過i+nums[i]更新max_i,如果在遍歷過程中max_i大於等於數組長度,則代表可以達到最後一個位置,反之不能。要注意的是,max_i既不是數組下標也不是數組中某個元素,而是二者的加和。

拿上面兩個示例為例:

435.無重疊區間

題目描述:給定一個區間的集合,找到需要移除區間的最小數量,使剩餘區間互不重疊。

注意:

可以認為區間的終點總是大於它的起點。

區間 [1,2] 和 [2,3] 的邊界相互「接觸」,但沒有相互重疊。

本題的要求是「找到需要移出區間的最小數量」,換句話說就是要更多地保留集合中的區間,那麼對於有重疊的區間,就應該儘可能刪去跨度較大的區間。

這裡我們根據區間的終點進行貪心選擇,不是說起點不行,而是終點更好,那原因呢?因為如果每次選擇的區間結尾越小,留給後面區間的空間自然就變多了,那麼後面能留下的區間數量也就越多。用一句話概括就是每次都選擇終點最小的,因為這一定是最優解的一部分,這不就是正是貪心思想的應用嘛。

解這道題時需要先將數組按照區間的終點進行排序,然後需要維護一個end指針,它代表當前集合中的最小終點,在遍歷數組時,若當前元素的起點大於前一區間的終點,那麼不重疊區間的計數器加一,更新end指針;反之則不做任何操作,最後區間總數減去不重疊區間即為需要移除區間的最小數量。

時間複雜度:一次排序加一次循環,。

LeetCode中第452題:用最少數量的箭引爆氣球與本題解法十分相似,大家可以類比本題的思路自己練習。

376. 擺動序列

如果連續數字之間的差嚴格地在正數和負數之間交替,則數字序列稱為擺動序列。第一個差(如果存在的話)可能是正數或負數。少於兩個元素的序列也是擺動序列。

例如, [1,7,4,9,2,5] 是一個擺動序列,因為差值 (6,-3,5,-7,3) 是正負交替出現的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是擺動序列,第一個序列是因為它的前兩個差值都是正數,第二個序列是因為它的最後一個差值為零。

給定一個整數序列,返回作為擺動序列的最長子序列的長度。通過從原始序列中刪除一些(也可以不刪除)元素來獲得子序列,剩下的元素保持其原始順序。

示例1和示例3比較特殊,一個是完全擺動序列,另一個是完全升序序列,所以這裡利用比較普通的示例2講解,依據示例2中的數組可以大致繪製出一個元素分布圖,如下:

其中橙色點就構成了一個擺動序列,所以橙色點的個數也是最終要輸出的結果。可以看到[5,10,13,15]是一個連續遞增的子序列,5處於17之後是符合題意的,所以一定將其保留,而對於[10,13,15]三個元素,只有保留15才可以形成擺動序列。

所以對於一段連續遞增的子序列,只有保留這段子序列的首尾元素時,才能形成一個擺動序列,並且這也加大了尾部的後一個元素成為擺動序列的下一個元素的可能性。同理連續遞減的子序列也做如上操作,比如圖中的[15,10,5]。

解決這道題的關鍵就在於如何保留連續連續遞增的子序列首尾元素,結合棧是一個很好的方法,但出棧入棧的條件是什麼呢?我們維護一個狀態值nowstate,他共有"up"和"down"兩種取值,"up"表示該元素大於前一個元素,"dowm"表示該元素小於前一個元素。

從第二個元素開始遍歷數組,因為第一個元素(下標為0)一定處於擺動序列內。若當前元素大於前一個元素且nowstate="up",這就說明連續遞增出現了,就需要從棧移除前一個元素。反之將nowstate更新為"up",因為此時前一個nowstate="down",另一種可能性同理。不論什麼條件下都要做入棧操作,因為這裡只靠條件過濾不符合的元素。

總結

從上面幾道題中不難看出只要依據題意找出相應的貪心策略,解題就十分容易,並且代碼也不複雜,但貪心選擇的方法並不唯一,主要還是靠對算法的理解和解題的經驗。貪心算法和動態規劃是原理有些相似的兩種算法,同一問題利用不同算法解題的思路、難易程度各不相同,不要相互混淆。

---

愛分享,愛開源,GitHubPorn 現已正式上線!專注於為大家分享優質的計算機學習資源與開發者工具。

如果今天的推薦符合你的口味,請在文章點讚,以表示對我的支持,你們的點讚和轉發關注,是我持續更新的動力^_^


相關焦點

  • 用經典例題輕鬆幫你搞定貪心算法
    運動貪心算法解決相應問題時會比較簡單和高效,省去了尋找全局最優解很多不必要的窮舉操作,由於貪心算法問題並沒有固定的貪心策略,所以唯一的難點就是找到帶求解問題的貪心策略,但畢竟熟能生巧嘛,算法的基本思想總是固定不變的。
  • 每日一道貪心算法
    貪心算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無後效性,即某個狀態以前的過程不會影響以後的狀態,只與當前狀態有關。物品 A B C D E F G 重量 35 30 60 50 40 10 25 價值 10 40 30 50 35 40 30 記得當時學算法的時候,就是這個例子,可以說很經典。
  • (貪心算法系列一)
    ❞周一本周正式開始了貪心算法,在關於貪心算法,你該了解這些!中,我們介紹了什麼是貪心以及貪心的套路。「貪心的本質是選擇每一階段的局部最優,從而達到全局最優。」有沒有啥套路呢?正是因為貪心算法有時候會感覺這是常識,本就應該這麼做!所以大家經常看到網上有人說這是一道貪心題目,有人是這不是。這裡說一下我的依據:「如果找到局部最優,然後推出整體最優,那麼就是貪心」,大家可以參考哈。
  • C語言最常用的貪心算法就這麼被攻克了
    實際上,貪心算法適用的情況很少。一般對一個問題分析是否適用於貪心算法,可以先選擇該問題下的幾個實際數據進行分析,就可以做出判斷。05貪心選擇性質所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,換句話說,當考慮做何種選擇的時候,我們只考慮對當前問題最佳的選擇而不考慮子問題的結果。這是貪心算法可行的第一個基本要素。貪心算法以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問題簡化為規模更小的子問題。
  • 詳解貪心算法
    但是,從許多可以用貪心算法求解的問題中看到這類問題一般具有2個重要的性質:貪心選擇性質和最優子結構性質。1、貪心選擇性質所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。
  • C語言最常用的貪心算法就這麼被攻略了
    實際上,貪心算法適用的情況很少。一般對一個問題分析是否適用於貪心算法,可以先選擇該問題下的幾個實際數據進行分析,就可以做出判斷。05貪心選擇性質所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,換句話說,當考慮做何種選擇的時候,我們只考慮對當前問題最佳的選擇而不考慮子問題的結果。這是貪心算法可行的第一個基本要素。貪心算法以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問題簡化為規模更小的子問題。
  • 真正了解貪心算法,這是一篇精華入門總結...
    貪心算法英語:greedy algorithm,又稱貪婪算法,是一種在每一步選擇中都採取在當前狀態下最好或最優(即最有利)的選擇,從而希望導致結果是最好或最優的算法比如在旅行推銷員問題中,如果旅行員每次都選擇最近的城市,那這就是一種貪心算法。貪心算法在有最優子結構的問題中尤為有效。最優子結構的意思是局部最優解能決定全局最優解。簡單地說,問題能夠分解成子問題來解決,子問題的最優解能遞推到最終問題的最優解。
  • Java 實現貪心算法實例介紹
    2.貪心問題通常一個數學問題會有幾種解法。可以迭代解決,也可以用分治法(快速排序算法)或者動態規劃(背包問題)這樣的高級方法。大多數情況我們都在尋找最優解,但可悲的是並非每次都能得到期望的結果。某些情況下,即使是次優解也很有用。藉助一些策略或者啟發式方法,我們能得到這樣的寶貴結果。
  • 程式設計師算法基礎——貪心算法
    前言貪心是人類自帶的能力,貪心算法是在貪心決策上進行統籌規劃的統稱。
  • 貪心算法:加油站
    可以看一下公眾號左下角的「算法匯總」,「算法匯總」已經把題目順序編排好了,文章順序即刷題順序,這是全網最詳細的刷題順序了,方便錄友們從頭打卡學習,「算法匯總」會持續更新!❞134.貪心算法(方法一)直接從全局進行貪心選擇,情況如下:情況一:如果gas的總和小於cost總和,那麼無論從哪裡出發,一定是跑不了一圈的情況二:rest[i] = gas[i]-cost[i]為一天剩下的油,i從0開始計算累加到最後一站,如果累加沒有出現負數,說明從0出發,油就沒有斷過,那麼0就是起點。
  • 一文詳解貪心算法
    但是,從許多可以用貪心算法求解的問題中看到這類問題一般具有2個重要的性質:貪心選擇性質和最優子結構性質。1、貪心選擇性質所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。
  • 小白帶你學---貪心算法
    貪心算法(Greedy Algorithm) 簡介貪心算法,又名貪婪法,是尋找最優解問題的常用方法,這種方法模式一般將求解過程分成
  • 一文搞懂貪心算法
    顧名思義,貪心算法總是作出在當前看來最好的選擇。也就是說貪心算法並不從整體最優考慮,它所作出的選擇只是在某種意義上的局部最優選擇。當然,希望貪心算法得到的最終結果也是整體最優的。雖然貪心算法不能對所有問題都得到整體最優解,但對許多問題它能產生整體最優解。如單源最短路經問題,最小生成樹問題等。
  • 貪心算法:合併區間
    局部最優可以推出全局最優,找不出反例,試試貪心。那有同學問了,本來不就應該合併最大右邊界麼,這和貪心有啥關係?有時候貪心就是常識!else {                result.push_back(intervals[i]);            }        }        return result;    }};空間複雜度:O(1),不算result數組(返回值所需容器佔的空間)總結對於貪心算法
  • 初中數學:勾股定理典型例題分析!啃透掌握,中考才能輕鬆考高分
    初中數學:勾股定理典型例題分析!啃透掌握,中考才能輕鬆考高分勾股定理是中考數學當中必考的一個知識點,同時也是初中數學運用最多的一個知識點。不同於函數和幾何,勾股定理在綜合解答題當中運用是非常多的,比如四邊形綜合解答題、圓在求線段長度的時候都是需要運用到勾股定理的,因此將這部分知識點啃透掌握是非常重要的。而要想學好它,首先就必須要了解其定義,從課本上的知識點來講,它的定義主要是描述直角三角形三條邊之間的關係:兩條直角邊的平方和等於斜邊的平方,因此勾股定理又有勾三股四玄五之稱。
  • 高中數學數列壓軸題,50道經典例題帶詳解,給你洪荒之力
    因為數列題型比較靈活,出題的思路捉摸不透,很多同學遇見就自動放棄,費勁吧啦的計算,最後也不一定對。其實,數列的題型雖然變化多,但公式和方法無非就是求和公式,錯位相減等。把書上簡短的定義記住,再進行專項刷題,得12分很容易。
  • 依據勾股定理列方程的幾道經典例題
    依據勾股定理列方程的幾道經典例題一、已知直角三角形的一邊和其他兩邊的數量關係,求三邊例1
  • javascript經典算法之最小硬幣找零問題實戰
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言筆者之前也斷斷續續寫過幾篇javascript數據結構和算法的文章,之所以要寫,是因為它們很重要。
  • 來見識一下貪心算法:Jump Game
    >在這個算法題中,我們將看到貪心算法的應用,今天要講的是LeetCode的第55號題目,Jump Game,這個題目是什麼意思呢?給定一個非負數的數組,最初我們在第一個數字的位置上,數組中的每一個數字表示在當前的位置上你最多可以跳多遠,題目要求我們確定能否跳到數組中的最後一個數。比如給定數組[2,3,1,1,4],你的代碼應該返回true,這其中一個可能的跳躍方式是從2跳到3,然後從3跳到4;當然從2開始以一次一步的方式也可以跳到最後。
  • leetcode算法之貪心
    今天來盤一盤 **貪心算法 ** 這類題目使用python刷題分類整理的筆記,請參考: https