2020年7月5日, LeetCode第196场周赛个人记录。第一次参加leetcode周赛,4道题目完成3道,中国排名163,全球排名377。对我个人来说算是个普通成绩吧。以后打算每两周参加一次周赛,同时利用博客记录下每次的成绩,同时分享一下对于题目的个人想法和感受。
给你一个数字数组 arr 。
如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。
如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。
示例 1:
输入:arr = [3,5,1]
输出:true
解释:对数组重新排序得到 [1,3,5] 或者 [5,3,1] ,任意相邻两项的差分别为 2 或 -2 ,可以形成等差数列。
示例 2:
输入:arr = [1,2,4]
输出:false
解释:无法通过重新排序得到等差数列。
题目链接1502.判断能否形成等差数列
第一题算是一道送分题,由等差数列的性质,等差数列相邻两项的差为定值,判断一个数组能否形成等差数列,可以直接sort排序,再遍历数组判断相邻项的差是否为定值。
class Solution {
public:
bool canMakeArithmeticProgression(vector<int>& arr) {
sort(arr.begin(), arr.end());
int differ = arr[1] - arr[0];
for (int i = 2; i < arr.size(); ++i)
if (arr[i] - arr[i - 1] != differ)
return false;
return true;
}
};
时间复杂度:时间主要消耗在sort排序中,sort使用快排,时间复杂度为O(nlogn)。
空间复杂度:O(1)
有一块木板,长度为 n 个 单位 。一些蚂蚁在木板上移动,每只蚂蚁都以 每秒一个单位 的速度移动。其中,一部分蚂蚁向 左 移动,其他蚂蚁向 右 移动。
当两只向 不同 方向移动的蚂蚁在某个点相遇时,它们会同时改变移动方向并继续移动。假设更改方向不会花费任何额外时间。
而当蚂蚁在某一时刻 t 到达木板的一端时,它立即从木板上掉下来。
给你一个整数 n 和两个整数数组 left 以及 right 。两个数组分别标识向左或者向右移动的蚂蚁在 t = 0 时的位置。请你返回最后一只蚂蚁从木板上掉下来的时刻。
题目链接:1503.所有蚂蚁掉下来前的最后一刻
比赛的时候被这道题卡了好久,一直到结束都没想出来,一直感觉很可惜,困难的题目都做出来了,被一道中等难度的题卡住。这道题其实很简单,硬要说的话其实是一道脑经急转弯,不然被相遇掉头给迷惑到,因为每一只蚂蚁是一样的,相遇掉头和相遇穿过本质上是相同的,所以这道题就是求哪知蚂蚁离木板边缘最远,我们只要遍历两个数组就可以得出答案。
class Solution {
public:
int getLastMoment(int n, vector<int>& left, vector<int>& right) {
int maxVal = 0;
for (auto x: left)
maxVal = max(x, maxVal);
for (auto x: right)
maxVal = max(n - x, maxVal);
return maxVal;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
给你一个只包含 0 和 1 的 rows * columns 矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。
题目链接1504.统计全1子矩形
这道题也算是运气好,在周赛前正好遇到了相同的题目所以虽然题目很难,但是思路还是很清晰的。看到这种题目首先想到的是暴力法,对遍历每一个元素,以这个元素为矩形的左上角,同时枚举所有可能的长和宽,判断这个矩形是否满足全1这个条件。但是这种方法的时间复杂度为O( m³ n³),m和n是矩阵的行和列,题目中m和n最高可达150,数据规模就达到了10^13 数量级,肯定会超时的。这里提一下,一般来说C/C++的数据规模在10 ^7,超过就会超时。这样就要考虑时间上的优化。这里介绍柱状图方法,时间复杂度可以达到O(m²n)。
首先我们把每个元素当成矩形的右下角,之所以是右下角,是为了让循环从下标0开始递增,符合我们的思路。我们利用动态规划的思想求出每一个元素左侧连续的1的个数,我们用dp[i][j]表示第i行j列的元素连续1的个数。我们可以在O(mn)的复杂度下完成。之后,对每个元素,我们遍历它可能的宽度,为了便于理解,我们用高度来称呼宽度,想象矩形是立起来的。当高度是1时,以当前元素为右下角形成的子矩形的个数就是dp[i][j],这很好理解,这里就不多赘述了,当高度变为2时,以当前元素为右下角形成的子矩形的个数为 min(dp[i][j], dp[i - 1][j],这里可能有点难以理解,但是让我们来看一个例子。
0 1 1
1 1 1
我们以第2行第3列的这个元素为例,dp[2][3] = 3, dp[1][3] = 2,首先这个元素为子矩阵的右下角,高度为1时,dp[2][3]就是可能形成的矩阵个数,当高度为2时,我们可以把高度为2的矩阵当成两个高度为1的矩阵的组合,所以个数为两个dp中小的那一个。由此类推,当高度增加时,我们要继续取最小的dp。
class Solution {
public:
int numSubmat(vector<vector<int>>& mat) {
if (mat.empty()) // 判空
return 0;
int row = mat.size(), line = mat[0].size();
int sum = 0;
vector<vector<int>>dp(row, vector<int>(line + 1, 0)); // 多初始化一列,便于循环中统一计算
for (int i = 0; i < row; ++i)
{
for (int j = 1; j <= line; ++j)
{
if (mat[i][j - 1] == 0)
dp[i][j] = 0;
else
dp[i][j] = dp[i][j - 1] + 1; //计算dp
int minDp = dp[i][j]; //储存最小的dp
for (int height = i; height >= 0 && mat[height][j - 1] != 0; --height) //遍历height
{
minDp = min(dp[height][j], minDp);
sum += minDp;
}
}
}
return sum;
}
};
给你一个字符串 num 和一个整数 k 。其中,num 表示一个很大的整数,字符串中的每个字符依次对应整数上的各个 数位 。
你可以交换这个整数相邻数位的数字 最多 k 次。
请你返回你能得到的最小整数,并以字符串形式返回。
题目链接:P1505.最多K次交换相邻数位后得到的最小整数
这道题难度不大,首先看到交换相邻的数位,这应该能让我们想到应该算我我们遇见的第一个算法–冒泡排序法。在仔细观察,这道题本质上就是一个限制了交换次数的冒泡排序,按照冒泡排序法的思想,外循环控制当前判断的是哪一位数,内循环寻找出最小的数。只不过普通的冒泡排序法内循环是可以遍历整个数组,而现在我们只能遍历最多k次,所以我们只要多加上一个判断就可以了。
完成了大体的思路后,我们有一些细节要处理,首先我们知道,冒泡排序的处理次数是n * (n - 1) / 2 - 1,所以当k大于这个数时,实际上就是进行了一次冒泡排序,我们可以直接sort然后返回。另外就是交换,我们不需要真的进行交换,我们只要知道,交换后的结果相当于把最后一个数提到了最开头,我们可以用insert和erase实现。最后注意一下判断的边界条件,是否需要加等号。
class Solution {
public:
string minInteger(string num, int k) {
int size = num.size();
if (k >= size * (size - 1) / 2 - 1) //当k过大时,可以直接sort返回
{
sort(num.begin(), num.end());
return num;
}
int head = 0; // 记录当前在查找哪一位的最小值
while (k > 0 && head < size) //外循环
{
int minIndex = head; //寻找最小的数的下标
for (int i = head; i < size && i - head <= k; ++i)
{
if (num[i] < num[minIndex])
minIndex = i;
}
if (minIndex != head) //当head就是最小的数时,移动没有意义
{
num.insert(num.begin() + head, num[minIndex]);
num.erase(num.begin() + minIndex + 1);
k -= minIndex - head;
}
++head;
}
return num;
}
};
第一次参加周赛,也算是知道了人外有人,天外有天,我4道题总用时75分钟,平均下来一道题也有接近20分钟,而且还有一道题目没有做出来,而高排名的大佬4道题一共只用了20分钟。这次周赛算是对我这一个月刷题的总结,希望以后的自己能多多努力。