笔者将在LeetCode题库中由浅入深理解编写贪心思想相关题目并不断更新自己对该算法的理解与思考
理解与思考:贪心在某种意义上可以借鉴动态规划的思想,但是在求各个状态的过程中可以通过贪心的思想将状态分组,关注的是每组状态中贪心思想选择的最优解
刷题过程中部分算法思路借鉴于官方题解,添加了详细注释用于理解
LeetCode455-分发饼干
典型的贪心算法题,思路很简单,也没什么坑,无非就是选择一个原则去遵循:优先大的饼干满足可满足的最大的胃口或者优先选小的饼干满足可满足的最小胃口
class Solution
{
public:
static bool cmp(int a, int b)
{
return a > b;
}
int findContentChildren(vector<int> &g, vector<int> &s)
{//从大饼干到小饼干的顺序考虑
sort(g.begin(), g.end(), cmp);
sort(s.begin(), s.end(), cmp);
int i = 0, j = 0, res = 0;
for (i = 0; i < s.size(); i++)
{
for (; j < g.size(); j++)
{
if (s[i] >= g[j])
{
j++;
res++;
break;
}
}
}
return res;
}
};
class Solution
{
public:
/*static bool cmp(int a,int b)
{
return a>b;
}*/
int findContentChildren(vector<int> &g, vector<int> &s)
{//从小饼干到大饼干的顺序考虑
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int i = s.size() - 1, j = g.size() - 1, res = 0;
for (; i >= 0; i--)
{
for (; j >= 0; j--)
{
if (s[i] >= g[j])
{
j--;
res++;
break;
}
}
}
return res;
}
};
LeetCode45-跳跃游戏 II
本题的贪心思想某种程度上和递归的思路非常相似,相同处在于计算每一状态的相关参数,不同处在于本题是在一定范围内贪心的保存最优解,并且通过保存的参数影响后续的状态转移。
class Solution
{
public:
int jump(vector<int> &nums)
{
int n = nums.size(), MaxPos = 0, end = 0, res = 0;
for (int i = 0; i < n - 1; i++)
{
MaxPos = max(MaxPos, nums[i] + i);
if (i == end) //end表示上一步可以走到最远的距离,更新end表示新的maxpos是在上一步的基础上加了一步
{
end = MaxPos;
res++;
}
}
return res;
}
};
LeetCode1013-将数组分成和相等的三个部分
拿到这个题开始会想只遍历一次,顺序的假设每个点都是分位点,然后计算后面的部分,本意是想节省求数组和然后三等分的时间消耗,但是忽视了这样做时间复杂度更高。
实际上本题只要信息,注意好边界值,遍历一次就足够得出结果,并不难QAQ
class Solution
{
public:
bool canThreePartsEqualSum(vector<int> &arr)
{
int sum = accumulate(arr.begin(), arr.end(), 0), n = arr.size();
if (sum % 3 != 0)
return false;
int target = sum / 3, cur = 0, i = 0;
while (i < n)//如果这里写小于n-2那么就需要加上后面注释掉的部分代码
{
cur += arr[i];
if (cur == target)
break;
i++;
}
/*if (cur != target) {
return false;
}*/
int j = i + 1;
while (j < n - 1)
{
cur += arr[j];
if (cur == target * 2)
return true;
j++;
}
return false;
}
};
LeetCode134-加油站
刚开始没啥思路,看了眼题解的思路然后自己写了代码,思路中最秒的点就是当前点出发如果不能一圈回到当前点,那么终止前行的那个点至起点的区间内所有点比不可能满足绕一圈的条件,直接越过判别。
其次编程过程中要注意取余和判别条件,官方题解是加入了一个新的变量用于标记,差别不大。
class Solution
{
public:
int canCompleteCircuit(vector<int> &gas, vector<int> &cost)
{
int n = gas.size(), i = 0, cur=0;
while (i < n)
{
if (gas[i] - cost[i] < 0)
{
i++;
continue;
}
else
{
cur = gas[i] - cost[i];
int j = i;
while (cur >= 0)
{
j++;
if (j%n == i)//j%n的原因是走一圈的过程中会路过当前点前面的点,但是不用j%n更新j的值是因为j的值要赋给i,而外层循环的条件是i<n;
return i;
cur = cur - cost[j%n] + gas[j%n];
}
i = j;
}
}
return -1;
}
};
LeetCode135-分发糖果
对于题目所给两个要求,我们先将sugur数组初始值设为1满足第一个条件,对于第二个条件将其分成两个要求,即对于每个高分同学,要比左边低分同学糖果多,由左向右遍历满足此条件;对于每个高分同学,还要比右边低分同学糖果多,由右向左遍历满足此条件。
class Solution
{
public:
int candy(vector<int> &ratings)//左右各遍历一次
{
int len = ratings.size();
vector<int> sugar(len, 1);//保存每个孩子的糖果数量
for (int i = 1; i < len; i++)//左遍历
{
if (ratings[i] > ratings[i - 1])
sugar[i] = sugar[i - 1] + 1;
}
int res = sugar[len - 1];
for (int i = len - 2; i >= 0; i--)//右遍历
{
if (ratings[i] > ratings[i + 1])
sugar[i] = max(sugar[i + 1] + 1, sugar[i]);
//满足当前比右侧相邻大时还要满足比左侧相邻大,所以需要比较后取较大值
res += sugar[i];//右遍历的同时统计糖果总数
}
return res;
}
};
LeetCode179-最大数
问题核心在于对任意两个数进行组合比较确定顺序,依据相对顺序和传递性,排好序的数组就可以直接组合!
class Solution
{
public:
static bool cmp(int &a, int &b)
{
long am = 10, bm = 10; //防止溢出
while (am <= a)
{
am *= 10;
}
while (bm <= b)
{
bm *= 10;
}
return bm * a + b > am * b + a; //根据组合后数字的大小进行排序
}
string largestNumber(vector<int> &nums)
{
sort(nums.begin(), nums.end(), cmp);
if (nums[0] == 0)
return "0";
string res;
for (int x : nums)
{
res += to_string(x);
}
return res;
}
};
LeetCode765-情侣牵手
暴力法直接二重遍历,O(n^2)的时间复杂度居然!居然!
坦白说题解并查集的方法没太看懂,但是问题好像不大?用官方题解去跑了一下还不如暴力遍历来的优秀。。。
class Solution
{
public:
int minSwapsCouples(vector<int> &row)
{
int len = row.size(), cnt = 0;
for (int i = 0; i < len; i += 2)
{
int a = row[i];
int b = (a % 2 == 0) ? a + 1 : a - 1;
//当前对象的情侣的值
for (int j = i + 1; j < len; j++)
//遍历寻找情侣的位置并叫唤
{
if (row[j] == b)
{
if (j == i + 1)
continue; //已经牵手的情况
else
{
swap(row[i + 1], row[j]);
//需要交换位置的情况
cnt++;
}
}
}
}
return cnt;
}
};
先刷这么多,要进行下一个板块了,后续就每日一题随缘碰到贪心再练咯
^ ^