当前位置: 首页 > news >正文

代码随想录---贪心篇

系列文章目录


文章目录

  • 系列文章目录
  • 前言
    • 贪心一般解题步骤
  • 一、2025.5.14
    • 1.学习内容
      • 455. 分发饼干
    • 2.复习内容
      • 51. N 皇后
      • 332. 重新安排行程
      • 37. 解数独
      • 70. 爬楼梯
  • 二、2025.5.17(前两天写的都没保存)
    • 1.学习内容
      • 55. 跳跃游戏
    • 2.复习内容
      • 455. 分发饼干
      • 376. 摆动序列
      • 53. 最大子数组和
      • 122. 买卖股票的最佳时机 II
  • 2025.5.18
    • 1.学习内容
      • 1005. K 次取反后最大化的数组和
    • 2.复习内容
      • 19. 删除链表的倒数第 N 个结点
      • 面试题 02.07. 链表相交
  • 2025.5.19
    • 1.学习内容
      • 134. 加油站
    • 2.复习内容
      • 55. 跳跃游戏
      • 45. 跳跃游戏 II
      • 1005. K 次取反后最大化的数组和
  • 2025.5.20
    • 1.学习内容
      • 135. 分发糖果
    • 2.复习内容
      • 134. 加油站
      • 142. 环形链表 II
  • 2025.5.21
    • 1.学习内容
      • 860. 柠檬水找零
      • 406. 根据身高重建队列
  • 2025.5.22
    • 1.学习内容
      • 452. 用最少数量的箭引爆气球
  • 2025.5.23
    • 1.学习内容
      • 435. 无重叠区间
      • 763. 划分字母区间
  • 2025.5.25
    • 1.学习内容
      • 738. 单调递增的数字
      • 968. 监控二叉树
    • 2.复习内容
      • 56. 合并区间
      • 435. 无重叠区间
      • 763. 划分字母区间


前言

贪心的本质是选择每一阶段的局部最优,从而达到全局最优。

贪心一般解题步骤

①将问题分解为若干个子问题
②找出适合的贪心策略
③求解每一个子问题的最优解
④将局部最优解堆叠成全局最优解


一、2025.5.14

1.学习内容

455. 分发饼干

int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(), g.end());sort(s.begin(), s.end());int index = s.size() - 1;int result = 0;//给胃口最大的孩子分配最大的饼干for (int i = g.size() - 1; i >= 0; i--){if (index >= 0 && s[index] >= g[i]){result++;index--;}}return result;}
int findContentChildren(vector<int>& g, vector<int>& s) {sort(g.begin(), g.end());sort(s.begin(), s.end());int index = 0;//最小的饼干满足胃口最小的孩子for (int i =  0; i < s.size(); i++){if (index < g.size() && s[i] >= g[index]) index++;}return index;}

2.复习内容

51. N 皇后

332. 重新安排行程

37. 解数独

70. 爬楼梯


二、2025.5.17(前两天写的都没保存)

1.学习内容

55. 跳跃游戏

//贪心:这道题主要考虑的就是覆盖范围,如果覆盖范围超过了nums的大小,那么就是true,反之就是false。bool canJump(vector<int>& nums) {int cover = 0;if (nums.size() <= 1) return true;for (int i = 0; i <= cover; i++){cover = max(i + nums[i], cover);if (cover >= nums.size()) return true;}return false;}

2.复习内容

455. 分发饼干

376. 摆动序列

//贪心int wiggleMaxLength(vector<int>& nums) {int preDiff = 0, curDiff = 0;//默认最右端为峰值或者谷值int result = 1;for (int i = 0; i < nums.size() - 1; i++){curDiff = nums[i + 1] - nums[i];if ((preDiff >= 0 && curDiff < 0) || (preDiff <= 0 && curDiff > 0)){result++;preDiff = curDiff;}}return result;}
//动态规划int wiggleMaxLength(vector<int>& nums) {//状态标识:i表示nums的第i个元素,j表示将该点作为0:波谷,1:波峰,dp[i][j]表示以i结尾的摆动序列的长度int dp[1010][2];//使用memset初始化二维数组的时候,只能将其值赋值为-1或0;memset(dp, 0, sizeof(dp));dp[0][0] = dp[0][1] = 1;// for (int i = 0; i < nums.size(); i++)// {//     dp[i][0] = dp[i][1] = 1;// }for (int i = 1; i < nums.size(); i++){//也可以在初始化dp数组的时候,利用for循环将所有的值设为1;dp[i][0] = dp[i][1] = 1;for (int j = 0; j <= i; j++){if (nums[i] < nums[j]) dp[i][0] = max(dp[j][1] + 1, dp[i][0]);if (nums[i] > nums[j]) dp[i][1] = max(dp[j][0] + 1, dp[i][1]);}}return max(dp[nums.size() - 1][0], dp[nums.size() - 1][1]);}

53. 最大子数组和

//贪心int maxSubArray(vector<int>& nums) {int count = 0, result = nums[0];for (int i = 0; i < nums.size(); i++){count += nums[i];//更新result必须在count判断之前,因为如果全是负数的话,如果颠倒了这个顺序,结果就是0,而不是最大负数result = max(result, count);if (count < 0) count = 0;}return result;}
int maxSubArray(vector<int>& nums) {//动态规划if (nums.size() == 0) return 0;vector<int> dp(nums.size(), 0);dp[0] = nums[0];int result = dp[0];for (int i = 1; i < nums.size(); i++){//状态转移方程dp[i] = max(dp[i - 1] + nums[i], nums[i]);result = max(result, dp[i]);}return result;}

122. 买卖股票的最佳时机 II

//贪心:这道题最重要的一点就是知道利润是可以分解的://假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。//相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。//此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!int maxProfit(vector<int>& prices) {int result = 0;for (int i = 1; i < prices.size(); i++){result += max(0, prices[i] - prices[i - 1]);}return result;}

2025.5.18

1.学习内容

1005. K 次取反后最大化的数组和

//贪心:第一次贪心,将绝对值大的负值优先转变为正值;//第二次贪心:最后还有反转次数的话,就将最小的值反复反转,就会得到最大的和//要是先第一个贪心,就需要对按照绝对值的大小对nums进行排序,然后根据k值将负值转变为正值static bool cmp(const int &a, const int &b){return abs(a) > abs(b);}int largestSumAfterKNegations(vector<int>& nums, int k) {int result = 0;sort(nums.begin(), nums.end(), cmp);for (int i = 0; i < nums.size(); i++){if (nums[i] < 0 && k > 0){nums[i] *= -1;k--;}}//第二次贪心if (k % 2) nums[nums.size() - 1] *= -1;for (int x : nums) result += x;return result;}

2.复习内容

19. 删除链表的倒数第 N 个结点

//利用快慢节点来实现一次遍历从而删除节点的效果
ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummy_Head = new ListNode(0);dummy_Head -> next = head;ListNode* pre = dummy_Head;ListNode* cur = dummy_Head;while (n-- && pre != nullptr){pre = pre -> next;}while (pre -> next != nullptr){pre = pre -> next;cur = cur -> next; }cur -> next = cur -> next -> next;//一般不要直接返回head,而是返回dummy_Head -> next;return dummy_Head -> next;}

面试题 02.07. 链表相交

public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {int lenA = 0, lenB = 0;ListNode* curA = headA, *curB = headB;while (curA){lenA++;curA = curA -> next;}while (curB){lenB++;curB = curB -> next;}if (lenA < lenB){swap(lenA, lenB);swap(headA, headB);}curA = headA;curB = headB;int diff = lenA - lenB;while (diff--){curA = curA -> next;}while (curA){if (curA == curB) return curA;curA = curA -> next;curB = curB -> next;}return nullptr;}

2025.5.19

1.学习内容

134. 加油站

//暴力做法 (会超时)int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {for (int i = 0; i < gas.size(); i++){int rest = gas[i] - cost[i];int index = (i + 1) % gas.size();while (index != i && rest >= 0){rest += (gas[index] - cost[index]);index = (index + 1) % gas.size();}if (rest >= 0 && index == i) return i;}return -1;}
//贪心算法:只有考虑全局最优,没有考虑局部最优int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int min = 0, sum = 0;for (int i = 0; i < cost.size(); i++){int rest = gas[i] - cost[i];sum += rest;if (sum < min) min = sum;}if (sum < 0) return -1;if (min >= 0) return 0;for (int i = cost.size() - 1; i >= 0; i--){int rest = gas[i] - cost[i];min += rest;if (min >= 0) return i;}return -1;}
//贪心思路:gas[i] - cost[i](i - j)的和如果为负数,代表从i-j这段距离走不通,因此就要从j + 1开始往后寻找//如果到最后总和为负值,那么就返回-1,反之就是我们之前定义的值。int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int curSum = 0, totalSum = 0;int start = 0;for (int i = 0; i < gas.size(); i++){totalSum += gas[i] - cost[i];curSum += gas[i] - cost[i];if (curSum < 0){start = i + 1;curSum = 0;}}if (totalSum < 0) return -1;return start;}

2.复习内容

55. 跳跃游戏

45. 跳跃游戏 II

1005. K 次取反后最大化的数组和

2025.5.20

1.学习内容

135. 分发糖果

//本题我采用了两次贪心的策略://一次是从左到右遍历,只比较右边孩子评分比左边大的情况。//一次是从右到左遍历,只比较左边孩子评分比右边大的情况。//这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。int candy(vector<int>& ratings) {//初始化数组vector<int> candies(ratings.size(), 1);//第一遍从左往右遍历for (int i = 1; i < ratings.size(); i++){if (ratings[i] > ratings[i - 1]) candies[i] = candies[i - 1] + 1;}//第二次遍历从右往左遍历,并且取candies[i]和(candies[i + 1] + 1/1)的最大值for (int i = ratings.size() - 2; i >= 0; i--){if (ratings[i + 1] < ratings[i]) candies[i] = max(candies[i + 1] + 1, candies[i]);}int sum = 0;for (int a : candies){sum += a;}return sum;}

2.复习内容

134. 加油站

142. 环形链表 II

ListNode *detectCycle(ListNode *head) {ListNode* slow = head, *fast = head;while (fast != nullptr && fast -> next != nullptr){slow = slow -> next;fast = fast -> next -> next;//快慢指针相遇:相遇的节点是环形内部的节点,但是这块需要找的是入环的第一个节点//但是我们根据(head - 入环的第一个节点)= (slow - 入环的第一个节点) + 环形内的节点个数 * n(n = 1,2,3...),那么让他们同时next,得到的第一个相同的节点就是ansif (slow == fast){ListNode *index1 = slow, *index2 = head;while (index1 != index2){index1 = index1 -> next;index2 = index2 -> next;}return index1;}}return nullptr;}

2025.5.21

1.学习内容

860. 柠檬水找零

//有如下三种情况://情况一:账单是5,直接收下。//情况二:账单是10,消耗一个5,增加一个10//情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5//贪心的情况只要考虑第三种情况:因为美元10只能给账单20找零,而美元5可以给账单10和账单20找零,美元5更万能!//所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。bool lemonadeChange(vector<int>& bills) {int five = 0, ten = 0;for (int i = 0; i < bills.size(); i++){if (bills[i] == 5){five++;}else if (bills[i] == 10){if (five <= 0) return false;five--;ten++;}else{if (ten > 0 && five > 0){ten--;five--;}else if (five >= 3){five -= 3;}else return false;}}return true;}

406. 根据身高重建队列

//贪心://局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性//全局最优:最后都做完插入操作,整个队列满足题目队列属性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底层使用链表实现的list<vector<int>> que;for (int i = 0; i < people.size(); i++){int index = people[i][1];//vector的insert操作是比较耗时的,因为如果插入后数组超过原数组大小,就会扩容,从而将数据拷贝被新的vector中,所以可以使用链表代替vector(iterator的用法我还是不是很熟悉,看看博客去)std::list<vector<int>>::iterator it = que.begin();while (index--){it++;}que.insert(it, people[i]);}return vector<vector<int>>(que.begin(), que.end());}

2025.5.22

1.学习内容

452. 用最少数量的箭引爆气球

//贪心:感觉和合并区间类似,比较好理解,对于这个问题,我刚开始一直有一个困惑:会不会前三个有交集,第三个和第四个有交集,从而不知道先射哪个,但是转念一想,好像按照这两种情况箭的数量没有什么区别,那就按照1好了,代码逻辑还清晰。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];}int findMinArrowShots(vector<vector<int>>& points) {//最少需要一只箭int arrowNum = 1;sort(points.begin(), points.end(), cmp);for (int i = 1; i < points.size(); i++){if (points[i][0] > points[i - 1][1]) arrowNum++;else points[i][1] = min(points[i - 1][1], points[i][1]);}return arrowNum;}

2025.5.23

1.学习内容

435. 无重叠区间

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];}int eraseOverlapIntervals(vector<vector<int>>& intervals) {sort (intervals.begin(), intervals.end(), cmp);int result = 1;for (int i = 1; i < intervals.size(); i++){if (intervals[i][0] < intervals[i - 1][1]){intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);}else result++;}return (intervals.size() - result);}

763. 划分字母区间

vector<int> partitionLabels(string s) {int hash[27];for (int i = 0; i < s.size(); i++){hash[s[i] - 'a'] = i;}int left = 0, right = 0;vector<int> result;for (int i = 0; i < s.size(); i++){right = max(right, hash[s[i] - 'a']);if (i == right){result.push_back(right - left + 1);left = right + 1;}}return result;}

2025.5.25

1.学习内容

738. 单调递增的数字

//贪心int monotoneIncreasingDigits(int n) {string num_Str = to_string(n);//如果下面的count没有被赋值,那么第二个循环就不会执行int count = num_Str.size();for (int i = num_Str.size() - 2; i >= 0; i--){if (num_Str[i + 1] < num_Str[i]){count = i;//这段代码一定要放在这块,因为变9可以之后再操作,但是比较的话应该是和-1之后的数进行比较(其实按照思路来说,如果前面发现了非单调递增的,那么更新所有的数,但是更新这些数变成9貌似对后面的比较没有太大的影响,所以每次只要将i的数-1即可,这种对比较结果还是有影响的)num_Str[i]--;}}for (int i = num_Str.size() - 1; i > count; i--){num_Str[i] = '9';}return stoi(num_Str);}

968. 监控二叉树

int result = 0;//由于要从叶子节点往上遍历才能使用更少的摄像头,所以我们使用后序遍历//三种状态: 0:没有被覆盖, 1:有摄像头, 2:被覆盖int traversal(TreeNode* node){//如果节点为空,表示已经被覆盖if (node == nullptr) return 2;//迭代int left = traversal(node -> left);int right = traversal(node -> right);//处理逻辑//如果左右节点都已经被覆盖,那么该节点就应该是未被覆盖的情况if (left == 2 && right == 2) return 0;//如果左右节点有一个未被覆盖,那么该节点就应该安摄像头if (left == 0 || right == 0){result++;return 1;}//如果左右节点有一个有摄像头(因为有一个未被覆盖的情况已经在上面考虑过了)if (left == 1 || right == 1) return 2;//肯定不会有-1的情况return -1;}int minCameraCover(TreeNode* root) {if (root == nullptr) return 0;if (traversal(root) == 0) result++;return result;}

2.复习内容

56. 合并区间

435. 无重叠区间

763. 划分字母区间

相关文章:

  • 企业为什么要建立网站百度网盘人工客服电话多少
  • 做网站所需的知识技能山东关键词快速排名
  • 东莞外贸公司建网站百度网站推广价格查询
  • 网站设计与网页设计的区别百度权重5的网站能卖多少钱
  • 合伙建网站武汉推广系统
  • 一家专门做开网店的网站网站快速排名优化哪家好
  • IS-IS报文
  • YOLO11解决方案之区域追踪探索
  • 华为OD机试真题——欢乐周末 (2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • GAMES104 Piccolo引擎搭建配置
  • 显示docker桌面,vnc远程连接docker
  • LeetCode 1040.移动石子直到连续II
  • 【公式】MathType公式上浮或下沉
  • 汉诺塔超级计算机数据区结构和源代码详细设计
  • C++语言入门————高精度计算
  • ubuntu下nginx
  • 如何在Windows右键菜单中添加“以管理员身份运行CMD”的选项(含图标设置)
  • 第十六篇:真正的学习,系统分析师考后总结
  • 【公式】批量添加MathType公式编号
  • Java单例模式:懒汉模式详解
  • el-input 按回车失去焦点
  • C#、C++、Java、Python 选择哪个好
  • PCB设计-立创
  • Android事件分发学习总结
  • 嵌入式自学第二十七天
  • 【硬件测试】基于FPGA的BPSK+卷积编码Viterbi译码系统开发,包含帧同步,信道,误码统计,可设置SNR