Skip to content

269. 火星字典

题设

现有一种使用字母的全新语言,这门语言的字母顺序与英语顺序不同。

假设,您并不知道其中字母之间的先后顺序。但是,会收到词典中获得一个不为空的单词列表。因为是从词典中获得的,所以该单词列表内的单词已经按这门新语言的字母顺序进行了排序。

您需要根据这个输入的列表,还原出此语言中已知的字母顺序。

输入:
[
  "wrt",
  "wrf",
  "er",
  "ett",
  "rftt"
]
输出: "wertf"

解法一:拓扑排序

  • 第一步:根据输入构建一个有向图
    • 逐位比较两个相互挨着的字符串
    • 当两个字符串某个相同位置出现不同,即可得出它们顺序,无需继续比较剩余字符。
  • 第二步:对该图进行拓扑排序
var alienOrder = function(words) {
  if (words === null || words.length === 0) {
    return null;
  }
  if (words.length === 1) {
    return words[0];
  }

  // 第一步:创建有向图,已邻接表的方式表示
  const adjList = {};

  for(let i = 0; i < words.length - 1; i++) {
    let w1 = words[i];
    let w2 = words[i + 1];

    // 表示一旦出现不同字母,则连接这两个顶点,之后的字符不必处理
    let found = false;

    for(let j = 0; j < Math.max(w1.length, w2.length); j++) {
      let c1 = w1[j], c2 = w2[j];

      // 为每个出现的字符创建顶点
      if (c1) {
        adjList[c1] = adjList[c1] || [];
      }
      if (c2) {
        adjList[c2] = adjList[c2] || [];
      }

      if(c1 && c2 && c1 !== c2 && !found) {
        adjList[c1].push(c2);
        found = true;
      }
    }
  }

  // 第二步:拓扑排序
  let visited = {}; // 记录已经访问过的顶点
  let loop = {}; // 防止有向图中出现环的情况
  let stack = []; // 记录拓扑排序顶点顺序:从某顶点出发,直到访问完其他顶点,才入栈

  for(let key in adjList) {
    if (!visited[key]) {
      if (!topologicalSort(adjList, key, visited, loop, stack)) {
        return '';
      };
    }
  }

  return stack.reverse().join('');
};

// 深度优先方式实现拓扑排序
function topologicalSort(adjList, v, visited, loop, stack) {
  visited[v] = true;
  loop[v] = true;

  if (adjList[v]) {
    for(let i = 0; i < adjList[v].length; i++) {
      let u = adjList[v][i];

      // 如果本轮访问中,u 已经被访问过,说明有环出现,则返回 false
      if (loop[u]) {
        return false;
      }

      // 如果 u 没被访问过,则开始递归
      if(!visited[u]) {
        if (!topologicalSort(adjList, u, visited, loop, stack)) {
          return false; // 如果递归访问时,出现环,则返回 false
        }
      }
    }
  }

  // 从 v 出发,访问完所有顶点,则此轮访问没有环,置为 false
  loop[v] = false;

  // 等所有依赖 v 的节点遍历完,v 才入栈
  stack.push(v);
  return true; // 返回 true,表示没有发现环
}