2019.1.24 leetcode 刷题总结
题号:680
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
示例 1:
输入: “aba”
输出: True
示例 2:
输入: “abca”
输出: True
解释: 你可以删除c字符。
我的想法:
- 双指针法:一个指向头,一个指向尾,指向的字符相等时同时向中间移动,若不相同,先将头指针向后移动一位,接着比较,若再次出现不相同的字符,回滚到第一次不相同的位置(头尾指针同时回滚),尾指针向前移动一位,继续比较,若再次出现不相同,返回false。
对应程序:
// java
class Solution {
public boolean validPalindrome(String s) {
// 头指针
int start = 0;
// 尾指针
int end = s.length() - 1;
// 记录第一次不相同时头指针的位置
int tempStartIndex = -1;
// 记录第一次不相同时尾指针的位置
int tempEndIndex = -1;
// 是否是第一次不相同
boolean isFirst = true;
// 是否回滚过
boolean isRollBack = false;
while(start < end) {
if(s.charAt(start) != s.charAt(end)) {
// 第一次出现不相同
if(isFirst) {
// 记录指针位置
tempStartIndex = start;
tempEndIndex = end;
// 头指针后移
start++;
// 标志置为否
isFirst = false;
// 再次出现不相同
}else {
// 判断是否回滚过
if(!isRollBack){
// 若没有回滚过,回滚
start = tempStartIndex;
end = tempEndIndex;
// 尾指针前移
end--;
// 回滚标志置为是
isRollBack = true;
}else {
return false;
}
}
}else {
// 指针向中间移动
start++;
end--;
}
}
return true;
}
}
题号:921
给定一个由 ‘(’ 和 ‘)’ 括号组成的字符串 S,我们需要添加最少的括号( ‘(’ 或是 ‘)’,可以在任何位置),以使得到的括号字符串有效。
从形式上讲,只有满足下面几点之一,括号字符串才是有效的:
它是一个空字符串,或者
它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者
它可以被写作 (A),其中 A 是有效字符串。
给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。
示例 1:
输入:"())"
输出:1
示例 2:
输入:"((("
输出:3
示例 3:
输入:"()"
输出:0
示例 4:
输入:"()))(("
输出:4
我的想法:
- 将字符串拆分成字符数组,利用栈结构
- 当遇到左括号时,左括号入栈,当遇到右括号时,判断栈是否为空,如果为空,返回的结果加一(这里记录了缺少的左括号的数目);若不为空,出栈
- 遍历结束后,返回的结果要加上此时栈中元素的个数(记录的是缺少的右括号的个数)
对应程序:
// java
class Solution {
public int minAddToMakeValid(String S) {
LinkedList<Character> stack = new LinkedList<>();
int res = 0;
for(char sChar : S.toCharArray()) {
if(sChar == '(') {
stack.addFirst(sChar);
}else {
if(stack.isEmpty()) {
res++;
}else {
stack.removeFirst();
}
}
}
res += stack.size();
return res;
}
}
题号:287
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
我的想法:
- 遍历数组,用容器存储数组中的每个值,在存入容器时做检查,若容器中已经存在该值,说明该值重复,返回该值
- 不推荐使用ArrayList.contains()方法,因为底层在做遍历List
- 可以使用也推荐使用Set,注意Set.add()方法是存在返回值的,类型是boolean,当返回值为true时说明插入成功,即集合中不存在该元素;若返回值为false,说明集合中已经存在该元素
- 还有一种方法是快慢指针法,推荐阅读:java数据结构面试问题—快慢指针问题
题号:665
给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。
示例 1:
输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
示例 2:
输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
我的想法:
- 将第一个元素设为标志数字,遍历数组,从第二个元素开始,比较数组中的每个值和标志数字的大小,若标志数字小于或等于当前数组元素,满足条件,将当前数字设为标志数字,继续遍历;若标志数字大于当前数组元素,说明出现了不符合的情况,用计数器记录出现次数,若出现的次数已经为2,返回false,因为改一个值是一定不会让数组非递减的,也就是说,我们对第一次出现的不符合的情况是宽容的:当去掉不符合当前非递减趋势的值后,若数组还是非递减的,我们认为当前出现的情况是允许的,即可以通过修改一个值使得数组满足非递减;
- 出现问题的数字可能是我们的标志数字([-1,4,2,3]),也可能是我们的当前数组的值([2,3,3,2,4]),结合这两种情况我们发现,只要在标志数字和当前数组数字之间有一个满足当前的非递减趋势,那这种情况是可以接受的;
- 上面说的情况对数组下标要限制的,index > 1 && index < nums.length - 1的,也就是在数组的中间;如果发生在数组的两端,无论如何都可以通过修改一个值来满足非递减的趋势,即将不符合的值修改为和他相邻的数字的值,因为在两端的数字只有一边限制
对应程序:
// java
class Solution {
public boolean checkPossibility(int[] nums) {
// 参数检查
if (nums.length < 2) {
return true;
}
// 标志数字
int flagNum = nums[0];
// 计数器,记录出现递减情况的次数
int counter = 0;
// 遍历数组
for(int i = 1; i < nums.length; ++i) {
// 出现递减情况
if(flagNum > nums[i]) {
// 计数器的值加1
counter++;
// 若第二次出现,返回false
if(counter == 2) {
return false;
}
/*若递减的情况出现在数组的两端,
*只需将flagNum改为和它相邻的数字的值即可满足非递减的趋势;
*
*若出现在中间,flagNum和nums[i]之中有一个数字的值满足当前的非递减趋势,
*另一个值改为和它相同即可;
*
*当前非递减趋势的意思是:这个趋势的最小值是flagNum左边的值,
*这个趋势的最大值是nums[i]右边的值
*/
if(i > 1 && i < nums.length - 1) {
int left = nums[i - 2];
int right= nums[i + 1];
// 若不满足趋势,返回false
if((flagNum < left || flagNum > right) &&
(nums[i] < left || nums[i] > right)) {
return false;
}
}
}
// 标志右移
flagNum = nums[i];
}
return true;
}
}