博主私藏的LeetCode刷题集合 有些较难的问题都有思路和注释
71. 简化路径
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
示例 1:
输入:"/home/" 输出:"/home" 解释:注意,最后一个目录名后面没有斜杠。 示例 2:
输入:"/../" 输出:"/" 解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。 示例 3:
输入:"/home//foo/" 输出:"/home/foo" 解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。 示例 4:
输入:"/a/./b/../../c/" 输出:"/c" 示例 5:
输入:"/a/../../b/../c//.//" 输出:"/c" 示例 6:
输入:"/a//b////c/d//././/.." 输出:"/a/b/c"
PS:
这里有一个需要注意 /...
这里是当文件夹的名字处理的
class Solution {
public String simplifyPath(String path) {
path="/"+path+"/";
String[] Stack = new String[500]; int top=-1;
int i=0,j=0;
char[] ch=path.toCharArray();
while(i<ch.length&&j<ch.length){
while(i<ch.length&&ch[i]=='/')
i++;
j=i;
while(j<ch.length&&ch[j]!='/')
j++;
if(i==ch.length||j==i)
break;
String cur=path.substring(i,j);
if("..".equals(cur)){
if(top!=-1) --top;
}else if(".".equals(cur)){
/*do nothing*/
}else{
Stack[++top]=cur;
}
i=j;
}
String res="/";
for(int k=0;k<top;k++){
res+=Stack[k]+"/";
}
return res+(top>=0?Stack[top]:"");
}
}
72. 编辑距离
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符 删除一个字符 替换一个字符 示例 1:
输入: word1 = "horse", word2 = "ros" 输出: 3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e') 示例 2:
输入: word1 = "intention", word2 = "execution" 输出: 5 解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
//处理有空字符串的特殊情况
if (len1 * len2 == 0)
return len1 + len2;
String longerStr = len1 > len2 ? word1 : word2;
String shorterStr = len1 > len2 ? word2 : word1;
int shorterOne = Math.min(len1, len2);
//dp数组长度为两字符串中长度较小的那个
int[] dp = new int[shorterOne + 1];
//初始化dp数组
for (int i = 0; i < shorterOne + 1; i++) {
dp[i] = i;
}
//从长度较长的字符串开始遍历,注意j从1开始,取第j-1位字符,因此结束位置是 longerStr.length()
for (int j = 1; j <= longerStr.length(); j++) {
// 每次遍历短字符串前,先给left赋初始值
int left = j;
//遍历长度较短的字符串,同样从1开始,取第i-1位字符,因此结束位置是 shortStr.length()
for (int i = 1; i <= shorterStr.length(); i++) {
int updateDown = dp[i] + 1;
int updateLeft = left + 1;
int updateLeftDown = dp[i - 1];
//如果当前字符不匹配,左下角的那个值要加一,表示替换当前字符
if (longerStr.charAt(j - 1) != shorterStr.charAt(i - 1)) {
updateLeftDown++;
}
//获取较小的那个
int min = Math.min(updateLeft, Math.min(updateDown, updateLeftDown));
//因为 dp[i - 1]后面用不到了,替换为当前的left值
dp[i - 1] = left;
//如果遍历到最后一个时,直接更新dp[i]
if(i == dp.length - 1){
dp[i] = min;
}else{
//否则更新左边的值,而不是直接更新 dp[i],因为下个循环需要用到原来的 dp[i]以及刚更新的left
left = min;
}
}
}
return dp[shorterOne];
}
}
73. 矩阵置零
给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
示例 1:
输入: [ [1,1,1], [1,0,1], [1,1,1] ] 输出: [ [1,0,1], [0,0,0], [1,0,1] ] 示例 2:
输入: [ [0,1,2,0], [3,4,5,2], [1,3,1,5] ] 输出: [ [0,0,0,0], [0,4,5,0], [0,3,1,0] ] 进阶:
一个直接的解决方案是使用 O(mn) 的额外空间,但这并不是一个好的解决方案。 一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。 你能想出一个常数空间的解决方案吗?
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/set-matrix-zeroes 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public void setZeroes(int[][] matrix) {
int[] xNum = new int[matrix[0].length];
int[] yNum = new int[matrix.length];
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[i].length;j++){
if(matrix[i][j]==0) {
xNum[j] = 1;
yNum[i] = 1;
}
}
}
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[i].length;j++){
if(xNum[j]==1||yNum[i]==1){
matrix[i][j] = 0;
}
}
}
}
}
74. 搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。 每行的第一个整数大于前一行的最后一个整数。 示例 1:
输入: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 3 输出: true 示例 2:
输入: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 13 输出: false
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0)
return false;
int begin, mid, end;
begin = mid = 0;
int len1 = matrix.length, len2 = matrix[0].length;
end = len1 * len2 - 1;
while (begin < end) {
mid = (begin + end) / 2;
if (matrix[mid / len2][mid % len2] < target)
begin = mid + 1;
else
end = mid;
}
return matrix[begin / len2][begin % len2] == target;
}
}
75. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意: 不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0] 输出: [0,0,1,1,2,2] 进阶:
一个直观的解决方案是使用计数排序的两趟扫描算法。 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 你能想出一个仅使用常数空间的一趟扫描算法吗?
class Solution {
public void sortColors(int[] nums) {
//双指针
int low = 0, high = nums.length - 1;
int i = 0;
while(i <= high){
if(nums[i] == 0){
int tmp = nums[i];
nums[i] = nums[low];
nums[low] = tmp;
++low; ++i;
}else if(nums[i] == 1){
++i;
}else if(i <= high && nums[i] == 2){
int tmp = nums[i];
nums[i] = nums[high];
nums[high] = tmp;
--high;
}
}
}
}
76. 最小覆盖子串
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC" 输出: "BANC" 说明:
如果 S 中不存这样的子串,则返回空字符串 ""。 如果 S 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
//滑动窗口的应用
public String minWindow(String s, String t) {
if (s.length() < t.length()) {
return "";
}
//题目里没有说只是大写 在提交的时候踩了不少坑..
int[] auxT = new int[128]; //auxT用来存目标字符串
for (int i = 0; i < t.length(); i++) {
auxT[t.charAt(i)]++;
}
int[] auxS = new int[128];//auxS用来存滑动窗口里的内容
for (int i = 0; i < t.length(); i++) {
auxS[s.charAt(i)]++;
}
//start end左右指针 min记录滑动窗口最小长度 startIndex记录最小长度的起始位置
int start = 0, end = t.length(), min = Integer.MAX_VALUE, startIndex = 0;
while (end <= s.length()) {
while (check(auxS, auxT)) {//如果包含 就移动左指针缩小范围
if (end - start < min) {
min = end - start;
startIndex = start;
}
auxS[s.charAt(start++)]--;
}
if (end <= s.length() - 1) { //这里判断一下end的越界问题
auxS[s.charAt(end++)]++;
} else {
break;
}
}
return min != Integer.MAX_VALUE ? s.substring(startIndex, startIndex + min) : "";
}
private boolean check(int[] nums1, int[] nums2) {
for (int i = 0; i < nums1.length; i++) {
if (nums1[i] < nums2[i]) {
return false;
}
}
return true;
}
}
77. 组合
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
class Solution {
private List<List<Integer>> res;
public List<List<Integer>> combine(int n, int k) {
res = new ArrayList<List<Integer>>();
if (n<1 || k<1 || n<k) {
return res;
}
List<Integer> selected = new ArrayList<Integer>();
pickNext(1, n, k, selected);
return res;
}
public void pickNext(int min, int max, int k, List<Integer> selected) {
if (k==0) {
res.add(new ArrayList<Integer>(selected));
return;
}
for (int i=min; i<=max-k+1; i++) {
selected.add(i);
pickNext(i+1, max, k-1, selected);
selected.remove(selected.size()-1);
}
}
}
78. 子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
res.add(new ArrayList<>());
for (int i = 0; i < nums.length; i++) {
int all = res.size();
for (int j = 0; j < all; j++) {
List<Integer> tmp = new ArrayList<>(res.get(j));
tmp.add(nums[i]);
res.add(tmp);
}
}
return res;
}
}
79. 单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ]
给定 word = "ABCCED", 返回 true. 给定 word = "SEE", 返回 true. 给定 word = "ABCB", 返回 false.
class Solution {
public boolean exist(char[][] board, String word) {
for (int i = 0; i < board.length; i++){
for (int j = 0; j < board[0].length; j++) {
if (search(board, word, i, j, 0)) {
return true;
}
}
}
return false;
}
boolean search(char[][] board, String word, int i, int j, int k) {
if (k >= word.length()) return true;
if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(k)) return false;
board[i][j] += 256;
boolean result = search(board, word, i - 1, j, k + 1) || search(board, word, i + 1, j, k + 1)
|| search(board, word, i, j - 1, k + 1) || search(board, word, i, j + 1, k + 1);
board[i][j] -= 256;
return result;
}
}
80. 删除排序数组中的重复项 II
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。 示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。 说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
class Solution {
public int removeDuplicates(int[] nums) {
int i = 0;
for (int n : nums)
if (i < 2 || n > nums[i-2])
nums[i++] = n;
return i;
}
}