题源:LeetCode
Day14:1219. 黄金矿工
你要开发一座金矿,地质勘测学家已经探明了这座金矿中的资源分布,并用大小为 m * n 的网格 grid 进行了标注。每个单元格中的整数就表示这一单元格中的黄金数量;如果该单元格是空的,那么就是 0。
为了使收益最大化,矿工需要按以下规则来开采黄金:
- 每当矿工进入一个单元,就会收集该单元格中的所有黄金。
- 矿工每次可以从当前位置向上下左右四个方向走。
- 每个单元格只能被开采(进入)一次。
- 不得开采(进入)黄金数目为 0 的单元格。
- 矿工可以从网格中 任意一个 有黄金的单元格出发或者是停止。
示例 1:
输入:grid = [[0,6,0],[5,8,7],[0,9,0]]
输出:24
解释:
[[0,6,0],
[5,8,7],
[0,9,0]]
一种收集最多黄金的路线是:9 -> 8 -> 7。
示例 2:
输入:grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]]
输出:28
解释:
[[1,0,7],
[2,0,6],
[3,4,5],
[0,3,0],
[9,0,20]]
一种收集最多黄金的路线是:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7。
提示:
1 <= grid.length, grid[i].length <= 15
0 <= grid[i][j] <= 100
最多 25 个单元格中有黄金。
思路:
深搜。
边界条件:上下左右,要么走过,要么是0,要么越界。(可以把走过的设定为0,就把条件要么走过要么是0合并了)
归时比较最大值。如果大于最大值,则更新。
class Solution {
public:
int maxRes = 0;
void dfs(int x, int y,int res, vector<vector<int>>& grid){
int t = grid[x][y];
res += grid[x][y];
if(x - 1 >= 0 && grid[x - 1][y]){
grid[x][y] = 0;
dfs(x - 1, y, res, grid);
grid[x][y] = t;
}
if(x + 1 < grid.size() && grid[x + 1][y] ){
grid[x][y] = 0;
dfs(x + 1, y, res , grid);
grid[x][y] = t;
}
if(y + 1 < grid[0].size() && grid[x][y + 1] ){
grid[x][y] = 0;
dfs(x, y + 1, res , grid);
grid[x][y] = t;
}
if(y - 1 >= 0 && grid[x][y - 1] ){
grid[x][y] = 0;
dfs(x, y - 1, res , grid);
grid[x][y] = t;
}
if(res > maxRes) maxRes = res;
return ;
}
int getMaximumGold(vector<vector<int>>& grid) {
for(int i = 0; i < grid.size(); i++){
for(int j = 0; j < grid[i].size(); j++){
if(grid[i][j])
dfs(i, j, 0, grid);
}
}
return maxRes;
}
};
Day15:1748. 唯一元素的和
给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。
请你返回 nums 中唯一元素的 和 。
示例 1:
输入:nums = [1,2,3,2]
输出:4
解释:唯一元素为 [1,3] ,和为 4 。
示例 2:
输入:nums = [1,1,1,1,1]
输出:0
解释:没有唯一元素,和为 0 。
示例 3 :
输入:nums = [1,2,3,4,5]
输出:15
解释:唯一元素为 [1,2,3,4,5] ,和为 15 。
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 100
思路:哈希表。遍历。累加恰好出现一次的元素值。
class Solution {
public:
int sumOfUnique(vector<int>& nums) {
unordered_map<int, int> cnt;
for(int num : nums){
cnt[num]++;
}
int ans = 0;
for(auto &[num, c] : cnt){
if(c == 1)
ans += num;
}
return ans;
}
};
Day16:1405. 最长快乐字符串
如果字符串中不含有任何 ‘aaa’,‘bbb’ 或 ‘ccc’ 这样的字符串作为子串,那么该字符串就是一个「快乐字符串」。
给你三个整数 a,b ,c,请你返回 任意一个 满足下列全部条件的字符串 s:
- s 是一个尽可能长的快乐字符串。
- s 中 最多 有a 个字母 ‘a’、b 个字母 ‘b’、c 个字母 ‘c’ 。
- s 中只含有 ‘a’、‘b’ 、‘c’ 三种字母。
如果不存在这样的字符串 s ,请返回一个空字符串 “”。
示例 1:
输入:a = 1, b = 1, c = 7
输出:“ccaccbcc”
解释:“ccbccacc” 也是一种正确答案。
示例 2:
输入:a = 2, b = 2, c = 1
输出:“aabbc”
示例 3:
输入:a = 7, b = 1, c = 0
输出:“aabaa”
解释:这是该测试用例的唯一正确答案。
提示:
0 <= a, b, c <= 100
a + b + c > 0
思路:贪心
题目要求找到最长的快乐字符串,且快乐字符串中不含有三个连续相同的字母。
为了找到最长的字符串,我们可以使用如下贪心策略:
- 尽可能有限使用当前数量最多的字母,因为最后同一种字母剩余的越多,越容易出现字母连续相同的情况。如果构建完成最长的快乐字符串后还存在剩余未选择的字母,则剩余的字母一定为同一种字母且该字母的总数量最多。
- 依次从当前数量最多的字母开始尝试,如果发现加入当前字母会导致出现三个连续相同字母,则跳过当前字母,直到我们找到可以添加的字母为止。实际上每次只会在数量最多和次多的字母中选择一个。
- 如果尝试所有的字母都无法添加,则直接退出,此时构成的字符串即为最长的快乐字符串。
class Solution {
public:
string longestDiverseString(int a, int b, int c) {
string res;
vector<pair<int, char> > arr = {{a, 'a'}, {b, 'b'}, {c, 'c'}};
while(true){
sort(arr.begin(), arr.end(), [](const pair<int, char> & p1, const pair<int, char> & p2){
return p1.first > p2.first;
});
bool hasNext = false;
for(auto & [freq, ch] : arr){
int m = res.size();//结果字符串的长度
if(freq <= 0) break;
if(m >= 2 && res[m - 2] == ch && res[m - 1] == ch) continue;//如果字符串的长度大于2 并且 前两个都是这个字符,那就不再安排这个字符
hasNext = true;//可以安排下一个字符
res.push_back(ch);
freq--;
break;
}
if(!hasNext) break;//如果不可以安排下一个字符,就退出
}
return res;
}
};
Day17:1001. 网格照明
在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态。
给你一个由灯的位置组成的二维数组 lamps ,其中 lamps[i] = [rowi, coli] 表示 打开 位于 grid[rowi][coli] 的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。
当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 。
另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] 。对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的,则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] ,关闭 位于单元格 grid[rowj][colj] 上及相邻 8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯。
返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果,1 表示照亮,0 表示未照亮。
示例 2:
输入:n = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,1]]
输出:[1,1]
示例 3:
输入:n = 5, lamps = [[0,0],[0,4]], queries = [[0,4],[0,1],[1,4]]
输出:[1,1,0]
提示:
1 <= n <= 109
0 <= lamps.length <= 20000
0 <= queries.length <= 20000
lamps[i].length == 2
0 <= rowi, coli < n
queries[j].length == 2
0 <= rowj, colj < n
试了一下并查集,不太行。因为并查集无法简单表示这个集合成员出现的次数。这种情况,用哈希表比较好。
using LL = long long;
int dir[][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}};
class Solution {
public:
vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
// 存储灯所在行、列、主对角线、副对角线的光的数量
unordered_map<int,int> row,col,left,right;
// 用来存储灯的坐标点的,便于在询问的时候,删除光的八个方向包括该光本身是否存在灯,然后将存在的灯熄灭
set<LL> point;
LL N=n;
auto change=[&](int x,int y,int c){
row[x]+=c,col[y]+=c,right[x-y]+=c,left[x+y]+=c;
};
// 遍历灯:存储以上数据结构
for(vector<int>& l:lamps){
int x=l[0],y=l[1];
// 重复点直接跳过
if(point.count(x*N+y))continue;
point.insert(x*N+y);
change(x,y,1);
}
vector<int> res;
for(vector<int>& q:queries){
int x=q[0],y=q[1];
// 判断(x,y)所在行、列、对角线是否存在光
if(row[x]||col[y]||right[x-y]||left[x+y])res.push_back(1);
else {res.push_back(0);continue;}
// 然后将光所在点的8个方向包括该点本身的所有灯进行关闭
for(int i=0;i<9;++i){
int nx=x+dir[i][0],ny=y+dir[i][1];
if(nx<0||ny<0||nx>=n||ny>=n)continue;
// 灯存在,则进行删除灯,并关闭行列对角线上的光
if(point.count(nx*N+ny)){
point.erase(nx*N+ny);
change(nx,ny,-1);
}
}
}
return res;
}
};
Day18:2006. 差的绝对值为 K 的数对数目
给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。
|x| 的值定义为:
- 如果 x >= 0 ,那么值为 x 。
- 如果 x < 0 ,那么值为 -x 。
示例 1:
输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]
- [1,2,2,1]
示例 2:
输入:nums = [1,3], k = 3
输出:0
解释:没有任何数对差的绝对值为 3 。
示例 3:
输入:nums = [3,2,1,5,4], k = 2
输出:3
解释:差的绝对值为 2 的数对为:
- [3,2,1,5,4]
- [3,2,1,5,4]
- [3,2,1,5,4]
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
1 <= k <= 99
思路:边遍历边记录某个数出现次数。
进行一次遍历,遍历时下标代表j。对每一个j,我们需要知道在这个j之前的符合条件的i的个数,即满足| nums[i] - nums[j] | = k 的 i 的个数,亦满足nums[i] = nums[j] + k,或nums[i] = nums[j] - k的i的个数。然后将nums[j]插入到哈希表中。
使用哈希表可以在O(1)的时间内统计出这样的个数,因此在遍历时可以使用一个哈希表来维护不同数值的频率,并统计符合条件的数对总数。
感觉没太懂。
class Solution {
public:
int countKDifference(vector<int>& nums, int k) {
int res = 0, n = nums.size();
unordered_map<int, int> cnt;
for(int j = 0; j < n; j++){
res += (cnt.count(nums[j] - k) ? cnt[nums[j] - k] : 0);
res += (cnt.count(nums[j] + k) ? cnt[nums[j] + k] : 0);
cnt[nums[j]]++;
}
return res;
}
};
复杂度分析
- 时间复杂度:O(n),其中 n 为数组 nums 的长度。我们仅使用了一次遍历来寻找所有符合条件的数对。
- 空间复杂度:O(n)。哈希表消耗了 O(n) 的空间。
Day19:1447. 最简分数
给你一个整数 n ,请你返回所有 0 到 1 之间(不包括 0 和 1)满足分母小于等于 n 的 最简 分数 。分数可以以 任意 顺序返回。
示例 1:
输入:n = 2
输出:[“1/2”]
解释:“1/2” 是唯一一个分母小于等于 2 的最简分数。
示例 2:
输入:n = 3
输出:[“1/2”,“1/3”,“2/3”]
示例 3:
输入:n = 4
输出:[“1/2”,“1/3”,“1/4”,“2/3”,“3/4”]
解释:“2/4” 不是最简分数,因为它可以化简为 “1/2” 。
示例 4:
输入:n = 1
输出:[]
提示:1 <= n <= 100
由于要保证分数在 (0,1)范围内,我们可以枚举分母 denominator∈[2,n] 和分子 numerator∈[1,denominator),若分子分母的最大公约数为 1,则我们找到了一个最简分数。
__gcd(x,y)函数
用于求x,y的最大公约数。x,y不能是浮点数
头文件:#include< algorithm>
class Solution {
public:
vector<string> simplifiedFractions(int n) {
vector<string> ans;
for (int denominator = 2; denominator <= n; ++denominator) {
for (int numerator = 1; numerator < denominator; ++numerator) {
if (__gcd(numerator, denominator) == 1) {
ans.emplace_back(to_string(numerator) + "/" + to_string(denominator));
}
}
}
return ans;
}
};
求最大公约数
辗转相除法:
int gcd(int x,int y){
int r;
while (a%b!=0){
r=a%b;
a=b;
b=r;
}
return b;
}
int gcd(int a,int b) {
return b>0 ? gcd(b,a%b):a;
}
Day20:1984. 学生分数的最小差值
给你一个 下标从 0 开始 的整数数组 nums ,其中 nums[i] 表示第 i 名学生的分数。另给你一个整数 k 。
从数组中选出任意 k 名学生的分数,使这 k 个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。
示例 1:
输入:nums = [90], k = 1
输出:0
解释:选出 1 名学生的分数,仅有 1 种方法:
- [90] 最高分和最低分之间的差值是 90 - 90 = 0
可能的最小差值是 0
示例 2:
输入:nums = [9,4,1,7], k = 2
输出:2
解释:选出 2 名学生的分数,有 6 种方法:
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 4 = 5
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 1 = 8
- [9,4,1,7] 最高分和最低分之间的差值是 9 - 7 = 2
- [9,4,1,7] 最高分和最低分之间的差值是 4 - 1 = 3
- [9,4,1,7] 最高分和最低分之间的差值是 7 - 4 = 3
- [9,4,1,7] 最高分和最低分之间的差值是 7 - 1 = 6
可能的最小差值是 2
提示:
1 <= k <= nums.length <= 1000
0 <= nums[i] <= 105
思路:排序,滑动窗口。
排序后任选k个,最小值和最大值差最小值被取到的时候,k个数一定是在排序后数组中连续排列的。
如果不是;假设最小数是x 最大数是y;在x到y中一定有数没有被取,则将y和没取的数交换,最大最小值的差会变小(或保持不变)。
因而,我们升序排序后遍历每个位置i,计算 nums[i+k-1]-nums[i] ,求其中的最小值极客。
题解看了wfnuser的懂了。
class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());//从小到大(升序)排列
int n = nums.size();
int ans = INT_MAX;
for(int i = 0; i < n - k + 1; i++){
ans = min(ans, nums[i + k - 1] - nums[i]);//i到i+k-1有k个数
}
return ans;
}
};
Day21:1020. 飞地的数量
给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
示例 1:
输入:grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释:有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。
示例 2:
输入:grid = [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
输出:0
解释:所有 1 都在边界上或可以到达边界。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 500
grid[i][j] 的值为 0 或 1
思路:并查集+虚拟节点
class UnionFind{
public:
vector<int> parent;
UnionFind(int _n):parent(_n){
iota(parent.begin(), parent.end(), 0);
}
int find(int x){
if(x == parent[x]) return x;
return parent[x] = find(parent[x]);
}
void merge(int x, int y){
/* 易错
int rootx = parent[x];是错误写法
*/
int rootx = find(parent[x]);
int rooty = find(parent[y]);
if(rootx != rooty){
parent[rooty] = rootx;//把y集合合并到x集合
}
}
bool isConnected(int x, int y){
return find(x) == find(y);
}
};
class Solution {
public:
int numEnclaves(vector<vector<int>>& grid) {
int m = grid.size();//行
if(!m) return 0;
int n = grid[0].size();//列
if(!n) return 0;
UnionFind uf(m * n + 1);
int dummyNode = m * n;
int cnt = 0;//记录有多少个陆地格子
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 1){
if(i == 0 || j == 0 || i == m - 1 || j == n - 1){
uf.merge(dummyNode, i * n + j);
}
for(int k = 0; k < 4; k++){
int x = dx[k] + i, y = dy[k] +j;
if(x >= 0 && y >= 0 && x < m && y < n && grid[x][y] == 1)
uf.merge(i * n + j, x * n + y);
}
cnt++;
}
}
}
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 1 && uf.isConnected(dummyNode, i * n + j)){
cnt --;
}
}
}
return cnt;
}
};
Day22:1189. “气球” 的最大数量
给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 “balloon”(气球)。
字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 “balloon”。
多个数比较最小值可以用min({});最大值同理。
class Solution {
public:
int maxNumberOfBalloons(string text) {
int balloon[5]={0};
for(auto c : text){
if(c == 'b')
balloon[0]++;
else if(c == 'a')
balloon[1]++;
else if(c == 'l')
balloon[2]++;
else if(c == 'o')
balloon[3]++;
else if(c == 'n')
balloon[4]++;
}
int res =min({balloon[0], balloon[1], balloon[2]/2, balloon[3]/2, balloon[4]});
return res;
}
};
周赛28:6005. 使数组变成交替数组的最少操作数
给你一个下标从 0 开始的数组 nums ,该数组由 n 个正整数组成。
如果满足下述条件,则数组 nums 是一个 交替数组 :
nums[i - 2] == nums[i] ,其中 2 <= i <= n - 1 。
nums[i - 1] != nums[i] ,其中 1 <= i <= n - 1 。
在一步 操作 中,你可以选择下标 i 并将 nums[i] 更改 为 任一 正整数。
返回使数组变成交替数组的 最少操作数 。
示例 1:
输入:nums = [3,1,3,2,4,3]
输出:3
解释:
使数组变成交替数组的方法之一是将该数组转换为 [3,1,3,1,3,1] 。
在这种情况下,操作数为 3 。
可以证明,操作数少于 3 的情况下,无法使数组变成交替数组。
示例 2:
输入:nums = [1,2,2,2,2]
输出:2
解释:
使数组变成交替数组的方法之一是将该数组转换为 [1,2,1,2,1].
在这种情况下,操作数为 2 。
注意,数组不能转换成 [2,2,2,2,2] 。因为在这种情况下,nums[0] == nums[1],不满足交替数组的条件。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
反省错误:对x2和y2的赋值考虑不周。
一开始是这样的:
int x1 = nums[1], y1 = nums[0], x2 = 0, y2 = 0, cntx = 0, cnty = 0;
if(nums.size() > 2 && nums[2] != y1) y2 = nums[2];
if(nums.size() > 3 && nums[1] != x1) x2 = nums[3];
过不了测试:[4,4,4,4,3,4]。
因为改变x2 || y2 的值不是线性的。
初始化x2不是遇到一个比现在x1大的值才把x2赋值x1,x1赋值新值。
class Solution {
public:
int minimumOperations(vector<int>& nums) {
if(nums.size() <2) return 0;
//记录奇数位置和偶数位置出现次数最多的数字x和y,(注意处理相等的情况),答案就是奇数个数n-x的个数+偶数个数m-y的个数。
unordered_map<int, int> numsMapX, numsMapY;
int x1 = nums[1], y1 = nums[0], x2 = 0, y2 = 0, cntx = 0, cnty = 0;
int flag1 = 1, flag2 = 1;
for(int i = 0; i < nums.size(); i++){
if(i%2 == 0){
//偶数
cnty++;
numsMapY[nums[i]]++;
if(flag1 && nums[i]!= y1){
//cout<<"y1: "<< y1<<endl;
y2 = nums[i];
flag1 = 0;
}
if(numsMapY[nums[i]] > numsMapY[y1] && nums[i] != y1){
//cout<<"y1: "<<y1<<" nums[i]: " <<nums[i]<<endl;
y2 = y1;
y1 = nums[i];
}
}else{
//奇数
cntx++;
numsMapX[nums[i]]++;
if(flag2 && nums[i] != x1){
x2 = nums[i];
flag2 = 0;
}
if(numsMapX[nums[i]] > numsMapX[x1] && nums[i] != x1){
x2 = x1;
x1 = nums[i];
}
}
}
//cout<<x1<<" "<<x2<<" "<<y1<<" "<<y2<<endl;
int ans = 0;
if(x1 == y1){
if(x2 == 0 && y2 != 0){
ans = min(cntx, cnty - numsMapY[y2]);
}else if(x2 != 0 && y2 == 0){
ans = min(cntx - numsMapX[x2], cnty);
}else if(x2 == 0 && y2 == 0){
ans = min(cntx, cnty);
}else{
ans = min(cntx - numsMapX[x1] + cnty - numsMapY[y2], cntx - numsMapX[x2] + cnty - numsMapY[y1] );
}
}else{
ans = cntx - numsMapX[x1] + cnty - numsMapY[y1];
}
return ans;
}
};
Day23:540. 有序数组中的单一元素
给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。
请你找出并返回只出现一次的那个数。
你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。
示例 1:
输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
示例 2:
输入: nums = [3,3,7,7,10,11,11]
输出: 10
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 105
二分好难
思路:由于只出现一次的元素所在下标 x 的左边有偶数个元素,因此下标 x 一定是偶数,可以在偶数下标范围内二分查找。二分查找的目标是找到满足 nums[x]=nums[x+1] 的最小的偶数下标 x,则下标 x 处的元素是只出现一次的元素。
初始时,二分查找的左边界是 0,右边界是数组的最大偶数下标,由于数组的长度是奇数,因此数组的最大偶数下标等于数组的长度减 1。每次取左右边界的平均值 mid作为待判断的下标,如果mid是奇数则将mid-1,确保mid是偶数。
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
int low = 0, high = nums.size() - 1;
while (low < high) {
int mid = (high - low) / 2 + low;
mid -= mid & 1;
if (nums[mid] == nums[mid + 1]) {
low = mid + 2;
} else {
high = mid;
}
}
return nums[low];
}
};
Day24: 1380. 矩阵中的幸运数
给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。
幸运数是指矩阵中满足同时下列两个条件的元素:
- 在同一行的所有元素中最小
- 在同一列的所有元素中最大
示例 1:
输入:matrix = [[3,7,8],[9,11,13],[15,16,17]]
输出:[15]
解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
示例 2:
输入:matrix = [[1,10,4,2],[9,3,8,7],[15,16,17,12]]
输出:[12]
解释:12 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。
示例 3:
输入:matrix = [[7,8],[1,2]]
输出:[7]
提示:
m == mat.length
n == mat[i].length
1 <= n, m <= 50
1 <= matrix[i][j] <= 10^5
矩阵中的所有元素都是不同的
class Solution {
public:
vector<int> luckyNumbers (vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<int> minRow(m, INT_MAX), maxCol(n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
minRow[i] = min(minRow[i], matrix[i][j]);
maxCol[j] = max(maxCol[j], matrix[i][j]);
}
}
vector<int> ret;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == minRow[i] && matrix[i][j] == maxCol[j]) {
ret.push_back(matrix[i][j]);
}
}
}
return ret;
}
};
Day25: 969. 煎饼排序
给你一个整数数组 arr ,请使用 煎饼翻转 完成对数组的排序。
一次煎饼翻转的执行过程如下:
选择一个整数 k ,1 <= k <= arr.length
反转子数组 arr[0…k-1](下标从 0 开始)
例如,arr = [3,2,1,4] ,选择 k = 3 进行一次煎饼翻转,反转子数组 [3,2,1] ,得到 arr = [1,2,3,4] 。
以数组形式返回能使 arr 有序的煎饼翻转操作所对应的 k 值序列。任何将数组排序且翻转次数在 10 * arr.length 范围内的有效答案都将被判断为正确。
示例 1:
输入:[3,2,4,1]
输出:[4,2,4,3]
解释:
我们执行 4 次煎饼翻转,k 值分别为 4,2,4,和 3。
初始状态 arr = [3, 2, 4, 1]
第一次翻转后(k = 4):arr = [1, 4, 2, 3]
第二次翻转后(k = 2):arr = [4, 1, 2, 3]
第三次翻转后(k = 4):arr = [3, 2, 1, 4]
第四次翻转后(k = 3):arr = [1, 2, 3, 4],此时已完成排序。
提示:
1 <= arr.length <= 100
1 <= arr[i] <= arr.length
arr 中的所有整数互不相同(即,arr 是从 1 到 arr.length 整数的一个排列)
思路与算法
设一个元素的下标是 index,我们可以通过两次煎饼排序将它放到尾部:
第一步选择 k = index+1,然后反转子数组 arr[0…k−1],此时该元素已经被放到首部。
第二步选择 k =n,其中 n 是数组 arr 的长度,然后反转整个数组,此时该元素已经被放到尾部。
通过以上两步操作,我们可以将当前数组的最大值放到尾部,然后将去掉尾部元素的数组作为新的处理对象,重复以上操作,直到处理对象的长度等于一,此时原数组已经完成排序,且需要的总操作数是 2×(n−1),符合题目要求。如果最大值已经在尾部,我们可以省略对应的操作。
分解出子问题就好做了。
class Solution {
public:
vector<int> pancakeSort(vector<int>& arr) {
vector<int> ret;
for(int n = arr.size(); n > 1; n--){
int index = max_element(arr.begin(), arr.begin() + n) - arr.begin();
//返回的是地址,减去数组名是获得下标
if(index == n - 1) continue;
reverse(arr.begin(), arr.begin() + index + 1);
reverse(arr.begin(), arr.begin() + n);
ret.push_back(index + 1);
ret.push_back(n);
}
return ret;
}
};