解密火星文:LeetCode 269 题详解与 Swift 实现
文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 构建图(Graph)
- 拓扑排序(Topological Sort)
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 实际场景类比
- 总结
摘要
这篇文章我们来聊聊 LeetCode 269 题:火星词典(Alien Dictionary)。虽然题目看起来像是在编造一个星球语言,但本质其实是在考察有向图 + 拓扑排序。我们会用 Swift 语言实现一个完整的解法,并通过实际测试场景来验证代码的有效性。同时,还会结合日常开发中“依赖优先级”和“版本管理”等场景类比,帮你更容易理解题目的应用背景。
描述
在一个火星文明中,他们的字母顺序和我们地球的不一样。现在火星人给你一个词典,里面的词都是按照他们的字母顺序排好的。你的任务就是——猜出这个火星语言的字母顺序是什么。
这个词典是一个字符串数组,比如:
let words = ["wrt", "wrf", "er", "ett", "rftt"]
这些字符串就是按火星字典顺序排好的一组词。
我们要根据这些词之间的字母差异,推导出一个可能的字母顺序,比如:
输出: "wertf"
当然,如果有矛盾(比如顺序冲突或者成环),就说明无解,应该返回空字符串。
题解答案
func alienOrder(_ words: [String]) -> String {var graph = [Character: Set<Character>]()var inDegree = [Character: Int]()// 初始化所有字符for word in words {for char in word {graph[char] = Set<Character>()inDegree[char] = 0}}// 根据相邻词构建有向图for i in 0..<words.count - 1 {let first = words[i]let second = words[i + 1]let minLen = min(first.count, second.count)var foundOrder = falsefor j in 0..<minLen {let a = first[first.index(first.startIndex, offsetBy: j)]let b = second[second.index(second.startIndex, offsetBy: j)]if a != b {if !graph[a]!.contains(b) {graph[a]!.insert(b)inDegree[b]! += 1}foundOrder = truebreak}}// 无效的前缀情况(如 ["abc", "ab"])返回空if !foundOrder && first.count > second.count {return ""}}// 拓扑排序var queue = Array(inDegree.filter { $0.value == 0 }.map { $0.key })var result = ""while !queue.isEmpty {let node = queue.removeFirst()result.append(node)for neighbor in graph[node]! {inDegree[neighbor]! -= 1if inDegree[neighbor]! == 0 {queue.append(neighbor)}}}return result.count == inDegree.count ? result : ""
}
题解代码分析
我们可以把这题拆解成两个步骤:
构建图(Graph)
这就像我们要建立“谁依赖谁”的关系。我们扫描相邻两个词,比如 "wrt"
和 "wrf"
,找到第一个不同的字母 t
和 f
,我们就可以得出一条边 t -> f
,表示 t
在 f
前面。
同时我们也记录每个字母的入度(被多少个其他字母依赖),为下一步做准备。
拓扑排序(Topological Sort)
这一步和“课程表安排”很像。我们找出所有入度为 0 的字符(没有前置依赖的),加入队列,然后不断把它们的“后继节点”的入度减 1。只要哪个节点的入度变成了 0,也加入队列。最终,我们可以排出一个合法的字符顺序。
如果最后排出来的字符个数不等于总字符数,那说明有环(依赖冲突),我们就返回空字符串。
示例测试及结果
print(alienOrder(["wrt", "wrf", "er", "ett", "rftt"]))
// 输出: "wertf"print(alienOrder(["z", "x"]))
// 输出: "zx"print(alienOrder(["z", "x", "z"]))
// 输出: ""(有环)print(alienOrder(["abc", "ab"]))
// 输出: ""(非法前缀)
这些测试用例基本覆盖了以下几种场景:
- 普通字母推理(按字母差异建图)
- 存在环(例如
"z" -> "x"
,又"x" -> "z"
) - 非法词典排序(前缀冲突)
时间复杂度
- 时间复杂度:O©,其中 C 是所有字符出现的总次数。我们需要遍历每个字符、每对单词比较,并做一次拓扑排序。
- 在最坏的情况下,我们每对字符串都可能建立一个边,所以图构建的复杂度是 O©,排序也是 O©。
空间复杂度
- 空间复杂度:O(U + E),U 是不同的字母数,E 是图中边的数量。
- 我们使用字典来存图和入度统计,队列用于拓扑排序中间状态。
实际场景类比
你可以把这题理解成一个“依赖管理系统”:
- 每个字母就像一个模块。
- 字典中的词组就像不同的版本组合。
- 根据不同模块在版本中出现的先后顺序,我们可以倒推出它们的依赖关系。
- 而你最后输出的字符串,就是所有模块的加载顺序。
类似的情况你可能在这些地方遇到:
- 构建工具的依赖排序(比如 SwiftPM / CocoaPods)。
- 操作系统驱动加载顺序。
- 前端资源按依赖顺序加载 JS/CSS。
总结
这道题表面是外星人的语言,其实核心考点是如何从局部规则推导出全局顺序,典型的图论思路。在实际项目中,我们常常会遇到“模块依赖冲突”、“加载顺序错乱”的问题,能写出拓扑排序的思维,也能帮助我们理清复杂系统中的“先来后到”。
相关文章:

解密火星文:LeetCode 269 题详解与 Swift 实现
文章目录 摘要描述题解答案题解代码分析构建图(Graph)拓扑排序(Topological Sort) 示例测试及结果时间复杂度空间复杂度实际场景类比总结 摘要 这篇文章我们来聊聊 LeetCode 269 题:火星词典(Alien Dictio…...

动态规划-62.不同路径-力扣(LeetCode)
一、题目解析 机器人只能向下或向左,要从Start位置到Finish位置。 二、算法原理 1.状态表示 我们要求到Finish位置一共有多少种方法,记Finish为[i,j],此时dp[i,j]表示:到[i,j]位置时,一共有多少种方法,满…...

5月9号.
v-for: v-bind: v-if&v-show: v-model: v-on: Ajax: Axios: async&await: Vue生命周期: Maven: Maven坐标:...

从 Git 到 GitHub - 使用 Git 进行版本控制 - Git 常用命令
希望本贴能从零开始带您一起学习如何使用 Git 进行版本控制,并结合远程仓库 GitHub。这会是一个循序渐进的指南,我们开始吧! 学习 Git 和 GitHub 的路线图: 理解核心概念:什么是版本控制?Git 是什么&…...
何时需要import css文件?怎么知道需要导入哪些css文件?为什么webpack不提示CSS导入?(导入css导入规则、css导入规范)
文章目录 何时需要import css文件?**1. 使用模块化工具(如 Webpack、Vite、Rollup 等)****适用场景:****示例:****优点:** **2. 动态加载 CSS(按需加载)****适用场景:***…...

双指针算法详解(含力扣和蓝桥杯例题)
目录 一、双指针算法核心概念 二、常用的双指针类型: 2.1 对撞指针 例题1:盛最多水的容器 例题2:神奇的数组 2.2 快慢指针: 例题1:移动零 例题2:美丽的区间(蓝桥OJ1372) 3.总…...

【网络编程】二、UDP网络套接字编程详解
文章目录 前言Ⅰ. UDP服务端一、服务器创建流程二、创建套接字 -- socketsocket 属于什么类型的接口❓❓❓socket 是被谁调用的❓❓❓socket 底层做了什么❓❓❓和其函数返回值有没有什么关系❓❓❓ 三、绑定对应端口号、IP地址到套接字 -- bind四、数据的发送和接收 -- sendto…...

【应急响应】- 日志流量如何分析?
【应急响应】- 日志流量如何下手?https://mp.weixin.qq.com/s/dKl8ZLZ0wjuqUezKo4eUSQ...
虚拟机设置NAT没网笔记
查看任务管理器的时候发现,VMware NAT Service 进程都没有,貌似是因为之前我给禁了,emmmmmm 1确认虚拟机网络设置的NAT模式 打开 VMware Workstation,点击 编辑 > 虚拟网络编辑器 确保 VMnet8 配置为 NAT 模式: …...

djinn: 3靶场渗透
djinn: 3 来自 <https://www.vulnhub.com/entry/djinn-3,492/> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182,靶场IP192.168.23.243 3࿰…...

VS Code配置指南:打造高效的QMK开发环境
VS Code配置指南:打造高效的QMK开发环境 前言 你是否曾为QMK固件开发环境的搭建而头疼不已?本文将手把手教你使用Visual Studio Code(简称VS Code)这款强大的代码编辑器来构建一个完美的QMK开发环境,让你的键盘固件开…...

服务器多客户端连接核心要点(1)
刷题 服务器多客户端连接核心要点 多进程服务器 实现原理 fork子进程:每次accept新客户端后,调用fork创建子进程。独立处理:子进程负责与客户端通信(如read/write),父进程继续监听新连接。 特点 隔离性…...
【Python-Day 11】列表入门:Python 中最灵活的数据容器 (创建、索引、切片)
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

Stagehand:AI驱动的下一代浏览器自动化框架
Stagehand 是一个结合了 AI 代理、AI 工具和 Playwright 的浏览器自动化框架。核心理念是:让自动化任务既可控又智能。与传统工具不同,Stagehand 不仅仅依赖 AI 代理的“黑箱操作”,而是通过与 Playwright 的深度结合,赋予开发者对…...
实现线程的4种方法
知识点详细说明 在Java中,实现线程的常用方法有以下四种: 1. 继承Thread类 核心要点: 定义一个类继承Thread,重写run()方法。通过调用start()启动线程(自动执行run())。关键细节: 单继承限制:Java不支持多继承,若类已继承其他类,无法再继承Thread。线程对象直接使用…...

爱普生FA-238在车身控制模块中的应用
在汽车智能化、电子化飞速发展的当下,车身控制模块(BCM)作为车辆的 “智能管家”,肩负着协调和控制众多车身功能的重任,从车门的解锁与锁定、车窗的升降,到车灯的智能点亮与熄灭,再到雨刮器的自…...
单片机嵌入式按键库
kw_btn库说明 本库主要满足嵌入式按键需求,集成了常用的按键响应事件:高电平、低电平、上升沿、下降沿、单击、双击、长按键事件。可以裸机运行,也可以配合实时操作系统运行。 本库开源连接地址:连接 实现思路 本库采用C语言进行…...

【A2A】管中窥豹,google源码python-demo介绍
前言 A2A(Agent2Agent)是 Google 推出的一项新协议,旨在解决多智能体(Multi-Agent)系统中跨平台、跨组织协作的难题。它为 AI 代理之间的通信、协作和任务分工提供了一个统一的标准,可以类比为网页世界的 H…...

004-nlohmann/json 快速认识-C++开源库108杰
了解 nlohmann/json 的特点;理解编程中 “数据战场”划分的概念;迅速上手多种方式构建一个JSON对象; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据…...

Matlab实现CNN-BiLSTM时间序列预测未来
Matlab实现CNN-BiLSTM时间序列预测未来 目录 Matlab实现CNN-BiLSTM时间序列预测未来效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CNN-BiLSTM时间序列预测未来; 2.运行环境Matlab2023b及以上,data为数据集,单变量时间序…...

C语言| sizeof(array)占多少字节
C语言| 数组名作为函数参数 sizeof(数组名); 可以求出整个数组在内存中所占的字节数。 被调函数Array_Sum()中,数组array使用sizeof会得到多少? 实参数组a占32字节,实参a传给形参array,只占4字节。 原因如下: 数组名做…...

【文件系统—散列结构文件】
文章目录 一、实验目的实验内容设计思路 三、实验代码实现四、总结 一、实验目的 理解linux文件系统的内部技术,掌握linux与文件有关的系统调用命令,并在此基础上建立面向随机检索的散列结构文件;## 二、实验内容与设计思想 实验内容 1.设…...

World of Warcraft [CLASSIC][80][Deluyia] [Fragment of Val‘anyr]
瓦兰奈尔的碎片 [Fragment of Valanyr] 有时候下个班打个游戏,没想到套路也这么多,唉,何况现实生活,这一个片版本末期才1000G,30个,也就30000G,时光徽章等同月卡15000G,折合一下也就…...

数组和指针典型例题合集(一维数组、字符数组、二维数组)
1.一维数组 数组名的理解 数组名是数组首元素(第一个元素)的地址 但是有两个例外: 1.sizeof (数组名)—— 数组名表示整个数组,就算的是整个数组的大小,单位是字节。 2.&数组名 —— 数…...

地级市-机器人、人工智能等未来产业水平(2009-2023年)-社科数据
地级市-机器人、人工智能等未来产业水平(2009-2023年)-社科数据https://download.csdn.net/download/paofuluolijiang/90623814 https://download.csdn.net/download/paofuluolijiang/90623814 此数据集统计了2009-2023年全国地级市在机器人、人工智能等…...

epub格式转txt格式工具,txt批量转PDF
epub格式转txt格式工具,功能如图: txt格式批量转PDF 参考原文:epub格式转txt格式工具,txt批量转PDF 轻轻一点就关注, 好运连连挡不住,点个关注吧。...

电赛经验分享——模块篇
1、前言 打算在这一个专栏中,分享一些本科控制题电赛期间的经验,和大家共同探讨,也希望能帮助刚刚参加电赛的同学,了解一些基本的知识。一些见解和看法可能不同或有错误,欢迎批评指正。 在本文中,主要介绍笔…...
前端面试宝典---JavaScript import 与 Node.js require 的区别
import 和 require 来自不同的规范: import 是 ES6(ECMAScript 2015)模块系统的一部分,是 JavaScript 语言的标准语法 require 是 CommonJS 规范的一部分,最初为 Node.js 环境设计 加载方式: require() …...

JVM之内存管理(一)
部分内容来源:JavaGuide二哥Java 图解JVM内存结构 内存管理快速复习 栈帧:局部变量表,动态链接(符号引用转为真实引用),操作数栈(存储中间结算结果),方法返回地址 运行时…...

鸿蒙编译boost整合linux跨平台应用
openharmony deveco 4.1支持armeabi-v7a deveco 5.0后不支持arm32位系统 boost编译 使用deveco的写cmake集成boost boost使用1.88的最新版本,带cmake工具链 https://github.com/boostorg/boost.git boost的源码都在sub_module中 deveco 4.1的版本sdk最高到9&am…...