最近在leetcode刷到一道,个人觉得很不错的前缀和题目
题目 找到需要补充粉笔的学生编号
一个班级里有 n 个学生,编号为 0 到 n - 1 。每个学生会依次回答问题,编号为 0 的学生先回答,然后是编号为 1 的学生,以此类推,直到编号为 n - 1 的学生,然后老师会重复这个过程,重新从编号为 0 的学生开始回答问题。
给你一个长度为 n 且下标从 0 开始的整数数组 chalk 和一个整数 k 。一开始粉笔盒里总共有 k 支粉笔。当编号为 i 的学生回答问题时,他会消耗 chalk[i] 支粉笔。如果剩余粉笔数量 严格小于 chalk[i] ,那么学生 i 需要 补充 粉笔。
请你返回需要 补充 粉笔的学生 编号 。
以下为题目给出的两个示例
附题目链接:https://leetcode-cn.com/problems/find-the-student-that-will-replace-the-chalk/
解题思路
首先,拿到这道题,最直接的思路就是不断遍历数组,每次循环时直接 k -= chalk[i];但这样如果数组很小,而k很大时,我们需要遍历很多次,这种暴力的解法会使得时间复杂度过大,甚至无法通过。因此,诞生了最佳的模拟解法:
模拟解法
我们不妨对其数组求和,然后将初始粉笔数k 对总和取余,然后再进行一次遍历数组,这样子的时间复杂度就是O(2*n) = O(n),n表示数组的尺寸。这样子最多循环遍历两次数组即可完成 代码如下
class Solution {
public:
int chalkReplacer(vector<int>& chalk, int k) {
int n = chalk.size();
long long sum = 0;
for(int i = 0; i < n; i++)
{
sum += chalk[i];
}
k %= sum;
int ans = 0;
for(int i = 0; i < n; i++)
{
if(k < chalk[i])
{
ans = i;
break;
}
k -= chalk[i];
}
return ans;
}
};
个人觉得代码较简单,所以没有做注释(其实就是当时懒) 但是还可以比这种方法更快的解法,当然,不是那种面向结果(指用if语句涵盖所有的测试案例)的解法,而是我们今天的主角:
前缀和+二分查找
思路:我们不妨先便历一次数组,每次求数组的前缀和,但是这样会有一个问题,其实前面的解法,小伙伴们如果细心的话就会发现我对于sum变量是用了long long 超长整型的,因为在加和的过程中可能会出现结果超过int的范围,但是我们的初始粉笔数k是int型,也就是如果出现了大于int的前缀和,那一定会比k大,进一步如果前缀和严格大于k,那么当前下标即为所求编号。而如果退一步,如果最后前缀和依然比k小,那不妨将k也和最后的前缀和(相当于数组总和)做取余操作,然后再在数组遍历查找,但是这样子不就和模拟解法一样了吗?这时我们可以引入二分思想来查找数组下标,使得复杂度再一次降低。 代码(完整,涵盖输入)如下
#include<iostream>
#include<vector>
using namespace std;
class Solution
{
public:
int binary_search(vector<int>& nums, int k)
{
int l = 0, r = nums.size() - 1;
while (l < r)
{
int m = l + (r - l) / 2;
if (nums[m] == k)
{
return m + 1;
}
else if (nums[m] < k)
{
l = m + 1;
}
else if (nums[m] > k)
{
r = m;
}
}
return r;
}
int chalkReplacer(vector<int>& chalk, int k)
{
int n = chalk.size();
if (k < chalk[0])
{
return 0;
}
for (int i = 1; i < n; i++)
{
chalk[i] += chalk[i - 1];
if (chalk[i] > k)
{
return i;
}
}
k %= chalk[n - 1];
return binary_search(chalk, k);
}
};
int main()
{
Solution s1;
vector<int> chalk;
char ch;
int num;
cin >> ch;
if (ch == '[')
{
while (ch != ']')
{
cin >> num >> ch;
chalk.push_back(num);
}
}
int k;
cin >> k;
cout << s1.chalkReplacer(chalk, k) << endl;
return 0;
}
愿你能从本文有所收获
|