LeetCode周练Contest-37代码解析(C++)

写在前面:

LeetCode这个网站相比不必多说了吧,凡是IT圈子的人应该都知道这个网站,最近开始准备找工作,当然也免不了上去刷刷题,做一做比较经典的编程题,刚好看到LeetCode有个周练,便报名参加。

进入正题:

概要: 总共4个题,一个半小时的时间安排。题目分级为,两个easy,一个medium,一个hard。

第一题 624. Maximum Distance in Arrays

题目描述

Given m arrays, and each array is sorted in ascending order. Now you can pick up two integers from two different arrays (each array picks one) and calculate the distance. We define the distance between two integers a and b to be their absolute difference |a-b|. Your task is to find the maximum distance.


这道题一开始有点卡,题意就是找到非同一个数组中最大值,和非同一个数组中的最小值,使得两数相减的值最大,给出来的数组都是已经排序完成了的,唯一的约束就是不能再同一个数组中选择最大值和最小值;

这道题有点借助了一道经典的题的思路,叫做求最长递增子序列,使用动态规划的方法来做,首先,选取第一个一维数组,取两端构建最大值和最小值,然后从下一个一维数组开始遍历,遍历到每一个一维数组,我们假设其中某一个值要在该数组中选择,所以有两种方法,选择该数组的 最大值 - 之前维护的最小值,或者 之前维护的最大值 - 该数组的最小值, 使用一个外部变量来维护一个差值最大的结果;同时,在遍历下一个一维数组之前,将该一维数组的最大值和最小值拿去更新一直以来在维护的最大值和最小值;

代码如下:

//TLE Solution
class Solution {
public:
    int maxDistance(vector<vector<int>>& arrays) {
        int lens = arrays.size();
        int ret = 0;
        for(int i=0;i<lens;i++){
            for(int j=0;j<lens;j++){
                if(i != j){
                    ret = max(ret, arrays[i].back()-arrays[j].front());
                }
            }
        }
        return ret;
    }
};

class Solution {
public:
    int maxDistance(vector<vector<int>>& arrays) {
        //用一个外部变量来维护最大距离差值
        int ret = 0;
        int min_val = arrays[0].front(), max_val = arrays[0].back();
        for(int i=1;i<(int)arrays.size();i++){
            //每一次计算分别选择当前array的最大值,最小值来计算差值,并保存最大值
            ret = max(arrays[i].back()-min_val, ret);
            ret = max(max_val-arrays[i].front(), ret);
            //更新当前array之前的所有array维护出来的最大值和最小值
            min_val = min(min_val, arrays[i].front());
            max_val = max(max_val, arrays[i].back());
        }
        return ret;
    }
};

第二题 623. Add One Row to Tree

题目描述

Given the root of a binary tree, then value v and depth d, you need to add a row of nodes with value v at the given depth d. The root node is at depth 1.

The adding rule is: given a positive integer depth d, for each NOT null tree nodes N in depth d-1, create two tree nodes with value v as N's left subtree root and right subtree root. And N's original left subtree should be the left subtree of the new left subtree root, its original right subtree should be the right subtree of the new right subtree root. If depth d is 1 that means there is no depth d-1 at all, then create a tree node with value v as the new root of the whole original tree, and the original tree is the new root's left subtree.


很简单的一个题,树形结构,又是它,第一选择就是递归来做,明白题目的意思以后分两种情况;

一种是直接在根节点插入一行节点;另一种情况就是在树种插入一行节点;选择的方法就是dfs来做,插入完节点之后就撤;

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    void dfs(TreeNode* root, int v, int d, int cur){
        //判断边缘情况,符合的话记得返回,不符合继续dfs
        if(root == NULL)    return;
        if(cur == d){
            TreeNode* new_left = new TreeNode(v); new_left->left = root->left;
            TreeNode* new_right = new TreeNode(v); new_right->right = root->right;
            root->left = new_left; root->right = new_right;
            return;
        }
        dfs(root->left, v, d, cur+1);
        dfs(root->right, v, d, cur+1);
    }
public:
    //递归解决树的深度优先问题
    TreeNode* addOneRow(TreeNode* root, int v, int d) {
        int real = d - 1;
        //如果real为根节点,所以直接用来作为根节点,左节点为原来的树
        if(real == 0){
            TreeNode* ret = new TreeNode(v);
            ret->left = root;
            return ret;
        }
        else{
            TreeNode* ret = root;
            dfs(root, v, real, 1);
            return ret;
        }
    }
};

第三题 625. Minimum Factorization

题目描述

Given a positive integer a, find the smallest positive integer b whose multiplication of each digit equals to a.

If there is no answer or the answer is not fit in 32-bit signed integer, then return 0.


身为medium难度的一题,其实只要把其中的规则摸清楚,medium甚至于hard的题目反而坑会比较少;

这道题要求给定一个数a,返回一个数b,是的将数b的每一位相乘以后都能得到a;

由此可以判断出,要想任何数相乘以后得到该数本身,那就和它自己的约数有关,在这一题,考虑的是单位数相乘,所以需要排除掉约数中有不可再约的非单位数,也就是说,可以将一个数a分解成质因数,并且这些质因数不能超过10;所以我们可以用一个数据结构来维护分解之后的质因数,然后由于要使得最终组合出来的b最小而且在unsigned int表示范围内;所以质因数中,2和3可以组合成更大的数(不超过10),这样使得b的范围更加收敛;

在这里组合质因数有一个规律,首先尽可能的用3个2组成一个8,剩下的2和3排成一列,如果总数为奇数,取第一位放在b的第一位,然后剩下的两两组合;如果直接总数为偶数,那就直接两两组合;这样可以使得最终组成的b最小;

代码如下:

class Solution {
private:
    // 用一个map来维护
    map<int, int> table;
    bool initNums(int a){
        int tmp = a, divisor = 2;
        while(tmp != 1){
            if(divisor >= 10)   return false;
            if(tmp%divisor == 0){
                table[divisor]++;
                tmp /= divisor;
            }
            else{
                divisor++;
            }
        }
        return true;
    }
    // 三个2结合成8,剩下的2和3组成序列,如果为基数,第一个数保留
    // 序列中剩下的数两两相乘来组成新的数放入map
    // 组合map里面的数,如果是int范围内的输出,否则输出0
    int deal(map<int, int>& table){
        int tmp = table[2] / 3, rest = table[2] % 3;
        table[8] += tmp;
        vector<int> nums(rest, 2), nums_tmp(table[3], 3);
        table.erase(2); table.erase(3); 
        nums.insert(nums.end(), nums_tmp.begin(), nums_tmp.end());
        int lens = nums.size(), i = 0;
        if(lens > 0){
            if(lens&0x01){
                i = 1; table[nums[0]] = 1;
            }
            for(;i<lens;i+=2){
                int res = nums[i]*nums[i+1];
                table[res]++;
            }
        }
        long long res = 0;
        for(auto k=table.begin();k!=table.end();k++){
            for(int i=1;i<=k->second;i++){
                res = res*10 + k->first;
            }
        }
        if(res>=INT_MIN&&res<=INT_MAX)  return (int)res;
        else    return 0;
    }
public:
    // 先将数用质数表示
    int smallestFactorization(int a) {
        if(a == 1)  return 1;
        // 判断能否用10以内的质数表示,不能的话返回false
        if(!initNums(a))    return 0;
        return deal(table);
    }
};

第四题 621. Task Scheduler

题目描述

Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.

However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle.

You need to return the least number of intervals the CPU will take to finish all the given tasks.


模拟CPU的作业调度机制,有两个约束条件,一个是要使得总的消耗的CPU单位时间最小,二个是每一个作业都有一个间隔时间n;

需要保证每一个作业至少隔大于等于n的单位时间后再一次调度,如果没有一个作业需要调度,用idle来填充输出;

思路:首先考虑到要使得CPU作业时间最短,即保证idle时间最少,那么采用的策略就是每一次优先在间隔时间内调度那些需要执行次数比较多的作业,同时,每隔一个周期n之后对需要调度的作业次数重新排序,以便于下一次还能按照所需次数最多的作业优先调度的策略来进行;所以最直观的一个思路就是使用优先队列来完成,同时用一个辅助队列来辅助;

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        //先统计每个单词出现的次数
        map<int, int> table;
        for(int i=0;i<(int)tasks.size();i++)    table[tasks[i]-'A']++;
        priority_queue<int> que;
        for(auto k=table.begin();k!=table.end();k++)    que.push(k->second);
        table.clear();
        //初始化变量
        int ret = 0, interval = n+1;
        queue<int> tmp;
        while(!que.empty()){
            //这种情况下,之后需要通过interval来计算,所以最好就把interval的减一操作放到循环里面去做
            //省得在外层interval为0break之后又要减一变成-1,影响后续操作
            while(!que.empty()&&interval){
                int val = que.top(); que.pop();
                if(val - 1 > 0) tmp.push(val-1);
                interval--;
            }
            //只有在两个队列都空掉的时候才考虑增加部分次数
            if(tmp.empty() && que.empty())  ret += n+1-interval;
            //不然需要考虑idle情况,增加所有的次数n+1
            else{
                ret += n+1;
                while(!tmp.empty()){
                    int x = tmp.front(); tmp.pop();
                    que.push(x);
                }
            }
            interval = n+1;
        }
        return ret;
    }
};

总结:

找工作路漫漫,但求不忘初心,回首对得起走过的路。

代码地址:[luuuyi/leetcode-contest]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值