每日一算:分發糖果

? ? ? ? ?在算法面試中,“分發糖果” 是一道經典的貪心算法應用題,核心考察對 “局部最優推導全局最優” 的理解。本文將從問題分析出發,提供兩種主流解題思路,并附上 C++ 實現代碼,幫助你徹底掌握這道題。

一、問題重述

題目要求

有?n?個孩子站成一排,每個孩子有評分?ratings[i],分發糖果需滿足:

  1. 每個孩子至少得 1 顆糖果;
  2. 相鄰孩子中,評分高的孩子必須獲得更多糖果。
    求最少需要準備的糖果總數。

示例理解

  • 示例 1:ratings = [1,0,2]
    分發方案?[2,1,2],總數?5
    解釋:第二個孩子評分最低(0)得 1 顆,第一個比第二個高(1>0)得 2 顆,第三個比第二個高(2>0)得 2 顆。
  • 示例 2:ratings = [1,2,2]
    分發方案?[1,2,1],總數?4
    解釋:第三個孩子與第二個評分相同(2=2),無需更多糖果,故得 1 顆(滿足 “至少 1 顆” 即可)。

二、解題思路

核心難點

相鄰關系是 “雙向約束”:既要保證 “左→右” 方向評分高的糖果多,也要保證 “右→左” 方向評分高的糖果多。若只單向處理,會導致另一側約束被破壞。

思路 1:兩次遍歷(貪心經典解法)

原理

貪心算法的核心是 “分階段滿足約束”:

  1. 左→右遍歷:保證每個孩子比左邊評分高時,糖果數比左邊多(不考慮右邊);
  2. 右→左遍歷:保證每個孩子比右邊評分高時,糖果數比右邊多(此時需結合左→右的結果,取最大值,避免破壞左側約束)。
步驟拆解
  1. 初始化數組?candies,所有元素為 1(滿足 “至少 1 顆”);
  2. 左→右遍歷(處理 “左邊約束”):
    若?ratings[i] > ratings[i-1],則?candies[i] = candies[i-1] + 1
  3. 右→左遍歷(處理 “右邊約束”):
    若?ratings[i] > ratings[i+1],則?candies[i] = max(candies[i], candies[i+1] + 1)
  4. 求和?candies?數組,得到最少糖果總數。
示例驗證(以?ratings = [1,0,2]?為例)
  • 初始化:candies = [1,1,1]
  • 左→右遍歷:
    i=1(0 <1):無變化;i=2(2> 0):candies[2] = 1+1=2?→ 此時?[1,1,2]
  • 右→左遍歷:
    i=1(0 <2):無變化;i=0(1> 0):candies[0] = max(1, 1+1)=2?→ 最終?[2,1,2]
  • 求和:2+1+2=5(正確)

思路 2:一次遍歷(優化空間,進階解法)

原理

觀察到 “糖果數的變化與評分的遞增 / 遞減序列相關”,可通過記錄當前遞增 / 遞減序列的長度,動態計算糖果數,無需額外存儲?candies?數組(空間復雜度從 O (n) 降至 O (1))。

關鍵概念
  • up:當前遞增序列的長度(評分持續上升時,每多一個孩子,糖果數 + 1);
  • down:當前遞減序列的長度(評分持續下降時,每多一個孩子,糖果數需回溯調整,避免重復計算);
  • pre:前一個孩子的糖果數(動態更新)。
步驟拆解
  1. 初始化?result=1(第一個孩子至少 1 顆)、up=1down=0pre=1
  2. 從第二個孩子開始遍歷:
    • 若?ratings[i] > ratings[i-1](遞增):
      up = pre + 1down=0pre=upresult += up
    • 若?ratings[i] == ratings[i-1](相等):
      up=1down=0pre=1result += 1(相等時無需更多糖果,取 1 即可);
    • 若?ratings[i] < ratings[i-1](遞減):
      down += 1up=1
      若?down == pre(遞減序列長度等于前一個糖果數,需補 1 避免沖突):result += 1
      result += downpre=1(遞減序列中當前孩子糖果數為 1,前一個需回溯調整);
  3. 遍歷結束,result?即為最少糖果總數。
示例驗證(以?ratings = [1,2,2]?為例)
  • 初始:result=1up=1down=0pre=1
  • i=1(2>1,遞增):
    up=2pre=2result=1+2=3
  • i=2(2=2,相等):
    pre=1result=3+1=4(正確)

三、C++ 代碼實現

實現 1:兩次遍歷(易理解,推薦面試首選)

#include <iostream>
#include <vector>
#include <algorithm> // for max()using namespace std;class Solution {
public:int candy(vector<int>& ratings) {int n = ratings.size();if (n == 0) return 0;// 初始化:每個孩子至少1顆糖果vector<int> candies(n, 1);// 左→右遍歷:處理左邊約束(比左邊高則+1)for (int i = 1; i < n; ++i) {if (ratings[i] > ratings[i-1]) {candies[i] = candies[i-1] + 1;}}// 右→左遍歷:處理右邊約束(比右邊高則取max(當前, 右邊+1))for (int i = n-2; i >= 0; --i) {if (ratings[i] > ratings[i+1]) {candies[i] = max(candies[i], candies[i+1] + 1);}}// 求和int total = 0;for (int num : candies) {total += num;}return total;}
};// 測試代碼
int main() {Solution sol;vector<int> ratings1 = {1,0,2};cout << "示例1最少糖果數:" << sol.candy(ratings1) << endl; // 輸出5vector<int> ratings2 = {1,2,2};cout << "示例2最少糖果數:" << sol.candy(ratings2) << endl; // 輸出4return 0;
}

實現 2:一次遍歷(空間優化,進階)

#include <iostream>
#include <vector>using namespace std;class Solution {
public:int candy(vector<int>& ratings) {int n = ratings.size();if (n == 0) return 0;if (n == 1) return 1;int result = 1;  // 第一個孩子的1顆糖果int up = 1;      // 當前遞增序列長度int down = 0;    // 當前遞減序列長度int pre = 1;     // 前一個孩子的糖果數for (int i = 1; i < n; ++i) {if (ratings[i] > ratings[i-1]) {// 遞增序列up = pre + 1;down = 0;pre = up;result += up;} else if (ratings[i] == ratings[i-1]) {// 相等:當前孩子取1顆,重置狀態up = 1;down = 0;pre = 1;result += 1;} else {// 遞減序列down += 1;up = 1;// 若遞減長度等于前一個糖果數,需補1(避免沖突)if (down == pre) {down += 1;}result += down;pre = 1;  // 遞減序列中當前孩子糖果數為1}}return result;}
};// 測試代碼
int main() {Solution sol;vector<int> ratings1 = {1,0,2};cout << "示例1最少糖果數:" << sol.candy(ratings1) << endl; // 輸出5vector<int> ratings2 = {1,2,2};cout << "示例2最少糖果數:" << sol.candy(ratings2) << endl; // 輸出4return 0;
}

四、復雜度分析

解法時間復雜度空間復雜度適用場景
兩次遍歷O(n)O(n)面試首選,易理解、易調試
一次遍歷O(n)O(1)空間敏感場景(如大數據量)

五、總結

  1. 兩次遍歷解法的核心是 “分階段滿足雙向約束”,通過兩次單向遍歷覆蓋所有相鄰關系,邏輯清晰,適合面試中快速實現;
  2. 一次遍歷解法通過動態記錄序列長度優化空間,需要對 “遞減序列的回溯調整” 有深入理解,適合進階學習;
  3. 無論哪種解法,都遵循貪心算法的 “局部最優→全局最優” 思想:每次只處理當前能確定的最優解,最終累積得到全局最少糖果數。

建議先掌握兩次遍歷解法,再嘗試理解一次遍歷的優化邏輯,逐步提升對貪心算法的應用能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98705.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98705.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98705.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【WorkManager】無法在 Direct Boot 模式下初始化

【WorkManager】無法在 Direct Boot 模式下初始化一、問題描述二、問題分析2.1 關于 Direct Boot 模式2.2 支持 Direct Boot 模式2.3 手動初始化 WorkManager 組件2.4 WorkManager 不支持 Direct Boot 的官方修改三、解決方案一、問題描述 在使用 WorkManager 庫來實現開機上報…

Hybrid應用性能優化實戰分享(本文iOS 與 H5為例,安卓同理)

前言在移動應用開發中&#xff0c;Hybrid 架構因其跨平臺特性和開發效率優勢被廣泛采用。然而&#xff0c;WebView 的性能問題一直是開發者面臨的挑戰。本文將基于實際項目經驗&#xff0c;分享 iOS Hybrid 應用的核心優化策略&#xff0c;涵蓋 WebView 池化、預加載、用戶體驗…

點積、叉積、矩陣行列式詳解、線性相關與線性無關、矩陣的秩、矩陣可逆與不可逆詳解

1.向量 1.1 點積&#xff08;Dot Product&#xff09; 1.1.1 定義 點積是在求一個標量&#xff0c;點積結果沒有方向。 對于兩個向量u(u1,u2,u3),v(v1,v2,v3)\bold{u}(u_1,u_2,u_3),\bold{v}(v_1,v_2,v_3)u(u1?,u2?,u3?),v(v1?,v2?,v3?) 點積定義為&#xff1a;u?vu1v1u…

Mac安裝nvm詳細教程(超簡單)

本章教程,主要介紹如何在Mac操作系統上安裝nvm. 我們使用官方一鍵安裝腳本,完成安裝 一、安裝步驟 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash配置環境變量,編輯.zshrc文件 vim .zshrcexport NVM_DIR="$(

【selenium】網頁元素找不到?從$(‘[placeholder=“手機號“]‘)說起

網頁元素找不到&#xff1f;從$(‘[placeholder“手機號”]’)說起總結&#xff1a;控制臺不騙人&#xff0c;元素選不到&#xff0c;八成是寫法、時機或環境的問題。我們在寫網頁自動化腳本或者調試頁面的時候&#xff0c;經常遇到一個讓人頭疼的問題&#xff1a;明明元素就在…

SSE 模仿 GPT 響應

后端代碼 const express require(express) const cors require(cors);const app express(); app.use(cors()); const port 3000;app.listen(port, () > {console.log(Server running at http://localhost:${port}/); });const msg 全國同胞們&#xff0c; 尊敬的各位國…

MAC 多個版本 JDK進行切換

1.查看本機所有的jdk/usr/libexec/java_home -V2、打開bash_profile文件。可以在終端vim ~/.bash_profile打開&#xff0c;也可以打開訪達shiftcmdG然后輸入/Users/mac/.bash_profile&#xff08;本機bash_profile的路徑&#xff09;加入新的環境變量格式如下&#xff08;參考我…

shell 中 expect 詳解

一、概述Expect是一個免費的編程工具語言&#xff0c;用來實現自動和交互式任務進行通信&#xff0c;而無需人的干預。Expect的作者DonLibes在1990年開始編寫Expect時對Expect做有如下定義&#xff1a;Expect是一個用來實現自動交互功能的軟件套件。通過expect系統管理員可以創…

第4講 機器學習基礎概念

機器學習作為人工智能的子領域&#xff0c;專注于訓練計算機算法自動發現數據中的模式與關聯關系。以下是其核心基礎概念&#xff1a;4.1 數據數據是機器學習的基石。缺乏數據&#xff0c;算法將無從學習。數據可呈現為結構化數據&#xff08;如電子表格、數據庫&#xff09;和…

Go組合式繼承:靈活替代方案

Go 語言沒有傳統面向對象編程中的繼承機制&#xff0c;但通過組合和接口實現類似功能。Go 更提倡組合優于繼承的設計原則&#xff0c;這種設計方式更靈活且易于維護。結構體組合&#xff08;偽繼承&#xff09;通過嵌套結構體實現類似繼承的效果。子結構體可以直接訪問父結構體…

Verilog三段式FSM,實現十字路口紅綠燈

運行環境&#xff1a;VCS verdi狀態說明&#xff1a;S0 &#xff1a; 初始狀態 S1 &#xff1a; 東西方向綠燈亮&#xff0c;南北方向紅燈亮&#xff1b;點亮30周期 S2 &#xff1a; 東西方向黃燈亮&#xff0c;南北方向紅燈亮&#xff1b;點亮2 周期 S3 &#xff1a; 東西方向…

java 將pdf轉圖片

如何將pdf文件轉為圖片 import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class Pdf2Png {/**…

手搓Spring

目錄 兩種方法創建Spring容器 自定義Spring容器及前置操作 Spring掃描邏輯實現 createBean()方法 getBean()方法 依賴注入&#xff08;DI&#xff09; BeanNameAware接口 InitializingBean接口 BeanPostProcessor接口 AOP的實現 Spring 是一個輕量級的 Java 開發框架…

.NET 單文件程序詳解:從原理到實踐

C# 混淆加密大師在最新版本中, 提供了.NET單文件解包打包功能, 它可以快速解包官方打包的單文件程序&#xff0c;恢復為原始的多文件結構。也可以對解包后的程序集進行混淆與加密&#xff0c;有效提升逆向門檻。最后還能重新打包成單文件程序&#xff0c;保持對用戶友好的分發形…

Spring面試題記錄?

請簡述 Spring 框架的核心是什么&#xff1f;它主要包含了哪些核心模塊&#xff1f; spring的核心模塊主要有spring-core&#xff08;工具類&#xff0c;資源加載&#xff09;&#xff0c;spring-bean&#xff08;bean的定義&#xff0c;創建&#xff0c;封裝&#xff09;&…

一次緩存引發的文件系統數據不一致問題排查與深度解析

01 起因EFC&#xff08;Elastic File Client&#xff09;是 NAS 自研的分布式文件系統客戶端&#xff0c;最近完成了對緩存架構的更新&#xff0c;現在支持多個客戶端之間構成分布式緩存&#xff0c;底層支持 NAS、CPFS 和 OSS。由于開發時間較短&#xff0c;一直沒有做 NAS 場…

Spring Boot Gateway 教程:從入門到精通

一、Spring Cloud Gateway 簡介Spring Cloud Gateway 是基于 Spring 5、Project Reactor 和 Spring Boot 2 構建的 API 網關&#xff0c;旨在為微服務架構提供一種簡單而有效的路由管理方式。它取代了 Netflix Zuul&#xff0c;提供了更高效和更強大的網關解決方案。核心特點&a…

防火墻 只允許信任的幾臺服務器訪問

1. 首先&#xff0c;確保 firewalld 服務正在運行&#xff1a;systemctl start firewalld systemctl enable firewall2. 設置默認拒絕規則&#xff1a;設置默認拒絕所有流量&#xff08;拒絕所有的入站流量&#xff09;&#xff1a;firewall-cmd --zonepublic --add-rejectal…

十三,數據結構-樹

定義樹也是基于節點的數據結構&#xff0c;和鏈表不同的是&#xff0c;樹的節點可以指向多個節點。首先對樹的一些常用術語進行說明&#xff1a;最上面的節點叫做根節點&#xff0c;根位于樹頂&#xff0c;如圖中的節點A&#xff1b;和族譜一樣&#xff0c;節點有后代和祖先&am…

JVM-默背版

1.JVM對sychronized的優化&#xff1a;鎖膨脹、鎖消除、鎖粗化、自適應自旋鎖 &#xff08;1&#xff09;鎖膨脹&#xff1a;從無鎖、偏向鎖、輕量級鎖、重量級鎖的過程叫做鎖膨脹。在JDK1.6以前&#xff0c;sychronized是由重量級鎖實現的&#xff0c;加鎖和解鎖的過程需要從用…