[题目][栈] 简化路径

简化路径

以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径

请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/simplify-path
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

路径简化并不算复杂,规则无非就几点:

  1. 保留一个斜杠,即不得有连续的/出现
  2. /./表示本路径,可以 使用/表示
  3. /a/../..表示上一层路径
  4. 其他都不需要处理

明显,解决问题的时候,只需要按照这个规则描述即可,不难发现我们只需要处理/.的问题处理即可,那么很简单,只需要保证/前面没有/,而遇到点则处理。

不难发现,对于.来说,除了路径的最后,其前后都需要有/才生效,否则无任何效果。那么对于.的处理应该放在发现/的时候处理,而遇到.时,前面不是/则不予于理会。

不过我们需要一样东西来记录最终的答案,题目属于栈类的题目,我们可以使用栈来作为存储容器。

看代码:

class Solution {
public:
    string simplifyPath(string path) {
        string res;
        std::deque<char> deq;
        int dot_count{0};

        path += '/';

        for (size_t i{0}; i<path.size(); ++i) {
            char ch = path[i];
            switch (ch) {
                case '/':
                    if (0 != dot_count) {
                        if ('.' != path[i-1]) {
                            --i;
                            continue;
                        }

                        switch (dot_count) {
                            case 2: {
                                // 向前
                                int slash{0};
                                while (2 != slash && !deq.empty()) {
                                    if ('/' == *(deq.end() - 1)) {
                                        slash += 1;
                                    }
                                    deq.pop_back();
                                }
                                deq.push_back('/');
                                break;
                            }
                            case 1:
                                deq.pop_back();
                                break;
                            default:
                                break;
                        }
                        dot_count = 0;
                    }

                    if (!deq.empty() && '/' == *(deq.end() - 1)) {
                        deq.pop_back();
                    }
                    deq.push_back('/');
                    break;
                case '.':
                    if ('/' == *(deq.end() - 1)) {
                        deq.push_back('.');
                        dot_count = 1;
                        continue;
                    }
                    deq.push_back('.');
                    if (0 != dot_count) {
                        dot_count+=1;
                    }
                    break;
                default:
                    dot_count = 0;
                    deq.push_back(ch);
                    break;
            }
        }

        if (1 != deq.size() && '/' == *(deq.end()-1)) {
            deq.pop_back();
        }

        while (!deq.empty()) {
            res += *(deq.begin());
            deq.pop_front();
        }

        return res;
    }
};    

用例跑完8ms,还算可以,超77%

首先给原有的字符串添加了/在末尾,这是为了处理原字符串末尾没有/的情况,如果末尾是.,按照里面的代码的思路 会将末尾的.忽略掉,最简单的办法是照一个可以被处理的符号在末尾,/很适合。

还有其他的解法,可以尝试将字符串分割,分隔标识符为/,那么最终需要处理的是.,似乎更加简单。

不过现在停留在栈的学习中,对于对应标签的题目尝试使用纯粹的题目来解题。

查看了下golang里面filepath中的Clean函数:

func Clean(path string) string {
    originalPath := path
    volLen := volumeNameLen(path)
    path = path[volLen:]
    if path == "" {
        if volLen > 1 && originalPath[1] != ':' {
            // should be UNC
            return FromSlash(originalPath)
        }
        return originalPath + "."
    }
    rooted := os.IsPathSeparator(path[0])

    // Invariants:
    //    reading from path; r is index of next byte to process.
    //    writing to buf; w is index of next byte to write.
    //    dotdot is index in buf where .. must stop, either because
    //        it is the leading slash or it is a leading ../../.. prefix.
    n := len(path)
    out := lazybuf{path: path, volAndPath: originalPath, volLen: volLen}
    r, dotdot := 0, 0
    if rooted {
        out.append(Separator)
        r, dotdot = 1, 1
    }

    for r < n {
        switch {
        case os.IsPathSeparator(path[r]):
            // empty path element
            r++
        case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
            // . element
            r++
        case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
            // .. element: remove to last separator
            r += 2
            switch {
            case out.w > dotdot:
                // can backtrack
                out.w--
                for out.w > dotdot && !os.IsPathSeparator(out.index(out.w)) {
                    out.w--
                }
            case !rooted:
                // cannot backtrack, but not rooted, so append .. element.
                if out.w > 0 {
                    out.append(Separator)
                }
                out.append('.')
                out.append('.')
                dotdot = out.w
            }
        default:
            // real path element.
            // add slash if needed
            if rooted && out.w != 1 || !rooted && out.w != 0 {
                out.append(Separator)
            }
            // copy element
            for ; r < n && !os.IsPathSeparator(path[r]); r++ {
                out.append(path[r])
            }
        }
    }

    // Turn empty string into "."
    if out.w == 0 {
        out.append('.')
    }

    return FromSlash(out.string())
}

其主要重点也是在处理...,并不复杂,golang实现起来确实优雅。

Mark一个,可以仿照来做一个C++版本的。

全部评论

相关推荐

行云流水1971:优化后简历(以 “后端开发岗” 为目标) 基本信息 姓名:XXX | 电话:XXX | 邮箱:XXX 求职意向:后端开发工程师 | 意向城市:XXX 教育经历 2023.09-2027.07 XX 大学 | 计算机科学与技术 | 本科 核心课程:Java 程序设计、数据库原理、计算机网络、数据结构(成绩均 85+) 技能关联:掌握 Java 基础语法、MySQL 增删改查,为后端开发奠定技术基础 项目经历 项目 1:小说推荐 - 大数据智能推荐平台 | 后端开发 | 2025.09-2025.12 技术栈:Java、SpringBoot、MySQL、Redis、Kafka 核心动作: 参与用户行为数据采集模块开发,用 Kafka 实现日志数据异步传输,峰值吞吐量提升 40%; 基于 MySQL 设计用户 - 小说关联表,配合 Redis 缓存热门推荐列表,页面响应时长从 300ms 缩短至 120ms; 成果:支撑日均 1000 + 用户访问,推荐内容点击率较初始版本提升 25%。 项目 2:在线博客 - 个性化博客分享平台 | 后端开发 | 2025.03-2025.06 技术栈:Java、SpringBoot、MyBatis、MySQL 核心动作: 开发博客发布 / 编辑接口,通过 MyBatis 实现数据持久化,接口成功率达 99.8%; 设计用户权限控制逻辑,区分普通用户 / 管理员操作权限,避免非法内容发布; 成果:完成 5 个核心功能模块开发,实现博客内容的全流程管理。 技能证书 技术栈:熟练使用 Java、SpringBoot、MyBatis 进行后端开发;掌握 MySQL 数据库设计与优化、Redis 缓存应用 工具:Git 版本管理、Postman 接口测试 自我评价 具备 Java 后端开发基础,参与 2 个完整项目的后端模块开发,能独立完成接口编写、数据持久化等工作;熟悉 SpringBoot 等主流框架,可快速上手企业级开发流程,具备良好的代码规范与逻辑思维。 需要我帮你补充项目的量化成果细节(比如接口性能、用户数据等)吗?若需要更精准的岗位适配优化,可私信沟通。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务