從零開始的數據結構教程(六) 貪心算法


🍬 標題一:貪心核心思想——發糖果時的最優分配策略

貪心算法 (Greedy Algorithm) 是一種簡單直觀的算法策略。它在每一步選擇中都采取在當前狀態下最好或最優(即最有利)的選擇,從而希望得到一個全局最優解。這就像你作為班主任發糖果,每次都想讓眼前的孩子滿意,希望這種局部最優能帶來整體最優的效果。

三大適用條件

并非所有問題都適合用貪心算法解決,它通常需要滿足以下三個條件:

  1. 貪心選擇性質 (Greedy Choice Property):每一步的局部最優選擇,都能導致最終的全局最優解。例如,在“硬幣找零”問題中,如果硬幣面額系統是“標準”的(如 1, 5, 10, 25 美分),那么每次都優先選擇最大面額的硬幣就能得到最少硬幣數。
  2. 無后效性 (Optimal Substructure):當前的選擇不會影響后續子問題的最優解,即后續問題的最優解不依賴于之前的選擇,只依賴于當前狀態。例如,在“區間調度”中,一旦你選擇了最早結束的會議,后續會議的選擇空間并不會因此受限,而是基于剩余時間繼續做決策。
  3. 子問題可合并 (Overlapping Subproblems):雖然這是動態規劃的特征之一,但在貪心算法中,這意味著局部解能夠直接或以簡單的方式構成全局解。例如,在霍夫曼編碼中,每次合并兩個最小頻率的節點,最終能構建出最優的二叉樹。

與 DP 的關鍵區別

貪心算法和動態規劃都涉及將問題分解為子問題,但它們在解決方式上存在根本差異:

  • 貪心:做選擇時不考慮未來,只看當前局部最優,并且一旦做出選擇就不可回退。它不需要存儲所有子問題的解。
  • 動態規劃:會探索所有可能的局部最優解,并存儲它們,通過狀態轉移方程來構建全局最優解。它具有“回退”機制,可以從之前的多種選擇中推導出當前的最優。
# 貪心 vs 動態規劃:以「硬幣找零」為例
# 假設硬幣面額為 [1, 5, 10, 25],找零 30
# 貪心:25 + 5 (2枚)
# DP:10 + 10 + 10 (3枚) 或 25 + 1 + 1 + 1 + 1 + 1 (6枚)# 貪心(可能無法得到最優解,例如面額 [1, 15, 25], 找零 30, 貪心會是 25+1+1+1+1+1 (6枚), 最優是 15+15 (2枚))
def coinChangeGreedy(coins, amount):coins.sort(reverse=True) # 優先使用大面額硬幣count = 0for coin in coins:while amount >= coin:amount -= coincount += 1return count if amount == 0 else -1 # 如果 amount 不為 0,說明無法找零# DP(保證最優解)
def coinChangeDP(coins, amount):# dp[i] 表示湊齊金額 i 所需的最少硬幣數dp = [float('inf')] * (amount + 1)dp[0] = 0 # 湊齊金額 0 需要 0 枚硬幣for i in range(1, amount + 1): # 遍歷所有可能的金額for coin in coins:         # 遍歷所有硬幣面額if i >= coin:# 如果當前金額 i 大于等于當前硬幣面額 coin# 那么湊齊 i 的最少硬幣數可能是:# 1. 不使用當前硬幣,沿用 dp[i] (之前可能計算過)# 2. 使用當前硬幣,那么剩下的金額 i - coin 需要 dp[i - coin] 枚硬幣dp[i] = min(dp[i], dp[i - coin] + 1)return dp[amount] if dp[amount] != float('inf') else -1 # 如果為 inf,表示無法湊齊# print(coinChangeGreedy([1, 15, 25], 30)) # 輸出 6
# print(coinChangeDP([1, 15, 25], 30))     # 輸出 2

? 標題二:區間調度問題——會議室的高效安排

區間調度問題是一個經典的貪心算法應用場景。

經典問題

給定若干會議的開始和結束時間 [start, end],如何安排最多的不重疊會議,使得一個會議室可以安排盡可能多的會議?

貪心策略

按結束時間排序:優先選擇最早結束的會議。為什么?因為最早結束的會議會給后續會議留出最大的時間空隙,從而使得我們有機會安排更多的會議。

import java.util.Arrays;
import java.util.Comparator; // 引入 Comparator 接口class Solution {public int maxEvents(int[][] intervals) {// Step 1: 按結束時間升序排序會議// 如果結束時間相同,則按開始時間升序排序(可選,但通常不影響結果)Arrays.sort(intervals, (a, b) -> a[1] - b[1]);int count = 0; // 記錄安排的會議數量int lastEndTime = Integer.MIN_VALUE; // 記錄上一個已安排會議的結束時間// Step 2: 遍歷排序后的會議for (int[] meeting : intervals) {int currentStart = meeting[0];int currentEnd = meeting[1];// 如果當前會議的開始時間 >= 上一個已安排會議的結束時間// 說明當前會議可以安排,且不與之前的會議重疊if (currentStart >= lastEndTime) {count++;            // 安排該會議lastEndTime = currentEnd; // 更新上一個會議的結束時間}}return count;}
}

變形題

  • 需要多間會議室時:這通常會轉化為其他問題,例如“最小箭射氣球”(LeetCode 452),它尋找最少數量的“箭頭”能射爆所有氣球(代表區間),本質上是尋找最少數量的區間來覆蓋所有給定區間。
  • 帶權值的區間:如果每個會議有不同的“價值”或“收益”,需要最大化總收益,那么純粹的貪心可能不再適用,通常需要結合動態規劃來解決(例如“最大收益的兼職工作”)。

🏃 標題三:跳躍游戲——從起點到終點的最少步數

跳躍游戲系列問題也是貪心算法的經典應用。

兩類經典問題

  1. 能否到達終點(LeetCode 55):給定一個非負整數數組 nums,每個元素 nums[i] 代表你在位置 i 最多可以向前跳躍的長度。判斷你是否能從第一個位置跳到最后一個位置。

    def canJump(nums):max_reach = 0 # 記錄當前能到達的最遠位置# 遍歷數組,直到到達終點或無法繼續前進for i in range(len(nums)):if i > max_reach: # 如果當前位置 i 已經超出了之前能到達的最遠位置return False  # 說明無法到達終點# 更新能到達的最遠位置:當前位置 i + 從當前位置能跳的距離 nums[i]max_reach = max(max_reach, i + nums[i])if max_reach >= len(nums) - 1: # 如果能到達或越過終點return True                # 則成功到達return True # 如果循環結束(即已經遍歷完數組),說明可以到達終點
    
  2. 最少跳躍次數(LeetCode 45):給定一個非負整數數組 nums,同上,求到達最后一個位置的最少跳躍次數

    def jump(nums):if len(nums) <= 1:return 0jumps = 0          # 跳躍次數current_end = 0    # 當前跳躍能到達的最遠邊界farthest = 0       # 在當前跳躍范圍內,下一步能到達的最遠位置for i in range(len(nums) - 1): # 遍歷到倒數第二個位置,因為最后一個位置不需要再跳# 更新在當前跳躍范圍內能到達的最遠位置farthest = max(farthest, i + nums[i])if i == current_end: # 如果當前位置 i 達到了當前跳躍的邊界jumps += 1       # 進行一次跳躍current_end = farthest # 更新下一次跳躍的邊界為當前能到達的最遠位置# 注意:這里不需要檢查 farthest 是否能到達終點,# 因為 for 循環條件保證了最終 current_end 會達到或超過 len(nums) - 1return jumps
    

關鍵洞察

貪心地在每一步選擇能跳最遠的選項(但不是盲目地跳到最遠,而是規劃好下一次跳躍的邊界)。


🛒 標題四:高頻面試題——分發糖果(雙向貪心)

問題

在一個班級里,有 n 個孩子,他們的能力值用整數數組 ratings 表示。你需要給這些孩子分發糖果,并滿足以下兩個條件:

  1. 每個孩子至少分到 1 顆糖果。
  2. 能力值比其相鄰孩子高的孩子,必須分到比相鄰孩子更多的糖果。

求最少需要分發多少顆糖果。

雙向遍歷策略

這個問題不能簡單地從左到右或從右到左遍歷一次,因為它有雙向的依賴關系。需要進行雙向貪心

  1. 從左到右遍歷:確保右邊的孩子比左邊高的,能獲得更多糖果。
  2. 從右到左遍歷:確保左邊的孩子比右邊高的,能獲得更多糖果。
  3. 取兩者最大值:最終每個孩子分到的糖果數,是兩次遍歷結果的最大值
def candy(ratings):n = len(ratings)# left 數組:從左到右遍歷,保證 ratings[i] > ratings[i-1] 時,left[i] > left[i-1]left = [1] * n # 初始化每個孩子至少1顆糖for i in range(1, n):if ratings[i] > ratings[i - 1]:left[i] = left[i - 1] + 1# right 變量:從右到左遍歷,保證 ratings[i] > ratings[i+1] 時,當前孩子獲得更多糖# total:總糖果數,先加上 left 數組的最后一個元素right = 1total = left[n - 1] # 最后一個孩子的糖果數,至少是 left[n-1]for i in range(n - 2, -1, -1): # 從倒數第二個孩子開始,向左遍歷if ratings[i] > ratings[i + 1]:right += 1 # 如果當前孩子比右邊高,right 增加else:right = 1  # 否則,重置 right 為 1 (因為右邊孩子可能比自己高或相等)# 當前孩子實際獲得的糖果數,是 left[i] 和 right 兩者中的最大值# 這樣才能同時滿足左右兩個方向的條件total += max(left[i], right)return total
  • 時間復雜度 O ( n ) O(n) O(n),因為進行了兩次線性遍歷。
  • 空間復雜度 O ( n ) O(n) O(n),因為使用了 left 數組。可以進一步優化到 O ( 1 ) O(1) O(1) 空間,但代碼會更復雜。

📊 總結表:貪心算法適用場景

問題類型典型例題貪心策略
區間問題無重疊區間(LeetCode 435)結束時間排序,優先選擇最早結束的。
分配問題分發糖果(LeetCode 135)雙向遍歷(從左到右,從右到左)滿足約束。
跳躍問題跳躍游戲 II(LeetCode 45)維護當前能到達的最遠位置,并在必要時進行跳躍。
字符串構造重構字符串(LeetCode 767)優先使用剩余最多的字符,避免連續。
硬幣/貨幣硬幣找零(標準面額系統)優先使用最大面額的硬幣。

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

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

相關文章

CPP中CAS std::chrono 信號量與Any類的手動實現

前言 CAS&#xff08;Compare and Swap&#xff09; 是一種用于多線程同步的原子指令。它通過比較和交換操作來確保數據的一致性和線程安全性。CAS操作涉及三個操作數&#xff1a;內存位置V、預期值E和新值U。當且僅當內存位置V的值與預期值E相等時&#xff0c;CAS才會將內存位…

Axure設計案例——科技感對比柱狀圖

想讓數據對比展示擺脫平淡無奇&#xff0c;瞬間抓住觀眾的眼球嗎&#xff1f;那就來看看這個Axure設計的科技感對比柱狀圖案例&#xff01;科技感設計風格運用獨特元素打破傳統對比柱狀圖的常規&#xff0c;營造出一種極具沖擊力的視覺氛圍。每一組柱狀體都仿佛是科技戰場上的士…

怒更一波免費聲音克隆和AI配音功能

寶子們&#xff01; 最近咱軟件TransDuck的免費聲音克隆和AI配音功能被大家用爆啦&#xff01;感謝各位自來水瘋狂安利&#xff01;&#xff01; DD這里也是收到好多用戶提的寶貴建議&#xff01;所以&#xff0c;連夜肝了波更新&#xff01; 這次重點更新使用克隆音色進行A…

UDP協議原理與Java編程實戰:無連接通信的奧秘

1.UDP協議核心原理 1. 無連接特性&#xff1a;快速通信的基石 UDP&#xff08;User Datagram Protocol&#xff0c;用戶數據報協議&#xff09;是TCP/IP協議族中無連接的輕量級傳輸層協議。與TCP的“三次握手”建立連接不同&#xff0c;UDP通信無需提前建立鏈路&#xff0c;發送…

vue-seamless-scroll 結束從頭開始,加延時后滾動

今天遇到一個大屏需求&#xff1a; 1??初始進入頁面停留5秒&#xff0c;然后開始滾動 2??最后一條數據出現在最后一行時候暫停5秒&#xff0c;然后返回1?? 依次循環&#xff0c;發現vue-seamless-scroll的方法 ScrollEnd是監測最后一條數據消失在第一行才回調&#xff…

[Protobuf] 快速上手:安全高效的序列化指南

標題&#xff1a;[Protobuf] (1)快速上手 水墨不寫bug 文章目錄 一、什么是protobuf&#xff1f;二、protobuf的特點三、使用protobuf的過程&#xff1f;1、定義消息格式&#xff08;.proto文件&#xff09;(1)指定語法版本(2)package 聲明符 2、使用protoc編譯器生成代碼&…

uniapp調用java接口 跨域問題

前言 之前在Windows10本地 調試一個舊項目&#xff0c;手機移動端用的是Uni-app&#xff0c;vue的版本是v2。后端是java spring-boot。運行手機移動端的首頁請求后臺接口老是提示錯誤信息。 錯誤信息如下&#xff1a; Access to XMLHttpRequest at http://localhost:8080/api/…

[ Qt ] | Qlabel使用

目錄 屬性 setTextFormat 插入圖片 設置圖片根據窗口大小實時變化 邊框和對其方式 ?編輯 設置縮進 設置伙伴 Qlabel可以用來顯式圖片和文字 屬性 text textFormat Qlabel獨有的機制&#xff1a;buddy setTextFormat 插入圖片 設置圖片根據窗口大小實時變化 Qt中表…

Springboot 項目一啟動就獲取HttpSession

在 Spring Boot 項目中&#xff0c;HttpSession 是有狀態的&#xff0c;通常只有在用戶發起 HTTP 請求并建立會話后才會創建。因此&#xff0c;在項目啟動時&#xff08;即應用剛啟動還未處理任何請求&#xff09;是無法獲取到 HttpSession 的。 方法一&#xff1a;使用 HttpS…

Step9—Ambari Web UI 初始化安裝 (Ambari3.0.0)

Ambari Web UI 安裝 如果還不會系統性的部署&#xff0c;或者前置內容不熟悉&#xff0c;建議從Step1 開始閱讀。不通版本針對于不同操作系統可能存在差異&#xff01;這里我也整理好了 https://doc.janettr.com/install/manual/ 1. 進入 Ambari Web UI 并登錄 在瀏覽器中訪…

熱門大型語言模型(LLM)應用開發框架

我們來深入探索這些強大的大型語言模型&#xff08;LLM&#xff09;應用開發框架&#xff0c;并且我會嘗試用文本形式描述一些核心的流程圖&#xff0c;幫助您更好地理解它們的工作機制。由于我無法直接生成圖片&#xff0c;我會用文字清晰地描述流程圖的各個步驟和連接。 Lang…

機器學習數據降維方法

1.數據類型 2.如何選擇降維方法進行數據降維 3.線性降維&#xff1a;主成分分析&#xff08;PCA&#xff09;、線性判別分析&#xff08;LDA&#xff09; 4.非線性降維 5.基于特征選擇的降維 6.基于神經網絡的降維 數據降維是將高維數據轉換為低維表示的過程&#xff0c;旨在保…

太陽系運行模擬程序-html動畫

太陽系運行模擬程序-html動畫 by AI: <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>交互式太陽系…

2025年全國青少年信息素養大賽 scratch圖形化編程挑戰賽 小低組初賽 內部集訓模擬題解析

2025年信息素養大賽初賽scratch模擬題解析 博主推薦 所有考級比賽學習相關資料合集【推薦收藏】 scratch資料 Scratch3.0系列視頻課程資料零基礎學習scratch3.0【入門教學 免費】零基礎學習scratch3.0【視頻教程 114節 免費】 歷屆藍橋杯scratch國賽真題解析歷屆藍橋杯scr…

grid網格布局

使用flex布局的痛點 如果使用justify-content: space-between;讓子元素兩端對齊&#xff0c;自動分配中間間距&#xff0c;假設一行4個&#xff0c;如果每一行都是4的倍數那沒任何問題&#xff0c;但如果最后一行是2、3個的時候就會出現下面的狀況&#xff1a; /* flex布局 兩…

通義靈碼2.5——基于MCP實現我的12306火車票智能查詢小助手

本文因排版顯示問題&#xff0c;為保證閱讀體驗&#xff0c;請大家訪問&#xff1a; 通義靈碼2.5——基于MCP打造我的12306火車票智能查詢小助手-CSDN博客 前沿技術應用全景圖 本項目作為通義靈碼2.5的標桿實踐案例&#xff0c;展現了AI輔助開發在復雜業務系統中的革命性突破…

Unity Button 交互動畫

在UGUI的Button組件中&#xff0c;有一個過渡動畫表現的功能。可以對按鈕的不同交互狀態添加交互反饋動畫&#xff0c;來提高玩家的交互體驗。 交互狀態 名稱 描述 Normal 正常情況 Highlighted 高亮顯示&#xff0c;例如鼠標觸碰到按鈕點擊范圍 Pressed 按鈕被按下的時…

釘釘熱點實時推送助理-思路篇

以下是針對熱點實時推送助理的功能描述&#xff0c;結合機器學習技術棧與用戶場景的通俗化解釋&#xff1a; 快速體驗的話直接用釘釘掃描下方二維碼體驗 1. 核心功能 &#xff08;1&#xff09;熱點抓取引擎 類比&#xff1a;像蜘蛛爬取全網信息&#xff08;網絡爬蟲信息抽取…

remote: error: hook declined to update refs/heads.....

gitee拉取分支&#xff0c;修改上傳出現的問題&#xff0c;折騰了好久&#xff0c;淺淺記錄. 1. 首次克隆倉庫 # 克隆倉庫&#xff08;使用 HTTPS 或 SSH&#xff09; git clone ------------ cd xxx-project2. 配置正確的用戶信息&#xff08;關鍵步驟&#xff01;&#xff…

使用Vue + Element Plus實現可多行編輯的分頁表格

需求背景&#xff1a; 在現代前端開發中&#xff0c;表格作為數據展示和交互的重要組件&#xff0c;在各類管理系統、數據平臺中有著廣泛的應用。隨著用戶對數據操作便捷性要求的不斷提高&#xff0c;具備靈活編輯功能的表格成為了開發中的常見需求。特別是在需求處理大…