C++經典的數據結構與算法之經典算法思想:貪心算法(Greedy)

貪心算法(Greedy Algorithm):通過局部最優達成全局最優的決策策略

貪心算法是一種通過每次選擇局部最優解來期望全局最優解的算法思想。它不考慮未來的影響,僅根據當前信息做出最優選擇,適用于具有貪心選擇性質最優子結構的問題。與動態規劃相比,貪心算法更簡單高效,但適用范圍更窄。

一、貪心算法的核心要素
  1. 貪心選擇性質:全局最優解可通過一系列局部最優選擇(貪心選擇)達成。即每次選擇的局部最優解,最終能累積成全局最優解。

  2. 最優子結構:問題的最優解包含子問題的最優解(與動態規劃相同)。

關鍵區別

  • 貪心算法:自頂向下,每次做貪心選擇后,僅留下一個子問題需要解決。
  • 動態規劃:自底向上或自頂向下,需考慮所有子問題并存儲其解。
二、經典應用:活動選擇問題

問題:有n個活動,每個活動有開始時間start[i]和結束時間end[i],求最多能參加的不重疊活動數量。

貪心策略:每次選擇最早結束的活動,為剩余活動留出更多時間。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// 活動結構:開始時間和結束時間
struct Activity {int start;int end;
};// 按結束時間排序
bool compare(const Activity& a, const Activity& b) {return a.end < b.end;
}// 選擇最多不重疊活動
int maxActivities(vector<Activity>& activities) {if (activities.empty()) return 0;// 1. 按結束時間排序(貪心選擇的前提)sort(activities.begin(), activities.end(), compare);int count = 1; // 至少選擇第一個活動int lastEnd = activities[0].end;// 2. 依次選擇不重疊的活動for (int i = 1; i < activities.size(); i++) {// 若當前活動開始時間 >= 上一個活動結束時間,可選擇if (activities[i].start >= lastEnd) {count++;lastEnd = activities[i].end;}}return count;
}int main() {vector<Activity> activities = {{1, 4}, {3, 5}, {0, 6}, {5, 7}, {3, 9}, {5, 9}, {6, 10}, {8, 11}, {8, 12}, {2, 14}, {12, 16}};cout << "最多可參加的活動數:" << maxActivities(activities) << endl; // 輸出4(如{1,4}, {5,7}, {8,11}, {12,16})return 0;
}

算法分析

  • 排序時間O(n log n),選擇過程O(n),總時間O(n log n)。
  • 正確性:通過數學歸納法可證明,選擇最早結束的活動能獲得最優解。
三、經典應用:哈夫曼編碼(Huffman Coding)

問題:為字符設計變長編碼,出現頻率高的字符用短編碼,頻率低的用長編碼,實現數據壓縮(無歧義解碼)。

貪心策略:每次選擇頻率最低的兩個節點合并為新節點,重復至只剩一個節點(哈夫曼樹),路徑左0右1即為編碼。

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;// 哈夫曼樹節點
struct HuffmanNode {char data;          // 字符(葉子節點)int freq;           // 頻率HuffmanNode *left, *right;HuffmanNode(char d, int f) : data(d), freq(f), left(nullptr), right(nullptr) {}
};// 優先隊列比較器(最小堆,頻率低的節點優先)
struct Compare {bool operator()(HuffmanNode* a, HuffmanNode* b) {return a->freq > b->freq; // 小頂堆(默認是大頂堆,需反向)}
};// 遞歸生成哈夫曼編碼
void generateCodes(HuffmanNode* root, string code, unordered_map<char, string>& codes) {if (!root) return;// 葉子節點:記錄編碼if (!root->left && !root->right) {codes[root->data] = code.empty() ? "0" : code; // 處理只有一個字符的情況return;}// 左子樹加"0",右子樹加"1"generateCodes(root->left, code + "0", codes);generateCodes(root->right, code + "1", codes);
}// 構建哈夫曼樹并生成編碼
unordered_map<char, string> huffmanCoding(unordered_map<char, int>& freq) {// 1. 創建葉子節點,加入最小堆priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> minHeap;for (auto& p : freq) {minHeap.push(new HuffmanNode(p.first, p.second));}// 2. 合并節點直至只剩一個根節點while (minHeap.size() > 1) {// 取出兩個頻率最低的節點HuffmanNode* left = minHeap.top(); minHeap.pop();HuffmanNode* right = minHeap.top(); minHeap.pop();// 合并為新節點(數據用特殊符號,頻率為兩者之和)HuffmanNode* merged = new HuffmanNode('$', left->freq + right->freq);merged->left = left;merged->right = right;minHeap.push(merged);}// 3. 生成編碼unordered_map<char, string> codes;generateCodes(minHeap.top(), "", codes);return codes;
}int main() {unordered_map<char, int> freq = {{'a', 5}, {'b', 9}, {'c', 12}, {'d', 13}, {'e', 16}, {'f', 45}};auto codes = huffmanCoding(freq);cout << "哈夫曼編碼:" << endl;for (auto& p : codes) {cout << p.first << ": " << p.second << endl;}// 輸出示例(可能因實現細節略有不同):// f: 0// c: 100// d: 101// a: 1100// b: 1101// e: 111return 0;
}

算法分析

  • 時間復雜度O(n log n)(n個節點,每次堆操作O(log n))。
  • 正確性:哈夫曼編碼是最優前綴碼,總編碼長度最短。
四、經典應用:零錢兌換(貪心適用場景)

問題:用最少的硬幣湊出指定金額,硬幣面額為{25, 10, 5, 1}。

貪心策略:每次選擇最大面額的硬幣,直至金額為0。

int coinChangeGreedy(int amount, vector<int>& coins) {sort(coins.rbegin(), coins.rend()); // 面額從大到小排序int count = 0;for (int coin : coins) {while (amount >= coin) {amount -= coin;count++;}if (amount == 0) break;}return amount == 0 ? count : -1; // 無法湊出返回-1
}

局限性:僅適用于“大面額是小面額倍數”的情況(如美元硬幣)。對于{1, 3, 4}面額湊6,貪心會選4+1+1(3枚),但最優解是3+3(2枚),此時需用動態規劃。

五、貪心算法 vs 動態規劃
特性貪心算法動態規劃
決策方式局部最優(僅看當前)全局最優(考慮所有子問題)
子問題關系貪心選擇后僅一個子問題需解決多個重疊子問題
時間復雜度通常更低(如O(n log n))較高(如O(n2)或O(nW))
適用場景具有貪心選擇性質的問題所有具有最優子結構的問題
典型例子活動選擇、哈夫曼編碼、最小生成樹背包問題、LCS、最長遞增子序列
六、貪心算法的優缺點

優點

  • 實現簡單,時間效率高(通常為線性或線性對數級)。
  • 空間開銷小(無需存儲子問題解)。

缺點

  • 適用范圍有限(僅能解決具有貪心選擇性質的問題)。
  • 無法回溯,一旦做出選擇就無法更改,可能錯過全局最優解。
七、如何判斷問題是否適合貪心算法
  1. 嘗試構造反例:假設貪心策略不成立,能否找到反例?
    例如:零錢兌換問題中,若存在非倍數面額,貪心可能失效。

  2. 證明貪心選擇性質
    假設全局最優解中不包含貪心選擇的元素,能否通過替換證明存在更優解?若不能,則貪心策略有效。

  3. 驗證最優子結構:問題的最優解包含子問題的最優解。

總結

貪心算法是一種直觀高效的算法思想,通過每次選擇局部最優解來逼近全局最優解。它特別適合解決資源分配、調度、編碼等具有貪心選擇性質的問題,如活動選擇、哈夫曼編碼、最小生成樹(Kruskal和Prim算法)等。

盡管貪心算法的適用范圍不如動態規劃廣泛,但其簡單性和高效性使其在許多場景中成為首選。掌握貪心算法的關鍵在于:

  1. 識別問題是否具有貪心選擇性質和最優子結構。
  2. 設計合理的貪心策略(如按結束時間排序、選最大面額等)。
  3. 通過數學證明或反例驗證策略的正確性。

在實際開發中,貪心算法常與其他算法結合使用(如貪心+動態規劃),以平衡效率和通用性。

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

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

相關文章

LangChain實戰(二十一):構建自動化AI客服系統

本文是《LangChain實戰課》系列的第二十一篇,將帶領您構建一個完整的自動化AI客服系統。通過結合對話記憶、工具調用和業務知識庫,我們將創建一個能夠處理復雜客戶查詢的智能客服解決方案。 前言 在現代商業環境中,客戶服務是企業成功的關鍵因素之一。傳統客服系統往往面臨…

一人公司智能管理系統概述

系統概述 項目結構 Al_Compny系統采用前后端分離的全棧架構&#xff0c;項目根目錄下包含兩個主要子目錄&#xff1a;Al_Compny_backend&#xff08;后端服務&#xff09;和Al_Compny_frontend&#xff08;前端應用&#xff09;。核心功能模塊 Al_Compny系統是一個面向"一…

OpenWrt | 在 PPP 撥號模式下啟用 IPv6 功能

文章目錄一、WAN 口配置二、LAN 口配置三、IPv6 測試本文將詳細介紹 將光貓的網絡模式改成橋接之后使用路由器撥號的上網方式的情況下&#xff0c;在 OpenWrt 上使用 PPP 撥號模式上網時&#xff0c;啟用 IPv6 功能的方法。 一、WAN 口配置 首先&#xff0c;我們需要在 網絡 …

Java如何實現一個安全的登錄功能?

安全登錄系統完整教程 &#x1f4cb; 目錄 項目概述技術棧安全特性項目結構核心組件詳解安全實現原理部署和運行安全最佳實踐常見問題解答進階擴展 &#x1f3af; 項目概述 這是一個基于Spring Boot和Spring Security的完整安全登錄系統&#xff0c;專為初學者設計&#xff…

星辰誕愿——生日快樂

前言 今天這篇博客并非技術文章&#xff0c;而是慶祝我可愛的妹妹18歲生日以及介紹我半年以來的學習經歷 祝生網站&#xff1a;星辰誕愿(用戶列表里第一位就是我妹妹&#xff0c;希望大家能獻上自己的祝福&#xff0c;能分享轉發更好&#xff0c;我在此感謝大家。如果使用手機&…

基于STM32單片機的智能糧倉溫濕度檢測藍牙手機APP設計

基于STM32單片機的智能糧倉溫濕度檢測藍牙手機APP設計 1 系統功能介紹 本系統是一款基于STM32單片機的智能糧倉環境監測與控制裝置&#xff0c;核心目標是通過傳感器實時采集糧倉內的溫度和濕度信息&#xff0c;并結合藍牙通信模塊將數據傳輸至手機端&#xff0c;實現對糧倉環境…

簡單視頻轉換器 avi轉mp4

直接上代碼package com.example.videoconverter;import ws.schild.jave.Encoder; import ws.schild.jave.EncoderException; import ws.schild.jave.MultimediaObject; import ws.schild.jave.encode.AudioAttributes; import ws.schild.jave.encode.EncodingAttributes; impor…

Kafka 與 RocketMQ 核心概念與架構對比

Kafka 與 RocketMQ 核心概念與架構對比DeepSeek生成&#xff0c;便于記憶大概邏輯核心概念對比圖 #mermaid-svg-dEbo1XpAjfzOjvUW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-dEbo1XpAjfzOjvUW .error-icon{fill…

30分鐘深度壓測cuBLAS:從FP64到INT8全精度性能剖析

在深度學習和高性能計算領域&#xff0c;GPU的矩陣運算性能是衡量系統算力的核心指標之一。NVIDIA的cuBLAS庫作為CUDA平臺上最基礎的線性代數計算庫&#xff0c;其性能表現直接影響著上層應用的運行效率。本文將詳細介紹如何使用cublasmatmulbench工具對多GPU進行全面的性能基準…

超越模仿:探尋智能的本源

引言&#xff1a;超越模仿&#xff0c;探尋智能的本源近年來&#xff0c;以大語言模型&#xff08;LLM&#xff09;為代表的自然語言處理&#xff08;NLP&#xff09;技術&#xff0c;在模仿人類語言生成方面取得了令人矚目的成就。從流暢的對話到精煉的文本摘要&#xff0c;機…

ROS/ROS2課程筆記00-大綱-25-26-1

大綱 AI版 以下是基于第四代高校課程核心理念設計的《ROS2機器人程序設計&#xff08;ROS2 Jazzy版&#xff09;》課程大綱&#xff0c;突出智能互聯、跨學科融合、終身學習等特征&#xff0c;并融入技術賦能、生態重塑、素養導向等要求&#xff1a; 課程名稱&#xff1a;ROS…

Linux內核進程管理子系統有什么第四十六回 —— 進程主結構詳解(42)

接前一篇文章&#xff1a;Linux內核進程管理子系統有什么第四十五回 —— 進程主結構詳解&#xff08;41&#xff09; 本文內容參考&#xff1a; Linux內核進程管理專題報告_linux rseq-CSDN博客 《趣談Linux操作系統 核心原理篇&#xff1a;第三部分 進程管理》—— 劉超 《…

Linux網絡連接不上?NetworkManager提示“device not managed“!

#操作系統 #Linux #NetworkManager適用環境kylin v10Centos 8Redhat 8一、故障現象在CentOS/RHEL(同樣適用于kylin v10&#xff09;系統中&#xff0c;管理員執行 nmcli connection up ens160 命令嘗試激活名為 ens160 的網絡連接時&#xff0c;遇到以下錯誤&#xff1a;[roo…

【系統分析師】第2章-基礎知識:數學與工程基礎(核心總結)

更多內容請見: 備考系統分析師-專欄介紹和目錄 文章目錄 一、數學統計基礎 1.1 概率論基礎 1.2 數理統計基礎 1.3 常用統計分析方法 二、圖論應用 2.1 基本概念 2.2 核心算法與應用 三、預測與決策 3.1 預測方法 3.2 決策方法 四、數學建模 4.1 建模過程 4.2 常用模型類型 五、…

StrUtil.isBlank()

這段代碼是一個條件判斷&#xff0c;用于檢查變量 shopJson 是否為空或空白&#xff0c;如果是&#xff0c;就直接返回 null。我們來逐句講解&#xff1a;原始代碼&#xff1a; if(StrUtil.isBlank(shopJson)) {// 3.存在&#xff0c;直接返回return null; }逐句解釋&#xff1…

mysql 回表查詢(二次查詢,如何檢查,如何規避)

h5打開以查看 “回表查詢”通常發生在使用二級索引&#xff08;Secondary Index&#xff09;的查詢中。當查詢所需的數據列并不全部包含在二級索引中時&#xff0c;即使使用了索引&#xff0c;MySQL 也需要根據索引記錄中的主鍵值&#xff0c;回到聚簇索引&#xff08;Cluster…

深度學習(二):神經元與神經網絡

在人工智能的浪潮中&#xff0c;神經網絡&#xff08;Neural Networks&#xff09;無疑是驅動核心技術的引擎&#xff0c;它賦予了計算機前所未有的學習和識別能力。而這一切的起點&#xff0c;是受到生物大腦中基本單元——神經元&#xff08;Neurons&#xff09;的深刻啟發。…

JavaScript 行為型設計模式詳解

1. 觀察者模式1.1. 使用場景觀察者模式用于對象間的一對多依賴關系&#xff0c;當一個對象的狀態發生變化時&#xff0c;所有依賴于它的對象都能收到通知并自動更新。常用于事件處理、通知系統。在前端中&#xff0c;觀察者模式用于實現事件監聽、數據綁定等功能。1.2. 代碼實現…

指令查找表LUT

本文整理自22. FlexSPI—讀寫外部SPI NorFlash — [野火]i.MX RT庫開發實戰指南——基于i.MXRT1052 文檔 用作個人學習和分享 指令查找表LUT 訪問FLASH存儲器通常包含一些讀寫功能的的控制指令&#xff0c;主控設備可通過這些指令訪問FLASH存儲器。 為了適應這種需求&#…

uv使用指南

&#x1f680; Python 打包工具 UV 使用指南 UV 是一個用 Rust 編寫的極速 Python 包管理器和解析器&#xff0c;旨在成為 pip、pip-tools、virtualenv 等工具的單一替代方案。 &#x1f4cb; 目錄 核心概念與設計哲學安裝與配置基礎使用方法項目管理與工作流高級功能與技巧…