今天三道题,难度范围:★~★★★,都不是很难的题,可能会需要动一动脑筋。
今日收获
1.当遇到一系列特殊情况时,可以将其存放到数组中,后面调用赋值更方便;
2.对于降序排列的需要,既可以先升序再逆序,也可以通过对其取负数再升序,以实现原数值的降序排列;
3.vector<vector<int>> mat 二维数组:访问------可以直接使用mat[i][j];赋值------先创建临时数组 vector<int> temp; 先将元素一行一行地装入temp,再将一整行temp装入mat
4.二维数组与一维数组可以相互"转换":ans[x / c][x % c],其中c为二维列数,x为一维下标
一.相对名次 ★★☆☆☆
题目
给你一个长度为 n 的整数数组 score ,其中 score[i] 是第 i 位运动员在比赛中的得分。所有得分都 互不相同 。
运动员将根据得分 决定名次 ,其中名次第 1 的运动员得分最高,名次第 2 的运动员得分第 2 高,依此类推。运动员的名次决定了他们的获奖情况:
- 名次第
1的运动员获金牌"Gold Medal"。 - 名次第
2的运动员获银牌"Silver Medal"。 - 名次第
3的运动员获铜牌"Bronze Medal"。 - 从名次第
4到第n的运动员,只能获得他们的名次编号(即,名次第x的运动员获得编号"x")。
使用长度为 n 的数组 answer 返回获奖,其中 answer[i] 是第 i 位运动员的获奖情况。
思路
先将原数组的数值和索引配套放入哈希表中存储,然后使原数组降序排列(先升序操作,再逆序),遍历降序排列后的数组中的每一个元素,从哈希表中可以得到每个元素在原数组中的索引,即每个元素在结果数组ans中的正确索引,对当前索引的ans的元素进行赋值即可。
代码
cpp
class Solution {
public:
vector<string> findRelativeRanks(vector<int>& score) {
int n=score.size();
vector<string> ans(n);
unordered_map<int,int> map;
//将原数组元素和其索引放入哈希表中
for(int i=0;i<n;i++){
map[score[i]]=i;
}
//对数组进行降序排列
//先升序
sort(score.begin(),score.end());
//再逆序
reverse(score.begin(), score.end());
for(int i=0;i<n;i++){
int index=map[score[i]];
if(i==0){
ans[index]="Gold Medal";
}else if(i==1){
ans[index]="Silver Medal";
}else if(i==2){
ans[index]="Bronze Medal";
}else{
ans[index]=to_string(i+1);
}
}
return ans;
}
};
复杂度
n为score数组的长度
时间复杂度:O(nlogn)。主要的操作有:将数组元素保存到哈希表,遍历n次,所以有O(n);升序操作O(nlogn);逆序操作O(n);遍历数组scoreO(nlogn)。所以整体的时间复杂度为O(n+nlogn+n+n)=O(3n+nlogn)=O(nlogn)
空间复杂度:O(n)。哈希表的空间复杂度+其他变量的空间复杂度=O(n)+O(1)=O(n)
sort函数+reverse函数的复杂度

哈希表/哈希集合相关操作的复杂度

补充说明
1.我一开始想到的是创建一个新数组 用于保存原数组的值,再将原数组降序排列,然后通过双层循环 计算对应得分的名次并保存与结果数组中。假设用score2数组保存原数组score的值,使score降序排列后,外层循环遍历数组score2的元素score2[ i ],内存循环遍历score,找到与score2[ i ]相等的值score[ j ],其对应的名次应该是 j+1,即ans[ i ]=j+1。循环结束后还需要对前三名另外处理。
2.使用哈希表法时,如果没有把前三名放进循环,而是直接在循环之前就给ans的前三名进行了赋值,在遇到数组长度为1或2时都会出现索引越界的错误,所以可以将前三名也放进循环,或者在循环外赋值时就对数组长度进行判断。
我的代码优化一下下
基于官方题解的对负分数进行升序排列进行的优化,减少了reverse函数
cpp
class Solution {
public:
vector<string> findRelativeRanks(vector<int>& score) {
int n=score.size();
vector<string> ans(n);
unordered_map<int,int> map;
//将原数组元素和其索引放入哈希表中
for(int i=0;i<n;i++){
map[score[i]]=i;
//将原分数变为负分数
score[i]=-score[i];
}
//只需要一次排序即可实现对分数的降序排列
sort(score.begin(),score.end());
for(int i=0;i<n;i++){
int index=map[-score[i]];
if(i==0){
ans[index]="Gold Medal";
}else if(i==1){
ans[index]="Silver Medal";
}else if(i==2){
ans[index]="Bronze Medal";
}else{
ans[index]=to_string(i+1);
}
}
return ans;
}
};
官方题解
思路都大差不差,重点是如何实现,官方用到的主要方法:
1.将前三名的结果字符串存入到一个字符串数组中,便于后续的直接赋值
2.创建一个成对数组arr ,用于存储负分数和原索引,通过一次sort使arr升序排列,从而实现分数的降序排列
代码
cpp
class Solution {
public:
vector<string> findRelativeRanks(vector<int>& score) {
int n=score.size();
//将前三名金牌字符串存入数组,便于后续的赋值
string desc[3]={"Gold Medal", "Silver Medal", "Bronze Medal"};
//创建 存储(负分数,原索引)的成对数组
vector<pair<int,int>> arr;
//结果数组
vector<string> ans(n);
//将-负分数和对应索引存入数组arr
for(int i=0;i<n;i++){
arr.emplace_back(make_pair(-score[i],i));
}
//使arr升序排序 → 使分数降序排列
sort(arr.begin(),arr.end());
for(int i=0;i<n;i++){
if(i>=3){
ans[arr[i].second]=to_string(i+1);
}else{
ans[arr[i].second]=desc[i];
}
}
return ans;
}
};
说明
1.为什么选择vector<pair<int,int>>,而不是哈希表

2.make_pair:创建一个 std::pair 类型的对象
3.arr[i].first ------ 负的分数(排序后的);arr[i].second ------ 该分数在原数组中的索引
复杂度
n为score数组的长度
时间复杂度:O(nlogn)。需要对数组进行一次排序,因此时间复杂度为 O(nlogn)
空间复杂度:O(n)
二.数组拆分 ★☆☆☆☆
题目
561. 数组拆分 给定长度为 2n的整数数组 nums ,你的任务是将这些数分成 n对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。返回该 最大总和 。
思路
因为是求min(ai,bi)总和的最大值,所以min中ai和bi相差越小越好,这样最后的min值也不会太小,不会浪费一个较大值,所以可以先将数组升序排列,使得相邻的数相差不大,然后遍历数组,数组元素两两组合,结果sum加上较小的数即可。
代码(从前往后)
cpp
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
int sum=0;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i+=2){
sum+=nums[i];
}
return sum;
}
};
代码(从后往前)
cpp
class Solution {
public:
int arrayPairSum(vector<int>& nums) {
int sum=0;
sort(nums.begin(),nums.end());
for(int i=nums.size()-1;i>=0;i-=2){
sum+=nums[i-1];
}
return sum;
}
};
复杂度
n是数组的长度
时间复杂度:O(nlogn)。对数组 nums 进行排序的时间复杂度。
空间复杂度:O(logn)。排序所需的栈空间
tips
想不过来为什么是这样的,可以去看++官方题解++,讲得很详细
三.重塑矩阵 ★★★☆☆☆☆
题目
566. 重塑矩阵 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。
给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。
如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵
思路
1.判断参数是否可行且合理:只有当原始矩阵的空间等于新矩阵的空间时,才合理。
2.先将原始矩阵的元素放入一维数组中,然后又将一维数组的元素放入要求创建的矩阵即可
代码
cpp
class Solution {
public:
vector<vector<int>> matrixReshape(vector<vector<int>>& mat, int r, int c) {
int rLen=mat.size();
int cLen=mat[0].size();
//不合规,返回原始矩阵
if(rLen*cLen != r*c){
return mat;
}
//结果矩阵
vector<vector<int>> ans;
//创建一个一维数组,保存mat中的元素
vector<int> temp(rLen*cLen);
int index=0;
// 展平二维矩阵为一维数组
for(int i=0;i<rLen;i++){
for(int j=0;j<cLen;j++){
temp[index++] = mat[i][j];
}
}
//将temp的值放入ans中
index=0;
for(int i=0;i<r;i++){
vector<int> temp2;
for(int j=0;j<c;j++){
temp2.push_back(temp[index++]);
}
ans.push_back(temp2);
}
return ans;
}
};
复杂度
n=rLen×cLen,即原矩阵的元素总数
时间复杂度:O(n)。两次遍历所有元素的次数均为n,所以时间复杂度为O(n)+O(n)=O(n)
空间复杂度:O(n)。一维数组temp的空间复杂度,以及临时变量:O(n)+O(1)=O(n)
官方题解------二维数组的一维表示
通过坐标与列数的数学关系将二维数组映射为一维数组。直接将原矩阵映射为一维数组,然后将元素依次赋值给新矩阵映射的一维数组,可以直接省略一维数组的过渡,直接进行两个矩阵元素的赋值。下面的图片是例子帮助理解。

代码
cpp
class Solution {
public:
vector<vector<int>> matrixReshape(vector<vector<int>>& nums, int r, int c) {
int m = nums.size();
int n = nums[0].size();
if (m * n != r * c) {
return nums;
}
vector<vector<int>> ans(r, vector<int>(c));
for (int x = 0; x < m * n; ++x) {
ans[x / c][x % c] = nums[x / n][x % n];
}
return ans;
}
};
复杂度
时间复杂度:O(rc)。这里的时间复杂度是在重塑矩阵成功的前提下的时间复杂度,否则当 mn=rc 时,C++ 语言中返回的是原数组的一份拷贝,本质上需要的时间复杂度为 O(mn),而其余语言可以直接返回原数组的对象,需要的时间复杂度仅为 O(1)
空间复杂度:O(1)。