一、题设
给你一个字符串 path
,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/'
开头),请你将其转化为更加简洁的规范路径。
在 Unix 风格的文件系统中,一个点(.
)表示当前目录本身;此外,两个点 (..
) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//'
)都被视为单个斜杠 '/'
。 对于此问题,任何其他格式的点(例如,'...'
)均被视为文件/目录名称。
请注意,返回的 规范路径 必须遵循下述格式:
- 始终以斜杠
'/'
开头。 - 两个目录名之间必须只有一个斜杠
'/'
。 - 最后一个目录名(如果存在)不能 以
'/'
结尾。 - 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含
'.'
或'..'
)。
返回简化后得到的 规范路径 。
示例 1:
输入:path = "/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:path = "/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。
示例 3:
输入:path = "/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:path = "/a/./b/../../c/" 输出:"/c"
二、基本思路
拿到题后我第一反应是面向if语句编程哈哈哈哈,其实就是分情况讨论:1.遇到“..”怎么办 ? 2.遇到“.”怎么办 ?3.栈空时对前面两种情况有影响吗?需要做什么额外的操作?4. 没有目录名怎么办? 5.针对path ="/../"(后来发现)
1.遇到“..”怎么办 ?:弹出上一个保存的目录名(栈非空时),栈空时不用操作;由这里就会很容易想到要使用栈。
2.遇到“.”怎么办 ?:不用进行任何操作,所以跳过就可以(与栈空没有影响)。
4.没有目录名怎么办? :直接返回 ‘ / ’。
5.针对path ="/../"(后来发现) :return res if res else '/'
很伤心,看了官方的题解,真的很简单,我不明白我第一种想法为何会这么愚蠢,官方用到了split方法直接分隔出 ' / ' 与 ’ / ' 的元素,然后表格中大致有这几种元素:1.目录名 2.‘ .. ’ 3. ' . ' 4. 空白。当遇到‘ .. ’时,则弹出栈顶元素;当遇到目录时(不为空白且有值且不为‘ .. ’),则加入栈中。
三、代码实现
def simplifyPath(self, path):
i = 0
stack = []
tag = True
while i < len(path):
# 1.如果是..:出栈
# 2.如果是.:跳过
# 3.如果遇到/跳过,斜杠之间存入栈中
# 4.如果第一个值是home则
if path[i] == '/':
while i < len(path) and path[i] == '/':
i += 1
start = i
# 针对path = "/"这种情况
if start == len(path) and tag:
return '/'
tag = False
while i < len(path) and path[i] != '/':
i += 1
end = i
tmp = path[start:end] # 目录名
if tmp == '..':
if stack:
stack.pop()
continue
else:
continue
elif tmp == '.' or tmp == '':
continue
stack.append(tmp)
res = ""
for i in range(len(stack)):
res += '/'+stack[i]
# 针对"/../"
return res if res else '/'
优化:
def simplifyPath(self, path):
names = path.split("/")
res = []
for name in names:
if name == "..":
if res:
res.pop()
elif name and name != ".":
res.append(name)
return "/" + "/".join(res)
这里产生了一个好奇的地方:在 if name == “ .. ” : 时,为啥不能写成并列条件:
if name == ".." and res:
res.pop()
原因就是当 path = "/../ " 时,会进 elif 加入栈中,这样结果就不对了。