Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

937. Reorder Data in Log Files - Leetcode Solution

Code Implementation

class Solution:
    def reorderLogFiles(self, logs):
        letter_logs = []
        digit_logs = []
        for log in logs:
            id_, rest = log.split(" ", 1)
            if rest[0].isdigit():
                digit_logs.append(log)
            else:
                letter_logs.append((rest, id_, log))
        # Sort by content, then identifier
        letter_logs.sort(key=lambda x: (x[0], x[1]))
        return [log[2] for log in letter_logs] + digit_logs
      
class Solution {
public:
    vector<string> reorderLogFiles(vector<string>& logs) {
        vector<pair<string, string>> letter_logs;
        vector<string> digit_logs;
        for (const string& log : logs) {
            int pos = log.find(' ');
            string id = log.substr(0, pos);
            string rest = log.substr(pos + 1);
            if (isdigit(rest[0])) {
                digit_logs.push_back(log);
            } else {
                letter_logs.emplace_back(rest, id);
            }
        }
        sort(letter_logs.begin(), letter_logs.end(), [](const pair<string, string>& a, const pair<string, string>& b) {
            if (a.first == b.first)
                return a.second < b.second;
            return a.first < b.first;
        });
        vector<string> result;
        for (const auto& p : letter_logs) {
            result.push_back(p.second + " " + p.first);
        }
        result.insert(result.end(), digit_logs.begin(), digit_logs.end());
        return result;
    }
};
      
class Solution {
    public String[] reorderLogFiles(String[] logs) {
        List<String> letterLogs = new ArrayList<>();
        List<String> digitLogs = new ArrayList<>();
        for (String log : logs) {
            int idx = log.indexOf(' ');
            if (Character.isDigit(log.charAt(idx + 1))) {
                digitLogs.add(log);
            } else {
                letterLogs.add(log);
            }
        }
        Collections.sort(letterLogs, (a, b) -> {
            int idxA = a.indexOf(' ');
            int idxB = b.indexOf(' ');
            String contentA = a.substring(idxA + 1);
            String contentB = b.substring(idxB + 1);
            if (contentA.equals(contentB)) {
                return a.substring(0, idxA).compareTo(b.substring(0, idxB));
            }
            return contentA.compareTo(contentB);
        });
        String[] result = new String[logs.length];
        int i = 0;
        for (String log : letterLogs) result[i++] = log;
        for (String log : digitLogs) result[i++] = log;
        return result;
    }
}
      
var reorderLogFiles = function(logs) {
    const letterLogs = [];
    const digitLogs = [];
    for (const log of logs) {
        const idx = log.indexOf(' ');
        const id = log.substring(0, idx);
        const rest = log.substring(idx + 1);
        if (/\d/.test(rest[0])) {
            digitLogs.push(log);
        } else {
            letterLogs.push([rest, id, log]);
        }
    }
    letterLogs.sort((a, b) => {
        if (a[0] === b[0]) {
            return a[1] < b[1] ? -1 : 1;
        }
        return a[0] < b[0] ? -1 : 1;
    });
    return letterLogs.map(x => x[2]).concat(digitLogs);
};
      

Problem Description

You are given an array of log strings, logs. Each log is a space-delimited string with two parts:

  • An identifier (the first word)
  • The rest of the log, which may contain either all letters (for letter-logs) or all digits (for digit-logs)
You need to reorder the logs so that:
  1. All letter-logs come before any digit-log.
  2. Letter-logs are sorted lexicographically by their content. If contents are the same, sort by identifier.
  3. Digit-logs should remain in their original order.
There is exactly one valid answer for each input. You must not reuse or modify elements beyond reordering.

Thought Process

To solve this problem, we need to distinguish between two types of logs: letter-logs and digit-logs. The main challenge is to sort the letter-logs according to their content and identifier, while keeping the digit-logs in their original order. A brute-force approach might try to sort all logs together, but this would not respect the special ordering rules for digit-logs. Instead, it's better to separate the logs into two groups, process each group as required, and then combine them.

The key insight is to treat letter-logs and digit-logs differently:

  • Letter-logs need custom sorting.
  • Digit-logs only need to be appended after the letter-logs, preserving their input order.
This separation makes the problem much simpler and allows us to use built-in sorting for the letter-logs.

Solution Approach

Let's break down the solution step by step:

  1. Separate logs into letter-logs and digit-logs:
    • For each log, split it into the identifier and the rest of the log.
    • If the first character of the rest is a digit, it's a digit-log; otherwise, it's a letter-log.
  2. Sort the letter-logs:
    • Sort primarily by the content (everything after the identifier).
    • If contents are the same, sort by identifier.
    • This can be done using a tuple or custom comparator.
  3. Combine the results:
    • Place all sorted letter-logs first.
    • Then append all digit-logs in their original order.

We use a list or array to store each group. Sorting is efficient because we only sort letter-logs, and digit-logs are simply appended.

Example Walkthrough

Let's use the following example input:

logs = [
  "dig1 8 1 5 1",
  "let1 art can",
  "dig2 3 6",
  "let2 own kit dig",
  "let3 art zero"
]
  
  1. Separate into letter-logs and digit-logs:
    • Letter-logs: ["let1 art can", "let2 own kit dig", "let3 art zero"]
    • Digit-logs: ["dig1 8 1 5 1", "dig2 3 6"]
  2. Sort letter-logs by content and identifier:
    • "let1 art can" (content: "art can")
    • "let3 art zero" (content: "art zero")
    • "let2 own kit dig" (content: "own kit dig")

    After sorting by content, we get:

    1. "let1 art can"
    2. "let3 art zero"
    3. "let2 own kit dig"

  3. Combine sorted letter-logs with digit-logs:
    • Result: ["let1 art can", "let3 art zero", "let2 own kit dig", "dig1 8 1 5 1", "dig2 3 6"]

This ordering meets all problem requirements.

Time and Space Complexity

  • Brute-force approach:
    • If you tried to sort all logs together and checked types during comparison, the complexity would be at least O(N log N), but with more overhead for custom logic.
  • Optimized approach:
    • Separating into letter-logs and digit-logs takes O(N).
    • Sorting letter-logs is O(M log M), where M is the number of letter-logs (M ≤ N).
    • Combining the lists is O(N).
    • Total time complexity: O(N log N), dominated by the sorting step.
    • Space complexity: O(N), for storing the separated logs.

Summary

The key to solving "Reorder Data in Log Files" is to separate the problem into manageable parts: identify letter-logs and digit-logs, sort only the letter-logs with a custom comparator, and maintain the original order of digit-logs. This approach leverages efficient sorting and simple list manipulations, resulting in a clean and effective solution. The elegance comes from reducing the problem to sorting with a custom key and combining the results in a straightforward way.