Studying-代碼隨想錄訓練營day29| 134. 加油站、135. 分發糖果、860.檸檬水找零、406.根據身高重建隊列

第29天,貪心part03,快過半了(? ?_?)?💪,編程語言:C++

目錄

134.加油站

135. 分發糖果

860.檸檬水找零

406.根據身高重建隊列


134.加油站

文檔講解:代碼隨想錄加油站

視頻講解:手撕加油站

題目:

學習:本題其實很容易發現gas數組和cost數組的差,就是該站點結余的油量,我們在移動過程中理應讓油量始終保持為正。因此本題的思考邏輯就是從某個站點出發,如果油量小于0則說明該點不能環路行駛一圈,反則則可以。

解法一:暴力解法,暴力遍歷每個節點環行一周的結果。(力扣超時)

注意:for循環適合模擬從頭到尾的遍歷,而while循環適合模擬環形遍歷,因此對于本題的場景需要使用while進行環行循環要十分注意。

//時間復雜度O(n^2)
//空間復雜度O(1)
class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {for (int i = 0; i < cost.size(); i++) {int rest = gas[i] - cost[i]; // 記錄剩余油量int index = (i + 1) % cost.size();while (rest > 0 && index != i) { // 模擬以i為起點行駛一圈(如果有rest==0,那么答案就不唯一了)rest += gas[index] - cost[index];index = (index + 1) % cost.size();}// 如果以i為起點跑一圈,剩余油量>=0,返回該起始位置if (rest >= 0 && index == i) return i;}return -1;}
};

解法二:貪心

貪心策略為:首先只有在總油量減去總消耗大于等于0的時候,才說明可以跑完一圈。其次我們在環行的過程中,還需要保持剩油量是正的,才能夠繼續行駛下去。因此我們可以 i 從0開始累加每個站點的剩油量(gas[i] - cost[i]),一旦累加和小于零,則說明[0, i]區間都不能作為起始位置,因為這個區間選擇任何一個位置作為起點,到i這里都會斷油(一定不會說從中間開始某個點不會斷油,因為如果是那樣的話后半部分為正,那前半部分一定為負,也會使得滿足剩油量為負的條件,跳到i+1),那么起始位置從i+1算起,再從0計算剩油量。

那么局部最優:當前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因為從i之前開始一定不行。全局最優:找到可以跑一圈的起始位置

代碼:

//時間復雜度O(n)
//空間復雜度O(1)
class Solution {
public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {//貪心,局部最優找到累加為正的地方int count = 0; //計算總和,總和小于0說明無法環路一周int idcount = 0; //計算當前i往后的總和,該總和小于0說明不能從i出發int index = 0; //找到正確的出發下標for(int i = 0; i < gas.size(); i++) {int rest = gas[i] - cost[i];count += rest;idcount += rest;if(idcount < 0) { // 當前累加rest[i]和 curSum一旦小于0idcount = 0;  // idcount從0開始計算index = i + 1;  //起始位置更新為i+1}}if(count < 0) return -1;return index;}
};

135. 分發糖果

文檔講解:代碼隨想錄分發糖果

視頻講解:手撕分發糖果

題目:

學習:本題屬于貪心算法中兩個維度的問題,也就是需要兩次貪心分別處理兩種情況。本題中我們對于一個孩子來說,即要考慮他的左邊和他的大小關系,又要考慮他的右邊和他的大小關系,這就是我們要處理的兩個問題。對于這種情況而言,我們需要將這兩個問題分開解決,先考慮每個孩子右邊比自身大的情況,再考慮每個孩子左邊比自身大的情況,不能同時考慮,否則就會顧此失彼。

首先考慮右邊孩子評分大的情況,從前向后遍歷。此時的局部最優為:只要右邊評分比左邊大,右邊的孩子就多一個糖果。全局最優為:相鄰的孩子中,評分高的右孩子獲得比左邊孩子更多的糖果。

接著我們再確定左邊孩子評分大的情況,從后往前遍歷。此時的局部最優就為:只要左邊評分比右邊大,左邊的孩子就多一個糖果。全局最優為:相鄰的孩子中,評分高的左孩子獲得比右邊孩子更多的糖果。(這個地方要注意,再更新評分更高的孩子的時候,我們要判斷此時更新的值和原本的值誰更大,因為原本的值是保證了他和他左邊孩子的關系,因為誰更大取誰,這樣就能夠同時保證左右孩子的關系了。

代碼:

//時間復雜度O(n)
//空間復雜度O(n)
class Solution {
public:int candy(vector<int>& ratings) {//兩次貪心vector<int> candys(ratings.size(), 1); //初始化先給每個小孩一個糖果//第一次貪心,從左向右,先遍歷右孩子比左孩子大的情況,局部最優:右孩子比左孩子大就多一個糖果for(int i = 1; i < ratings.size(); i++) {if(ratings[i] > ratings[i - 1]) {candys[i] = candys[i - 1] + 1;}}//第二次貪心,從右向左,遍歷左孩子比右孩子大的情況,局部最優:左孩子比右孩子大就多一個糖果for(int i = ratings.size() - 2; i >= 0; i--) {if (ratings[i] > ratings[i + 1]) {//這里要注意要去當前值和右孩子加1之間的最大值,這樣才能保證該點同時大于左右孩子。candys[i] = max(candys[i], candys[i + 1] + 1); }}//求和int result = 0;for(int i = 0; i < candys.size(); i++) {result += candys[i];}return result;}
};

860.檸檬水找零

文本講解:代碼隨想錄檸檬水找零

視頻講解:手撕檸檬水找零

題目:

學習:本題看起來題干很多很復雜,但實際上就是每位顧客花5美元買檸檬水,但這個過程中會出現三種情況分別是:1.顧客給了5美元,不需要找錢;2.顧客給了10美元,需要找5美元給顧客;3.顧客給了20美元,需要找15美元給顧客。針對這三種情況我們可以逐個分析。?

1.顧客給了5美元:對于這種情況,我們不需要找錢的同時,我們手里還多了一張5美元。

2.顧客給了10美元,需要找5美元:對于這種情況,需要我們手頭上有至少一張5美元,否則我們就不能正確找零返回false。如果我們手頭上有至少一張5美元,則我們減少一張5美元,多一張10美元。

3.顧客給了20美元,需要找15美元:對于這種情況,就是需要我們進行貪心算法的時候了,因為對于我們來說5美元既可以給10美元的顧客找錢,又可以給20美元的顧客找錢。因此我們在給20美元的顧客找錢的時候應優先消耗美元10,保留更加萬能的5美元,這樣才能更好的進行正確的找零。因此有三種情況:1.手頭上有10美元,5美元,則減少一張10美元,一張5美元;2.手頭上美元10美元,但5美元大于3張,則減少3張5美元;3.手頭上湊不出15美元的數(注意兩張10美元不行)返回false。

代碼:根據以上三種情況,則可編寫代碼

//時間復雜度O(n)
//空間復雜度O(1)
class Solution {
public:bool lemonadeChange(vector<int>& bills) {//記錄手頭上5美元和10美元的數量,20美元的不需要記錄,因為20沒有不能用于找錢int count5 = 0;int count10 = 0;for(int i = 0; i < bills.size(); i++) {//分別處理收到三種錢的情況if(bills[i] == 5) {count5++;}if(bills[i] == 10) {if(count5 == 0) {return false;}count5--;count10++;}//第三種情況需要進行貪心//貪心策略:如果有10元鈔票優先找10元鈔票,因為5元鈔票比10元鈔票更萬能if(bills[i] == 20) {if(count10 > 0 && count5 > 0) {count10--;count5--;}else if(count5 >= 3) {count5 -= 3;}else {return false;}}}return true;}
};

406.根據身高重建隊列

文檔講解:代碼隨想錄根據身高重建隊列

視頻講解:手撕根據身高重建隊列

題目:

學習:本題與分發糖果相同,同樣都是兩維問題,我們需要分別考慮身高和前面身高大于等于的人的數量這兩個因素。不能兩個維度一起考慮,一定會出現顧此失彼的情況。

本題中如果我們需要對數組先進行排序,來解決一個維度上的問題。如果我們選擇對k(身高大于等于的數量)進行排序,會發現最后得到的數組會發現k的排列不符合條件,身高也不符合條件,兩個維度都沒辦法確定下來(這是因為會存在部分身高很高,因此k為0的干擾)。

因此本題我們需要先對身高進行排序,身高從高到低進行排序,這樣也會有個好處,排序之后,如果后面的位置出現問題需要前插,也不會影響前面的數的排序,因為后面的數一定比前面的數小,不會影響到k值。

因此本題的貪心就是:局部最優:優先按身高高的people的k來插入。插入操作過后的people滿足隊列屬性;全局最優:最后都做完插入操作,整個隊列滿足題目隊列屬性。

代碼:

//時間復雜度O(nlogn + n^2)
//空間復雜度O(n)
class Solution {
public:static bool camp(vector<int>&a, vector<int>&b) {//身高從大到小排列,便于進行插入處理if(a[0] != b[0]) {return a[0] > b[0];}else { //如果身高一樣,讓其前面身高少的排前面return a[1] < b[1];}}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {//對數組進行排序,讓身高高的先排前面,這樣我們就能依據前面身高的數量,來插入每一個數sort(people.begin(), people.end(), camp);//貪心策略,遍歷數組,優先按照身高高的people的k來進行插入,這樣也就不用擔心后續身高矮的插入造成影響vector<vector<int>> queue; //這里不能初始化數組的大小,因為后續要采用插入進行操作for(int i = 0; i < people.size(); i++) {int index = people[i][1]; //確定要插入的位置queue.insert(queue.begin() + people[i][1], people[i]);}return queue;}
};

本題中存在一個問題,使用vector是非常費時的,C++中vector(可以理解是一個動態數組,底層是普通數組實現的)如果插入元素大于預先普通數組大小,vector底部會有一個擴容的操作,即申請兩倍于原先普通數組的大小,然后把數據拷貝到另一個更大的數組上。所以使用vector(動態數組)來insert,是費時的,插入再拷貝的話,單純一個插入的操作就是O(n^2)了,甚至可能拷貝好幾次,就不止O(n^2)了。

代碼:因此本題可以采用list類來保存數組,雖然時間復雜度沒變,但實際效率是增加了的。

class Solution {
public:// 身高從大到小排(身高相同k小的站前面)static bool cmp(const vector<int>& a, const vector<int>& b) {if (a[0] == b[0]) return a[1] < b[1];return a[0] > b[0];}vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort (people.begin(), people.end(), cmp);list<vector<int>> que; // list底層是鏈表實現,插入效率比vector高的多for (int i = 0; i < people.size(); i++) {int position = people[i][1]; // 插入到下標為position的位置std::list<vector<int>>::iterator it = que.begin();while (position--) { // 尋找在插入位置it++;}que.insert(it, people[i]);}return vector<vector<int>>(que.begin(), que.end());}
};

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

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

相關文章

《夢醒蝶飛:釋放Excel函數與公式的力量》8.3 COUNTBLANK函數

8.3 COUNTBLANK函數 在數據處理和分析中&#xff0c;我們經常需要識別和統計數據集中的空白單元格。COUNTBLANK函數是Excel中用于統計某個范圍內空白單元格數量的強大工具。 8.3.1 函數簡介 COUNTBLANK函數用于統計指定范圍內的空白單元格數量。這在數據清洗、數據完整性檢查…

MySQL之備份與恢復(四)

備份與恢復 存儲引擎和一致性 3.復制 從備庫中備份最大的好處是可以不干擾主庫&#xff0c;避免在主庫上增加額外的負載。這是一個建立備庫的好理由&#xff0c;即使不需要用它做負載均衡或高可用。如果錢是個問題&#xff0c;也可以把備份用的備庫用于其他用戶&#xff0c;…

【C/C++ new/delete和malloc/free的異同及原理】

new/delete和malloc/free都是用于在C&#xff08;以及C語言在malloc/free的情況下&#xff09;中動態申請和釋放內存的機制&#xff0c;但它們之間存在一些顯著的異同點。以下是對這兩組函數/運算符的異同點的詳細分析&#xff1a; 相同點 目的相同&#xff1a;兩者都用于在堆…

C++編程邏輯講解step by step:類之間的交互

題目 設計一個點類Point&#xff0c;再設計一個矩形類&#xff0c;矩形類使用Point類的兩個坐標點作為矩形的對角頂點。并可以輸出4個坐標值和面積。 分析 1.點類&#xff0c;自然維護的是一個點的坐標&#xff0c; #include < iostream > using namespace std; class …

【C語言基礎知識點】C語言-使用 fgets 讀取包含空格的字符串

使用 fgets 讀取包含空格的字符串 // 使用 fgets 讀取包含空格的字符串 #include <stdio.h> #include <string.h> int main() { char name[100]; printf("Enter your name: "); fgets(name, sizeof(name), stdin); // 移除可能讀取到的換行符 n…

Matlab/simulink三段式電流保護

電流1段仿真波形如下所示 電流2段仿真波形如下所示 電流3段仿真波形如下所示

Centos7安裝Minio筆記

一、Minio概述 Minio是一款開源的對象存儲服務器&#xff0c;可以運行在多種操作系統上&#xff0c;包括Linux、Windows和MacOS等。提供一種簡單、可擴展、高可用的對象存儲解決方案&#xff0c;支持多種數據格式&#xff0c;包括對象、塊和文件等。Minio是一款強大、靈活、可…

WCCI 2024第三彈:忍者表演驚艷全場,盛大晚宴不容錯過

WCCI 2024第三彈&#xff1a;忍者表演驚艷全場&#xff0c;盛大晚宴不容錯過&#xff01; 會議之眼 快訊 會議介紹 IEEE WCCI&#xff08;World Congress on Computational Intelligence&#xff09;2024&#xff0c;即2024年IEEE世界計算智能大會&#xff0c;于6月30日至7月…

【前端知識】一篇速成 建議收藏

HTML基礎概念 正式敲代碼之前呢,我們先來看幾個概念: 0 靜態網頁和動態網頁 靜態網頁: 頁面的內容和顯示效果就基本上不會發生變化了--除非你修改頁面代碼。 動態網頁: 頁面代碼雖然沒有變&#xff0c;但是顯示的內容卻是可以隨著時間、環境或者數據庫操作的結果而發生改變的…

【康復學習--LeetCode每日一題】3099. 哈沙德數

題目&#xff1a; 如果一個整數能夠被其各個數位上的數字之和整除&#xff0c;則稱之為 哈沙德數&#xff08;Harshad number&#xff09;。給你一個整數 x 。如果 x 是 哈沙德數 &#xff0c;則返回 x 各個數位上的數字之和&#xff0c;否則&#xff0c;返回 -1 。 示例 1&a…

【Qt知識】window frame 對窗口坐標的影響

在Qt中&#xff0c;窗口框架&#xff08;Window Frame&#xff09;對Widget的尺寸計算和坐標定位有著直接的影響&#xff0c;這主要是因為窗口框架本身占據了一定的空間&#xff0c;包括標題欄、最小化/最大化/關閉按鈕以及邊框。這部分額外的空間在不同的應用場景下需要被考慮…

windows非白名單exe監控并殺死

需求&#xff1a;孩子在家用電腦上網課&#xff0c;總是悄悄打開游戲或視頻軟件 方案&#xff1a;指定白名單exe&#xff0c;打開非白名單的就自動被殺死&#xff0c;并記錄日志供查看 不知道是否還有更好的結果方案&#xff1f; import psutil import time import logging#…

2024.7.4 刷題總結

2024.7.4 **每日一題** 3086.拾起k個1需要的最少行動次數&#xff0c;在這道題我們可以把0看成空位&#xff0c;第二種操作相當于把一個1移動到和它相鄰的空位上&#xff0c;而第一種操作則是貪心地把和當前下標相鄰的0變成1;當maxchanges較大時&#xff0c;優先使用第一種操作…

第二十條:與抽象類相比,優先選擇接口

要定義多種實現的類型&#xff1a;JAVA有兩種機制&#xff1a;接口和抽象類。這兩種機制都支持為某些實例方法提供實現&#xff0c;但二者有個重要的區別&#xff1a;要實現由抽象類定義的類型&#xff0c;這個類必須是抽象類的子類。因為Java只允許單繼承&#xff0c;對抽象類…

使用SSE實現echarts數據實時更新

區別 SSE 和 WebSocket 原理和實現方式的區別 SSE( Server-Sent Events) SSE 是基于傳統的 HTTP 協議實現的&#xff0c;采用了長輪詢&#xff08;long-polling&#xff09;機制。客戶端通過向服務器發送一個 HTTP 請求&#xff0c;服務器保持連接打開并周期性地向客戶端發送…

內網穿透--利用everything實現目錄映射

免責聲明:本文僅做技術交流與學習... 目錄 來源文章 frp下載網址 為了隱藏: 演示: 1-靶機的everything開啟http服務 2-Linux服務器: 3-靶機windows: 4-最后訪問: 來源文章 滲透測試技巧|Everything的利用 frp下載網址 Release v0.58.1 fatedier/frp GitHub 為了隱…

協程調度模塊

什么是協程和協程調度&#xff1f; 基本概念 協程 協程是一種比線程更輕量級的并發編程結構&#xff0c;它允許在函數執行過程中暫停和恢復執行狀態&#xff0c;從而實現非阻塞式編程。協程又被稱為用戶級線程&#xff0c;這是由于協程包括上下文切換在內的全部執行邏輯都是…

WAIC熱點聚焦|具身智能簡介:AI新浪潮的領跑者

WAIC熱點聚焦|具身智能簡介&#xff1a;AI新浪潮的領跑者 引言 隨著"具身智能"&#xff08;Embodied Intelligence&#xff09;的火熱討論&#xff0c;2024年標志著人機交互新時代的開啟。在大模型技術的推動下&#xff0c;機器人響應語音指令成為現實&#xff0c;…

Linux Rsyslog+LogAnalyzer+MariaDB部署日志服務器

文章目錄 Linux RsyslogLogAnalyzerMariaDB部署日志服務器1 環境準備1.1 服務器端安裝LAMP環境1.2 服務啟動并加入開機啟動1.2.1 Apache1.2.2 MariaDB1.2.3 Php 2 Rsyslog服務端安裝及配置2.1 安裝Rsyslog及Rsyslog連接MySQL的模塊2.2 導入rsyslog-mysql數據庫文件2.3 查看剛導…

深入淺出:npm常用命令詳解與實戰

theme: smartblue npm是什么 npm&#xff08;Node Package Manager&#xff09;是Node.js平臺的默認包管理器&#xff0c;它讓JavaScript開發者能夠輕松地共享、管理和使用彼此編寫的代碼模塊。npm不僅僅是一個安裝工具&#xff0c;它還是一個全面的生態系統&#xff0c;用于發…