练题100天——DAY31:相对名次+数组拆分+重塑矩阵

今天三道题,难度范围:★~★★★,都不是很难的题,可能会需要动一动脑筋。

今日收获

1.当遇到一系列特殊情况时,可以将其存放到数组中,后面调用赋值更方便;

2.对于降序排列的需要,既可以先升序再逆序,也可以通过对其取负数再升序,以实现原数值的降序排列;

3.vector<vector<int>> mat 二维数组:访问------可以直接使用mat[i][j];赋值------先创建临时数组 vector<int> temp; 先将元素一行一行地装入temp,再将一整行temp装入mat

4.二维数组与一维数组可以相互"转换":ans[x / c][x % c],其中c为二维列数,x为一维下标

一.相对名次 ★★☆☆☆

题目

506. 相对名次

给你一个长度为 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) ,使得从 1nmin(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 矩阵,以及两个正整数 rc ,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。

如果具有给定参数的 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)。

相关推荐
LYFlied2 小时前
【算法解题模板】-解二叉树相关算法题的技巧
前端·数据结构·算法·leetcode
Ven%2 小时前
【AI大模型算法工程师面试题解析与技术思考】
人工智能·python·算法
天勤量化大唯粉2 小时前
枢轴点反转策略在铜期货中的量化应用指南(附天勤量化代码)
ide·python·算法·机器学习·github·开源软件·程序员创富
爱学习的小仙女!2 小时前
算法效率的度量 时间复杂度 空间复杂度
数据结构·算法
AndrewHZ2 小时前
【复杂网络分析】什么是图神经网络?
人工智能·深度学习·神经网络·算法·图神经网络·复杂网络
Swizard2 小时前
拒绝“狗熊掰棒子”!用 EWC (Elastic Weight Consolidation) 彻底终结 AI 的灾难性遗忘
python·算法·ai·训练
Trouvaille ~3 小时前
【C++篇】把混沌映射成秩序:哈希表的底层哲学与实现之道
数据结构·c++·stl·哈希算法·散列表·面向对象·基础入门
Yeats_Liao3 小时前
MindSpore开发之路(四):核心数据结构Tensor
数据结构·人工智能·机器学习
fab 在逃TDPIE3 小时前
Sentaurus TCAD 仿真教程(十)
算法