链接 | 简化路径 |
---|---|
题序号 | 71 |
题型 | 字符串 |
解法 | 栈 |
难度 | 中等 |
熟练度 | ✅✅✅ |
题目
-
给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 ‘/’ 开头),请你将其转化为 更加简洁的规范路径。
-
在 Unix 风格的文件系统中规则如下:
一个点 ‘.’ 表示当前目录本身。
此外,两个点 ‘…’ 表示将目录切换到上一级(指向父目录)。
任意多个连续的斜杠(即,‘//’ 或 ‘///’)都被视为单个斜杠 ‘/’。
任何其他格式的点(例如,‘…’ 或 ‘…’)均被视为有效的文件/目录名称。 -
返回的 简化路径 必须遵循下述格式:
始终以斜杠 ‘/’ 开头。
两个目录名之间必须只有一个斜杠 ‘/’ 。
最后一个目录名(如果存在)不能 以 ‘/’ 结尾。
此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 ‘.’ 或 ‘…’)。
返回简化后得到的 规范路径 。 -
示例 1:
输入:path = “/home/”
输出:“/home”
解释:
应删除尾随斜杠。 -
示例 2:
输入:path = “/home//foo/”
输出:“/home/foo”
解释:
多个连续的斜杠被单个斜杠替换。 -
示例 3:
输入:path = “/home/user/Documents/…/Pictures”
输出:“/home/user/Pictures”
解释:
两个点 “…” 表示上一级目录(父目录)。 -
示例 4:
输入:path = “/…/”
输出:“/”
解释:
不可能从根目录上升一级目录。 -
示例 5:
输入:path = “/…/a/…/b/c/…/d/./”
输出:“/…/b/d”
解释:
“…” 在这个问题中是一个合法的目录名。 -
提示:
1 <= path.length <= 3000
path 由英文字母,数字,‘.’,‘/’ 或 ‘_’ 组成。
path 是一个有效的 Unix 风格绝对路径。
题型
- 核心思想:该题使用栈(Stack)这种数据结构来模拟路径的遍历和回退过程。
- 复杂度:时间复杂度是 O(n),其中 n 是路径字符串的长度,因为我们需要遍历整个字符串。空间复杂度也是 O(n),因为我们需要存储路径的有效部分。
- c++ 实现算法:
class Solution {
public:
std::string simplifyPath(std::string path) {
std::vector<std::string> stack;//定义一个栈
//创建了一个 std::stringstream 对象 ss,并将字符串 path 作为其初始内容。
//这样做的目的是为了方便地对路径字符串进行分割和读取操作。
std::stringstream ss(path);
//dir 用于存储从 stringstream 中读取的每个目录
std::string dir;
//使用 getline 函数从 stringstream 中按 / 分割读取路径的每个部分,直到没有更多内容可读。
while (getline(ss, dir, '/')) {
//如果目录为空字符串或为 ".",表示当前目录,不做任何操作,继续下一次循环。
if (dir.empty() || dir == ".") {
continue;
}
//如果目录为 "..",表示返回上一级目录。如果栈不为空,则弹出栈顶元素(即移除上一级目录)。
else if (dir == "..") {
if (!stack.empty()) {
stack.pop_back();
}
}
//否则,将该目录压入栈中。
else {
stack.push_back(dir);
}
}
//初始化一个空字符串 simplified,用于存储简化后的路径。然后遍历栈中的每个目录,
//将其添加到 simplified 字符串中,并在每个目录前加上 /。
std::string simplified;
for (const std::string& d : stack) {
simplified += "/" + d;
}
//simplified 为空字符串,说明路径简化后是一个根目录,返回 /。否则,返回 simplified。
return simplified.empty() ? "/" : simplified;
}
};
- 算法推演:
初始化
stack:[]
ss:/a/./b/…/…/c/遍历路径
读取第一个部分:“”
空字符串,忽略。
stack:[]读取第二个部分:“a”
将 “a” 压入栈。
stack:[“a”]读取第三个部分:“.”
当前目录,忽略。
stack:[“a”]读取第四个部分:“b”
将 “b” 压入栈。
stack:[“a”, “b”]读取第五个部分:“…”
返回上一级目录,弹出栈顶元素 “b”。
stack:[“a”]读取第六个部分:“…”
返回上一级目录,弹出栈顶元素 “a”。
stack:[]读取第七个部分:“c”
将 “c” 压入栈。
stack:[“c”]构建简化后的路径
初始化 simplified:“”
遍历栈中的每个目录,将其添加到 simplified 字符串中,并在每个目录前加上 /。
simplified:“/c”返回结果
simplified 不为空,返回 “/c”最终结果
简化后的路径为:“/c”
- c++ 完整demo:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
class Solution {
public:
std::string simplifyPath(std::string path) {
std::vector<std::string> stack;//定义一个栈
//创建了一个 std::stringstream 对象 ss,并将字符串 path 作为其初始内容。
//这样做的目的是为了方便地对路径字符串进行分割和读取操作。
std::stringstream ss(path);
//dir 用于存储从 stringstream 中读取的每个目录
std::string dir;
//使用 getline 函数从 stringstream 中按 / 分割读取路径的每个部分,直到没有更多内容可读。
while (getline(ss, dir, '/')) {
//如果目录为空字符串或为 ".",表示当前目录,不做任何操作,继续下一次循环。
if (dir.empty() || dir == ".") {
continue;
}
//如果目录为 "..",表示返回上一级目录。如果栈不为空,则弹出栈顶元素(即移除上一级目录)。
else if (dir == "..") {
if (!stack.empty()) {
stack.pop_back();
}
}
//否则,将该目录压入栈中。
else {
stack.push_back(dir);
}
}
//初始化一个空字符串 simplified,用于存储简化后的路径。然后遍历栈中的每个目录,
//将其添加到 simplified 字符串中,并在每个目录前加上 /。
std::string simplified;
for (const std::string& d : stack) {
simplified += "/" + d;
}
//simplified 为空字符串,说明路径简化后是一个根目录,返回 /。否则,返回 simplified。
return simplified.empty() ? "/" : simplified;
}
};
int main() {
Solution solution;
std::string path = "/home//foo/";
std::string simplifiedPath = solution.simplifyPath(path);
std::cout << "Simplified Path: " << simplifiedPath << std::endl;
return 0;
}