【C/C++】937. 重新排列日志文件

608 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情


题目链接:937. 重新排列日志文件

题目描述

给你一个日志数组 logs。每条日志都是以空格分隔的字串,其第一个字为字母与数字混合的 标识符

有两种不同类型的日志:

  • 字母日志:除标识符之外,所有字均由小写字母组成
  • 数字日志:除标识符之外,所有字均由数字组成 请按下述规则将日志重新排序:
  • 所有 字母日志 都排在 数字日志 之前。
  • 字母日志 在内容不同时,忽略标识符后,按内容字母顺序排序;在内容相同时,按标识符排序。
  • 数字日志 应该保留原来的相对顺序。 返回日志的最终顺序。

提示:

  • 1logs.length1001 \leqslant logs.length \leqslant 100
  • 3logs[i].length1003 \leqslant logs[i].length \leqslant 100
  • logs[i] 中,字与字之间都用 单个 空格分隔
  • 题目数据保证 logs[i] 都有一个标识符,并且在标识符之后至少存在一个字

示例 1:

输入:logs = ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"]
输出:["let1 art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1","dig2 3 6"]
解释:
字母日志的内容都不同,所以顺序为 "art can", "art zero", "own kit dig" 。
数字日志保留原来的相对顺序 "dig1 8 1 5 1", "dig2 3 6"

示例 2:

输入:logs = ["a1 9 2 3 1","g1 act car","zo4 4 7","ab1 off key dog","a8 act zoo"]
输出:["g1 act car","a8 act zoo","ab1 off key dog","a1 9 2 3 1","zo4 4 7"]

题意整理

题目给出了一组字符串数组 logs,按照题目要求对这些字符串进行排序,最后输出排序后的结果即可。

解题思路分析

习惯性动作,观察题目数据范围为 100,数据范围较小,按照题意模拟即可。

该题难点在于理解题意上:

logs.jpg

对于每个字符串都有一个标识符,我们以标识符后的第一个空格为分界点,将字符串分为标识符和日志两部分。日志又分为字母日志和数字日志,我们需要:

  • 将所有字母日志排在所有数字日志之前;
  • 将所有字母日志字符串先按照字母日志排序,如果字母日志相同,再按照标识符排序。
  • 将所有数字日志按照原来的相对顺序放在所有字母日志之后;

分析完题意之后可以发现,该题就是在考察 自定义排序字符串处理

具体实现

  1. 首先找到标识符和日志的分界点。
  2. 判断当前日志是字母日志还是数字日志。
  3. 进行排序时分为四种情况:
    • 字母日志和字母日志:我们先判断日志部分是否相同,相同的话进行标识符排序;
    • 数字日志和字母日志:我们将字母日志放在数字日志之前;
    • 数字日志和数字日志:按照原来的相对顺序即可,我们无需交换顺序;
    • 字母日志和数字日志:字母日志应该在数字日志之前,我们无需交换顺序。
  4. 输出排序后的结果即可。

复杂度分析

  • 时间复杂度:O(mnlog(n))O(mn\log(n)),其中 n 为字符串数组的大小,m 为字符串长度,nlog(n)n\log(n) 为排序所需的时间。
  • 空间复杂度:O(mn)O(mn),其中 n 为字符串数组的大小,m 为字符串长度,仅需存储所有字符串的空间。

代码实现

此处使用 sort 函数进行排序,自定义 sort 函数的 cmp 比较函数(也就是 sort 函数中的第三个参数),可以将 cmp 自定义函数写在外面,具体操作方法可以看 sort 函数中 cmp 的使用)。

这里使用的是 lambda 表达式 进行内置 cmp 函数(可以理解为 匿名的内联函数 ),这样的好处在于不用将需要在 cmp 中使用的外部参数修改为全局变量。 lambda 表达式会自动捕获上下文中的数据,也就是可以 直接使用外部的数据变量

[&](const string& log1, const string& log2)->bool{
    ...
}
  • 这里 [&] 表示引用外部的变量,也就是在该函数中对外部变量进行修改,外部变量也会跟着改变。
  • (const string& log1, const string& log2) 这里表示传入的参数,这里将 log1log2 作为常量引用,即避免了额外使用空间进行拷贝,又避免了在函数中修改该参数,这样我们就不能在函数中修改 log1log2
  • ->bool 这里表示函数的返回值类型。
  • 剩下的 {...} 就是函数体本身了。

此外还用到了 stable_sort() 稳定排序 函数,与 sort() 函数相比,stable_sort 的用法与 sort 一致,区别是 stable_sort 函数遇到两个数相等时,不对其交换顺序;这个应用在数组里面不受影响,当函数参数传入的是结构体时,会发现两者之间的明显区别;

完整 Accept 代码

class Solution {
public:
    vector<string> reorderLogFiles(vector<string>& logs) {
        stable_sort(logs.begin(), logs.end(), [&](const string& log1, const string& log2)->bool{
            //找到第一个空格作为分界线
            int pos1 = 0;
            int len1 = log1.length();
            while(pos1 < len1 && log1[pos1] != ' '){
                pos1++;
            }
            int pos2 = 0;
            int len2 = log2.length();
            while(pos2 < len2 && log2[pos2] != ' '){
                pos2++;
            }
            //判断是否是字母日志
            bool isLower1 = islower(log1[pos1 + 1]);
            bool isLower2 = islower(log2[pos2 + 1]);
            //都是字母日志
            if(isLower1 && isLower2){
                //截取除了标识符以外的字符
                string str1 = "";
                while(pos1 < len1){
                    str1 += log1[pos1];
                    pos1++;
                }
                string str2 = "";
                while(pos2 < len2){
                    str2 += log2[pos2];
                    pos2++;
                }
                //先判断除标识符外的字符排序
                if(str1 != str2){
                    return str1 < str2;
                }
                //再以标识符排序
                else return log1 < log2;
            }
            //都是数字日志
            if(!isLower1 && !isLower2) return false;
            //一个数字日志一个字母日志,将字母日志放前面
            return isLower1 ? true : false;
        });
        return logs;
    }
};

总结

该题主要考察 自定义排序字符串处理 ,其中难点在于题意理解上。

通过该题了解 lambda 表达式 的使用,通常我们还有一下几种操作:

  • auto check = [&]()->bool{...} :通常在写 check() 函数的时候可以用到,auto 表示自动获取返回值类型赋给 check ,这里返回的是一个函数方法给 check 返回值类型为 bool 类型,之后直接调用 check() 即可。

还需要注意 stable_sort()sort() 函数的区别,stable_sort() 是稳定排序,sort() 是不稳定排序,虽然都是快速排序,时间复杂度都为 O(nlog2n)O(n\log_2n),区别在于稳定排序在对相同元素进行排序时不会交换其顺序,不稳定排序会交换其顺序,这个应用在数组里面不受影响,当函数参数传入的是结构体时,会发现两者之间的明显区别。


结束语

生活因劳动而变好,人生因劳动而充实。不管身处哪一个岗位,每一位劳动者的付出都值得尊重。要相信,只要坚持奋斗,平凡的我们终会闪闪发光。