LeetCode算法题0:分发糖果【贪心算法】
文章目錄
- 前言
- 一、題目
- 二、思路詳解
- 三、搞點實際點兒的(C++實現)
- 1.略顯粗糙的代碼實現
- 2.稍顯精致的代碼實現
- 3.最終的代碼實現
- 4.提交結果
- 總結
前言
??????本文記錄自己在LeetCode上關于一道算法題的解題過程,這道題花費了不少時間才通過。感覺在自己的這種解題風格道路上走遠了!但是代碼提交的結果還是比較滿意的。 哈哈,過程比較曲折,下面為大家一一道來:
一、題目
??????老師想給孩子們分發糖果,有 N 個孩子站成了一條直線,老師會根據每個孩子的表現,預先給他們評分。
??????你需要按照以下要求,幫助老師給這些孩子分發糖果:
??????要求1:每個孩子至少分配到 1 個糖果。
??????要求2:評分更高的孩子必須比他兩側的鄰位孩子獲得更多的糖果。
??????那么這樣下來,老師至少需要準備多少顆糖果呢?
示例1:
輸入:[1,0,2]
輸出:5
解釋:你可以分別給這三個孩子分發 2、1、2 顆糖果。
示例2:
輸入:[1,2,2]
輸出:4
解釋:你可以分別給這三個孩子分發 1、2、1 顆糖果。第三個孩子只得到 1 顆糖果,這已滿足上述兩個條件。
該題所在網址:https://leetcode-cn.com/problems/candy/
二、思路詳解
??????瞧一瞧,看一看,大家品一下這個老師有多摳門~
??????首先:給每個孩子至少給一個糖果,這個簡單。要求 2 稍微難理解一點,參考下面這個示例來理解,概括為:如果一個孩子兩側,只要存在一個比他評分低的孩子,那么他的糖果數就要比這個低評分孩子的糖果要多。
這里給出一個示例:所有孩子的評分:{ 1,0,2,3,4,3,2,2 } 它對應的糖果數:{ 2,1,2,3,4,2,1,1 }。后面會對這個示例進一步分析。
??????最開始的思路是這樣的:按照平移的思想來設計,即;給起始第一個孩子1顆糖果,依次往后判斷,如果比它大則糖果加1,比它小則減1,碰到相等的情況則另做分析。 最后將整體結果往上平移(可能有負數存在),使得最小糖果數為1即可。 但是后來發現不可行,邏輯判斷太復雜,很難分析。 還有最重要的一點是,糖果數有時候會有一個突然的下降,比如給出的示例中評分為4和3的孩子,前者給4顆糖果,后者給2顆糖果。
??????所以單純的只從左到右的分析是不充分的,當然可以在這個基礎上再改改,比如補充上從右到左進行分析,不過在這兒不聊這個。聊點清新脫俗的~
??????就糖果數突然有一個下降的現象,我思考了一下原因。從而得到一種解題思路:
??????發現:如果能找到那些極小值點,也就是那些最終只分配 1 顆糖果的孩子(好慘!),因為給這些孩子的糖果數是已經確定的了。以此(這些糖果數)為基礎,再對這些孩子的左右分別進行分析,很容易就可以確定他們的左右孩子應得的糖果數,然后依次迭代計算擴展至所有孩子。 ???將整個評分序列想象成為一個函數,接下來要做的是找到其中的極小值點,給它們分配糖果,再對其余的評分序列繼續找當前的極小值點,分配糖果。直至分配結束。 ???想象一下快速排序的特點,每一次循環會確定一個最終排序結束時元素的位置。而目前的這個思路和它類似:每一次會確定若干個極小值點的糖果數。
??????所以具體的算法思路如下:
??????如果一個孩子的左右兩側評分都大于等于它,那么它就是一個極小值。
??????(1) 找到所有極小值點,將它們對應的糖果數量置為1
??????(2) 排除上述已經分配了糖果的孩子,在其余孩子中再次找極小值點,將它們對應的糖果數量置為2.(每一輪的極小值點分配的糖果數要比之前的多1個)
??????(3) 不斷重復步驟2. 直至給所有的孩子都分配了糖果。
??????對上面的示例進行分析:給定孩子的評分數組為 { 1,0,2,3,4,3,2,2 }
??????第一輪之后糖果數組為:_ 1 _ _ _ _ 1 1 ,其中第二個數 0 ,倒數第二個數 2 和最后一個數 2 為極小值。
??????第二輪在第一輪的基礎之上,得到兩個孩子的評分區間分別為:{ 1 } 和 { 2,3,4,3} 。對這兩個數組找它們的極小值,給這些極小值點的糖果數為2,得到第二輪之后糖果數組為:2 1 2 _ _ 2 1 1 ,其中區間 1 中唯一的一個數 1 是極小值,區間 2 中的第一個數 2 和最后一個數 3 為極小值。
??????第三輪在第二輪的基礎之上,得到一個孩子的評分區間分別為:{ 3,4} 。對這個數組找極小值,給這些極小值點的糖果數為3,得到第三輪之后糖果數組為:2 1 2 3 _ 2 1 1 ,其中第一個數 3 為最小值。
??????第四輪在第三輪的基礎之上,得到一個孩子的評分區間分別為:{ 4} 。對這個數組找極小值,給這些極小值點的糖果數為4,得到第四輪之后糖果數組為:2 1 2 3 4 2 1 1 ,這個唯一的數為極小值。
??????糖果數:{ 2,1,2,3,4,2,1,1 }即為最終結果。
三、搞點實際點兒的(C++實現)
1.略顯粗糙的代碼實現
??????說明:下面的代碼有助于對整體思路的把控,并且我在代碼中已經給出了詳細的注釋。缺點是太繁瑣,提交的時候會報超時,我稍后會在此之上做一些優化。
??????代碼如下:
??????需要指出一點,也是之前困擾了我比較久的一個問題。上面這個代碼,在while(count<N)循環中,有一行代碼我之前是寫成這樣的:
...while (flag[i] == 0 && i < N)++i;...??????然后呢,它在VS2017中是可以編譯并運行的,但是在LeetCode中會報錯:“AddressSanitizer: heap-buffer-overflow on address…” ,個人猜測,在VS中對 && 應該是有做過優化的,而在LeetCode中是嚴格按照從左到右來判斷的,所以會出現數組訪問越界的現象。 解決方法:把 && 的左右互換一下就行。先判斷 i<N 就好了。
2.稍顯精致的代碼實現
??????上面這個代碼因為在提交時會報超時,所以需要進行邏輯上的刪減,去掉多余的部分。這次的代碼采用遞歸來實現,我依然會給出詳細的注釋。
代碼如下:
3.最終的代碼實現
??????2 中的代碼解決了 1 中存在的問題,但是它有一個缺點。
??????當測試用例為{20000,19999,19998,19997,…,4,3,2,1}時,這樣的逆序數組時,它的時間復雜度會急劇增大,是 2 中時間復雜度的 N 倍。所以,只好在 2 的基礎上加上了關于逆序的判斷;并且考慮到算法在完全升序排列的樣本上時間復雜度會增大,趨于O(N2),所以在加上關于升序的判斷,這點可以參考快速排序的缺點。
??????整體上代碼的時間復雜度為O(lnN),空間復雜度為O(lnN),性能都還可以。得到最終的代碼如下:
4.提交結果
??????以 3 中的代碼進行提交,結果如圖:
總結
??????最終的代碼實現整體上采用了遞歸的方法,也包含到了貪心和分治的思想。總算是按照自己的思路寫完了,挺費勁的,道路是曲折的,但是結局是美好的~
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的LeetCode算法题0:分发糖果【贪心算法】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中的Native方法
- 下一篇: 静态程序分析chapter1 - 概述和