简化路径

算法标签:栈、字符串

给我们一个路径,要求把文件路径化简,给定的路径一定是合法的 Linux 路径,一个合法的 Linux 路径一般从 / 开始,/ 表示根目录,有很多的子目录 home、y,"." 表示在当前目录(一个 "." 没有任何变化), ".." 表示返回上一级目录

如果一个路径是 / home / a / .. / b,根目录 → home → a → b,化简其实就是把所有的 "." 和 ".." 去掉,当前案例去掉就是      / home / b

注意特殊情况:斜杠 " / " 可能会有连续多个,需要把连续多个斜杆换成一个斜杆,如果在根目录返回上一级目录的话,由于根目录已经是最靠上的一个目录了,不能再返回,如果从根目录往上再返回还是根目录,保证输入的目录一定是合法的目录,所以不需要考虑不合法的情况

注意输出的时候,如果不是根目录,最后的斜杆不用加,如果是根目录就返回一个斜杆

参考

LeetCode 71 简化路径 AcWing

res表示当前的路径,name表示遇到两个'/'之间的字符

1.遇到". .",回到上一个目录,即将res最后一个以"/"开始往后的字符全部删去
    2.遇到"."或者多余的"/"不做处理
    3.其他表示可以延伸新的路径,将"/" + name字符串加入到res后面

模拟样例

path = "/a/./b/../../c/"

实现的时候类似一个栈,所有的操作要么是从当前目录往下走一格,要么就是从当前目录返回上一格,整个数据结构只会发生在栈顶的位置,要么向栈顶插入一个元素,要么从栈顶弹出一个元素,实现的时候不需要模拟栈,直接使用字符串就可以了,用 string 表示当前的目录,根据当前目录的格式进行操作,如果是 "." 的话不用变化,如果是 ".." 返回上一级目录,如果不是以上两种情况就往下走一格,运用栈的思想

时间复杂度分析

整个字符串只会扫描一遍,每一个目录最多只会进栈出栈一次,所以时间复杂度是线性的

class Solution {
public:string simplifyPath(string path) {//定义最终答案路径 记录当前文件名string res, name;//统一格式 如果 path 最后不是 "/" 就补上一个 "/" 在读取每一个文件名的时候是读到 "/" 截止 保证最后一个文件名也用类似的方式去读if (path.back() != '/') path += '/';//从前往后扫描每一个字符for (auto c : path) {//如果当前字符不是 "/" 说明当前文件名还没有完 需要把它加到文件名中if (c != '/') name += c;else {//否则说明当前字符是 "/" 表示已经读取出完整的文件名 文件名放到name里面//处理特殊情况 如果是 ".." 返回上一级目录if (name == "..") {//res不为空并且res最后一个字符不是 "/" 就弹出最后一个字符while (res.size() && res.back() != '/') res.pop_back();//res不为空把 "/" 去掉if (res.size()) res.pop_back();} //如果 name 是 "." 或者是空 "//" 不用处理else if (name != "." && name != "") {//表示可以延伸新的路径,将"/" + 当前文件名插到路径最后res += '/' + name;}//把name清空name.clear();}}//空字符串表示在根目录的位置if (res.empty()) res = "/";return res;}
};

编辑距离

算法标签:字符串、动态规划

给出两个单词 word1 和 word2,要将 word1 转换成 word2,可以对任何一个单词进行 3 种操作,可以在任意位置插入一个字符,可以将任意一个字符删除,可以将任意一个字符替换成另外一个字符,问最少需要进行多少次操作可以将 word1 变成 word2?

模拟样例

经典 dp 问题

两个字符串用两维来表示

①所有的操作方案有很多种,不会出现先插入一个字符,再把这个字符删除的操作,因为这样相当于无缘无故多了两次操作,一定不是最优解

不会出现在原字符串 "abc" 上加上 "b",再把 "b" 去掉

②操作的顺序不会影响答案,例如 字符串abc,先把 a 删除再在 b 和 c 之间加一个 x 与 先在 b 和 c 之间加一个 x 再把 a 删除的效果其实是一样的

总共的操作有很多种,但是我们在考虑的时候,只需要考虑其中的一小部分就可以了,这一小部分首先没有多余操作,不会出现加一个删一个,也不会出现把一个字母变两次

第二要注意的是在考虑的时候可以不考虑顺序,可以人为规定一个顺序,操作的顺序对答案的个数是没有影响的,可以固定一个顺序,固定操作顺序是从前往后进行的

在以上的分析基础上再去考虑这个 dp 问题

状态表示 f[ i ][ j ] 要从两个角度来考虑,第一个角度是要考虑的是 f[ i ][ j ] 表示哪个集合,第二个要考虑的是 f[ i ][ j ] 表示的是这个集合的哪个属性

我们只需要考虑操作是从前往后进行的方案,不需要考虑操作顺序会变化的情况

f[ i ][ j ] 表示所有操作方案里面操作步数的最小值

集合划分的方式求最小值

看 f[ i ][ j ] 这个集合可以分成哪些子集,然后分别求每个子集的最小值,这些最小值取一个 min 就是答案

划分其实就是找最后一个不同点,把 a[ 1,j ] 变成 b[ 1, j ] 最后一步一共有多少种操作方案,然后我们按照最后一步把它分成若干种

由于我们是考虑按顺序进行的所有方案,所以我们只需要考虑在最后一步进行的操作就可以了,不需要考虑在中间存在的情况

最后一个操作有多少种呢?

首先每一个字符串都有三种操作,两个字符串就有 6 种操作,所以我们一共有 6 种不同的操作

先看第一种情况,先看只操作第一个字符串的情况,只操作第一个字符串有三种操作方案,第一种操作方案,删除 a 的最后一个字符,删除后 a[ 1,i ] 变成 b[ 1,j ],也就是去掉 ai 之后,第一个字符串等于第二个字符串,也就意味着这个操作方案的前面若干步,已经可以把 a[ 1,i - 1 ] 变成 b[ 1,j ],去掉 ai 之前,a[ 1,i -  1 ] 已经等于 b[ 1,j ],所有把 a[ 1,i -  1 ] 已经变成 b[ 1,j ] 的最小步数加 1,也就是 f(i - 1,j) + 1

第二种情况在 a 的后面加上一个字符,加上之后使得第一个字符串和第二个字符串相等,加上的那个字符必然是 bj,加之前已经有 a(1,i) 等于 b(1,j - 1),在 ai 后面补上一个 bj,这样两个字符串就相等了,如果在 a 后面加上一个字符的话,总共的最小值应该是 f(i,j - 1) + 1

第三种情况,修改 ai,把 ai 变成 bj,修改之后 ai 和 bj 相等,修改之前,a 的前 i - 1 个字符和 b 的前 i - 1 个字符相等,如果 a[ i ] 和 b[ j ] 相等的话也可以不用改,如果不相等就需要加上 1

对于 a 的操作有三种情况,对于 b 的操作也有类似的三种情况,如果删除 b 的最后一个字符,在删除之前就已经有 a 的第 ai 个字符和 b 的第 j - 1 个字符相等

由上面的推导,一共有 6 种情况

但是仔细观察,我们可以发现只有 3 大类,状态数量为 n^2 级别,转移数量只有 3 个,整个的计算量就是 3n^2,也就是 n^2

class Solution {
public:int minDistance(string a, string b) {//先求两个字符串长度int n = a.size(),m =b.size();//为了方便 让两个字符串的下标都是从 1 开始 先给两个字符串的前面补上一个空格a = ' ' + a,b = ' ' + b;//定义状态数组vector<vector<int>> f(n + 1,vector<int>(m + 1));//初始化边界 当第一个字符串长度是 0 的时候,另一个字符串长度是多少就需要操作多少次for(int i = 1;i <= n;i++ ) f[i][0] = i;//a、b两个字符串的边界都需要初始化for(int i = 1;i <= m;i++ ) f[0][i] = i;//求所有状态的值for(int i = 1;i <= n;i++ )for(int j = 1;j <= m;j++ ) {//两个状态f[i][j] =  min(f[i - 1][j],f[i][j - 1]) + 1;//最后一个状态 用t来表示加的是1还是0 如果a[i]不等于b[j]表示要加的是1 如果相等表示不需要修改要加的是0int t = a[i] != b[j];f[i][j] = min(f[i][j],f[i - 1][j - 1] + t);}return f[n][m];}
};

矩阵置零

算法标签:数组、哈希表、矩阵

给我们一个 n × m 的矩阵,里面有一些是 0、有一些是 1 或者其他数字,需要修改矩阵里面的数值,只要有一个元素是 0,就需要将这个元素的所在的行和列都初始化成 0

要求使用原地算法解决,不可以额外开数组解决这个问题

本来需要考虑 0 需要将某些位置赋值成 0

现在考虑矩阵某一个位置什么情况下会被赋成 0?

只要这个位置所在的行或列出现 0,这个位置就要赋成 0,如果这个位置所在的行和列都不是 0 就不是 0

如果我们简化一下这个问题,某一个位置上是 0 只将某一行变成 0,这个问题就非常简单啦!

只需要枚举每一行,看这一行有没有 0,如果这一行出现 0,就把这一行变成 0,如果没有出现 0 就不用管

如果只有列的话也很简单,只需要分别看一下每一列,每一列可以用一个 bool  变量,最后判断这一列有没有 0 就可以了

如果行和列交织就不太好做了,因为我们在修改行的时候,其实也会把列修改掉

行和列是相互影响的,在做行变化的时候不能做列变化,在做列变化的时候也不能做行变化

接下来考虑一下怎么去做这个问题?

我们一定会用到一个数组来记录每一行每一列的状态,这个题目只是让我们不要开额外的新数组,所以我们其实可以利用原数组,可以用原数组第一行的数来表示每一列里面有没有 0,用原数组第一列的数来表示这个矩阵每一行有没有 0,当然 (0,0) 位置上的元素是没有用的,这样就可以表示划线部分的 子矩阵 的所有状态了

框起来的矩阵里面的所有情况都记录清楚了,但是还有第一行和第一列的情况没有记录清楚,可以开两个变量记录就可以了,用 r0 记录第一行有没有 0,用 c0 记录第一列有没有 0

综上,我们就只需要用额外的两个变量,以及原矩阵的第一行和第一列,就可以把整个矩阵每一行每一列有没有 0 全部记录清楚了

最后,先看第一列有没有 0 如果第一列有 0 就把第一列全部变成 0,再看第二列有没有 0,如果第 2 列有 0 就把第二列全部变成 0,先把每一列扫描一遍,再看每一行,如果这一行有 0 就把这一行全部变成 0,再扫描一遍赋值就可以了

时间复杂度分析

整个矩阵会扫描很多遍,但是只回扫描常数遍,所以整个算法的时间复杂度是 4 × n^2,用到的空间只有两个变量,所以是 O( 1 )

class Solution {
public:void setZeroes(vector<vector<int>>& matrix) {//矩阵为空返回 falseif(matrix.empty() || matrix[0].empty()) return;//n 表示行数 m 表示列数int n = matrix.size(),m = matrix[0].size();/* 记录 *///求r0第一行和c0第一列 1表示没有0 0表示有0int r0 = 1,c0 = 1;//看第一行有没有0 如果有0的话 r0就是0,如果全是1的话,r0就是1for(int i = 0;i < m;i++ ) if(!matrix[0][i]) r0 = 0;//看第一列有没有0for(int i = 0;i < n;i++ ) if(!matrix[i][0]) c0 = 0;/* 记录 *///看第一列一直到第 n - 1 列for(int i = 1;i < m;i++ ) //对于第 i 列来说,j 从 0 开始,matrix[0][i] 表示第 i 列有没有 0for(int j = 0;j < n;j++ )//处理每一列if(!matrix[j][i]) matrix[0][i] = 0;//处理每一行 matrix[i][0] 表示第 i 行有没有 0for(int i = 1;i < n;i++ )for(int j = 0;j < m;j++ )if(!matrix[i][j]) matrix[i][0] = 0;/* 修改 *///枚举除了第一列之外的每一列 for(int i = 1;i < m;i++ )//如果matrix[0][i]=0 表示第 i 列需要全部赋值成 0 if(!matrix[0][i])for(int j = 0;j < n;j++ )matrix[j][i] = 0;//看一下每一行是不是应该赋值成 0for(int i = 1;i < n;i++ )//如果matrix[i][0]=0  表示第 i 行需要全部赋值成 0 if(!matrix[i][0])for(int j = 0;j < m;j++ )matrix[i][j] = 0;//看第一行是不是应该是 0if(!r0) for(int i = 0;i < m;i++ ) matrix[0][i] = 0;//看第一列是不是应该是 0if(!c0) for(int i = 0;i < n;i++ ) matrix[i][0] = 0;}
};

搜索二维矩阵

算法标签:数组、二分查找、矩阵

题目给出一个矩阵,要求在矩阵中找一个值,先考虑一维的情况,如果给我们一个数组,数组是有序的,让我们在数组中找一个值是不是存在,可以怎么做呢?

由于数组是有序的,所以可以用二分来解决,相当于数组里面的值是递增的,要找一个目标值,每次看中点这个值,如果中点这个值小于等于当前值,说明目标在右侧,把区间缩小到右边就可以了,如果中点的值大于当前值,说明答案在左侧,所以可以二分出来

题目虽然给的是一个矩阵,本质上也是一维的情况

这个矩阵满足每一行从左到右是递增的,同时保证每一行的第一个数大于上一行的最后一个数

求解的时候可以先把矩阵展开成一维,展开成一维的情况是递增的,然后二分就可以了

需要做一个映射,把 n × m 的矩阵的下标想象成从 0 - n × m - 1,二分的 mid 是在 0 - n × m - 1 这个坐标系里面,需要把这个坐标系的里面的某一个下标变成二维的下标,把展开之后的一维数组的下标变回原来二维数组的下标

任取一个下标 t,它在原二维矩阵中的下标是多少呢?

一行有 m 个数,下标从 0 开始,所以是 t 除以 m 行,模以 m 就可以得到列数,这样就得到在原数组里面的下标

其实就是一个一维二分,注意坐标的变换就可以了

class Solution {
public:bool searchMatrix(vector<vector<int>>& matrix, int target) {//为空返回 falseif(matrix.empty() || matrix[0].empty()) return false;//n 表示行数 m 表示列数int n = matrix.size(),m = matrix[0].size();//二分int l = 0,r = m *n - 1;while(l < r){int mid = (l+ r) /2;if(matrix[mid / m][mid % m] >= target) r = mid;else l = mid + 1;}if(matrix[r/ m][r % m] == target) return true;return false;}
};

颜色分类

算法标签:数组、双指针、排序

给我们一个数组,数组里面只有 0、1、2 这 3 个数,需要将数组排序,有两个要求:只扫描一次,仅使用常数空间

本质是一个双指针算法,在扫描的时候,用 i 下标来扫描,i 从左往右扫描,j 也是从左往右扫描,再维护一个 k,k 从右往左扫描,扫描的时候保证从 0 ~ j - 1 这一段全部是 0,从 j ~ i - 1 这一段全部是 1,从 k + 1 ~ n - 1 最后一个字符,这一段全是 2,就可以保证它排好序了

i 每次往后移动一位,移动 i 的时候维护 j 和 k

当 i 超过 k 的时候就可以满足以上条件

接下来看一下如何维护这一点

一开始的时候,i 和 j 都是在起点的位置,k 在最后一个位置

一开始从 0 ~ j - 1 是不存在的,此时 j 为 0,这一段是空的,满足要求,从 j ~ i 是空的,满足要求,最后一段也是空的,满足要求

在扫描的过程中,每次根据 i 的位置来调整就可以了,ai 有三种情况,如果 ai 等于 0,j 指向的位置是 1,把 j 指向的位置的那个 1 变成 0,把 1 移到后面的位置,最后把 j 往后移动一位,此时从 0 ~ j - 1 都是 0,j 移到了下一个 1 的位置,从 j ~ i - 1 都是 1

如果 ai 等于 2,ai 应该放到 k 这个位置,然后 k- -,因为 k 的位置已经是 2,i 不需要变化,因为 i 不一定是 1,有可能是 0

如果 ai 等于 1,i+ + 即可

每次要么是 i ++,要么是 k - -,i 和 k 之间的距离每次都会减 1,最终循环 n 次之后就会结束,由于只扫描了一遍,所以整个算法的时间复杂度是 O( n ),扫描之后 i = k + 1

用三个变量 a、b、c 统计 0、1、2 有多少个,扫描一遍,然后从前往后输出 a 个 0,b 个 1,c 个 2

class Solution {
public:void sortColors(vector<int>& nums) {for(int i = 0,j = 0,k = nums.size() - 1;i <= k;) {if (nums[i] == 0) swap(nums[i ++ ], nums[j++ ]);else if(nums[i] == 2) swap(nums[i], nums[k -- ]);else i ++;}}
};

LeetCode+ 71 - 75相关推荐

  1. LeetCode 71~75

    前言 本文隶属于专栏<LeetCode 刷题汇总>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构请见LeetCode 刷题汇总 正文 幕布 幕 ...

  2. LeetCode 71. Simplify Path

    LeetCode 71. Simplify Path 本博客转载自:https://blog.csdn.net/makuiyu/article/details/44497901 Solution1:没 ...

  3. LeetCode 71. 简化路径(栈)

    1. 题目 以 Unix 风格给出一个文件的绝对路径,你需要简化它.或者换句话说,将其转换为规范路径. 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身:此外,两个点 (..) 表示将目录 ...

  4. LeetCode —— 71.简化路径(Python3)

    以 Unix 风格给出一个文件的绝对路径,你需要简化它.或者换句话说,将其转换为规范路径. 在 Unix 风格的文件系统中,一个点(.)表示当前目录本身:此外,两个点 (-) 表示将目录切换到上一级( ...

  5. 【leetcode】75.颜色分类(多种解法,超详细图文解析)

    75. 颜色分类 难度中等 给定一个包含红色.白色和蓝色,一共 n 个元素的数组,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 此题中,我们使用整数 0. 1 ...

  6. 【leetcode】75. Sort Colors

    题目如下: 解题思路:我的解题思路是遍历数组,遇到0删除该元素并插入到数组头部,遇到1则不处理,遇到2删除该元素并插入到数组尾部. 代码如下: class Solution(object):def s ...

  7. [LeetCode]--71. Simplify Path

    Given an absolute path for a file (Unix-style), simplify it. For example, path = "/home/", ...

  8. [Leetcode][第75题][JAVA][颜色分类][双(三)指针][计数排序]

    [问题描述][中等] [解答思路] 1. 三指针 时间复杂度:O(N) 空间复杂度:O(1) class Solution {public void sortColors(int[] nums) {i ...

  9. 2022-3-19 Leetcode 71. 简化路径

    第一版 class Solution {public:string simplifyPath(string path) {vector<string> mysk;string ret;in ...

最新文章

  1. 微信小程序使用npm 进行下载构建组价
  2. C#(.Net)中调用Sql sever汉字字符串显示为?问号
  3. 计算机应用基础人才培养方案,1. 培养方案(计算机应用基础课程).doc
  4. ajax后台重定向会返回什么_跳转,AJAX返回和重定向
  5. LintCode 378. 将二叉树转换成双链表(非递归遍历)
  6. 牛客16500 珠心算测试
  7. python入门题目及答案_Python基础知识的一些练习与解答,python,部分,习题,及,答案...
  8. mysql的事物隔离机制?
  9. ajax传图片的方法
  10. 相关系数(Correlation coefficient)
  11. 5年码农吐血推荐10款用了就离不开的网站
  12. smartbi v7 Linux,配置Smartbi
  13. VMware虚拟网络编辑器,没有桥接模式或本地计算机不显示网络适配器
  14. 2020年最好用的手机是哪一款_2020年公认最值得入手的3款手机,颜值性能兼具,用三年不亏!...
  15. 【转载】OceanBase架构介绍
  16. 基于微信小程序的水果丨蔬菜丨农产品商城系统源码(SSM与Springboot版本均有)
  17. CIKM 2021 | 淘宝多场景推荐排序模型ZEUS
  18. Ubuntu 16.04下安装Caffe解决 undefined symbol: _ZN5boost6python6detail11init_moduleER11PyModuleDefPFvvE
  19. [iOS] Xcode 5 + iOS 7免证书(iDP)真机调试与生成IPA全攻略
  20. 辅流式沉淀池固体负荷计算方法_辐流式沉淀池设计计算

热门文章

  1. Turbo译码原理说明(一)
  2. LT8711/LT8712 Type-C转HDMI设计方案|替代LT8711/LT8712芯片|GSV2201可完全替代兼容LT8711/LT8712
  3. Pytorch Lightning 之 amp_level
  4. 怎么通过scp把本地文件传到远程树莓派上
  5. 在Windows下完成转录组分析
  6. matlab运行慢cpu低,为什么我的GPU运算速度低于CPU运算速度???
  7. SQL学习笔记——AND,OR,NOT运算符
  8. 将 .eml 格式的文件批量发送给指定用户
  9. xp计算机管理员桌面文件没有权限,WinXP系统设置文件夹权限的操作方法
  10. 分布式系统中的CAP理论,面试必问,你理解了嘛?