前言
一、题:重新排列日志文件
给你一个日志数组 logs
。每条日志都是以空格
分隔的字串,其第一个字为字母与数字混合的 标识符 。
-
有两种不同类型的日志:
- 字母日志:除标识符之外,所有字均由小写字母组成
- 数字日志:除标识符之外,所有字均由数字组成
-
请按下述规则将日志重新排序:
- 所有 字母日志 都排在 数字日志 之前。
- 字母日志 在内容不同时,忽略标识符后,按内容字母顺序排序;在内容相同时,按标识符排序。
- 数字日志 应该保留原来的相对顺序。
-
返回日志的最终顺序。
示例 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"]
二、方法
- 先遍历一遍数组,将所有字母日志加入数组中,按规则排序:
- 在遍历一遍数组,将数字日志加在后面;
- 比较时,先将数组日志按照第一个空格分成两部分字符串,其中第一部分为标识符。第二部分的首字符可以用来判断该日志的类型。两条日志进行比较时,需要先确定待比较的日志的类型,然后按照以下规则进行比较:
- 字母日志始终小于数字日志。
- 数字日志保留原来的相对顺序。当使用稳定的排序算法时,可以认为所有数字日志大小一样。当使用不稳定的排序算法时,可以用日志在原数组中的下标进行比较。
- 字母日志进行相互比较时,先比较第二部分的大小;如果相等,则比较标识符大小。比较时都使用字符串的比较方式进行比较。
代码:
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
bool is_letter(char *str) {
char *cp = strchr(str, ' ') + 1;
return *cp >= 'a' & 'z';
}
int comp(const void *p1, const void *p2) {
char *s1 = *(char **) p1;
char *s2 = *(char **) p2;
// 找到指向第一个空格字符的指针
char *space1 = strchr(s1, ' ');
char *space2 = strchr(s2, ' ');
// 将字符串截成两段
*space1 = '\0';
*space2 = '\0';
// 分别得到 标识 和 内容的排序结果
int comp_tag = strcmp(s1, s2);
int comp_con = strcmp(space1 + 1, space2 + 1);
// 还原字符串
*space1 = ' ';
*space2 = ' ';
return comp_con != 0 ? comp_con : comp_tag;
}
char **reorderLogFiles(char **logs, int logsSize, int *returnSize){
char **res = (char **) malloc(logsSize * sizeof(char *));
*returnSize = 0;
for (int i = 0; i < logsSize; i++) {
if (is_letter(logs[i])) {
res[(*returnSize)++] = logs[i];
}
}
qsort(res, *returnSize, sizeof(char *), comp);
for (int i = 0; i < logsSize; i++) {
if (!is_letter(logs[i])) {
res[(*returnSize)++] = logs[i];
}
}
return res;
}
C语言函数:qsort()
;
- 头文件:
#include <stdlib.h>
,其时间复杂度为O(nlogn)
。函数原型如下:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
- 此函数需要四个参数。
- 第一个参数是需要排序的数组的基地址,因为是
void *
类型,所以此函数可以给任何类型的数组进行排序; - 第二个参数是待排序的数量(
size_t
是一种特别的数据类型,可以近似理解为int
型); - 第三个是单个数组元素的大小,即字节数,例如
int
型就是4
或者sizeof(int)
(sizeof 的返回值类型就是 sizeof),char
型就是1
或者sizeof(char)
。因为为了适用于各种数据结构,第一个参数将指向数组的指针强转成了void *
类型,也即此时函数并不知道将要进行排序的数组内存储的是什么元素,因此我们需要显式地告诉它单个元素所占的长度; - 第四个参数是一个指向函数的指针,其作用是规定排序的规则,即按照什么样的方式进行排序。
- 第一个参数是需要排序的数组的基地址,因为是
下面我们对一个整型数组排序为例:
#include<stdio.h>
#include<stdlib.h>
//比较函数原型
int mycmp(const void* p1, const void* p2);
int main() {
int num[10] = {32, -4, 89, 232, 2, 12, -32, 0, -4, 89};
qsort(num, 10, sizeof(int), mycmp);
for(int i=0; i<10; i++)
printf("%d ", num[i]);
printf("\n");
return 0;
}
//比较函数
int mycmp(const void* p1, const void* p2){
const int * a = (const int *) p1;
const int * b = (const int *) p2;
int value = 0;
if(*a < *b)
value = -1;
else if(*a == *b)
value = 0;
else value = 1;
return value;
}