leetcode刷题记录(680、921、287、665)

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

我的想法:

  1. 将字符串拆分成字符数组,利用栈结构
  2. 当遇到左括号时,左括号入栈,当遇到右括号时,判断栈是否为空,如果为空,返回的结果加一(这里记录了缺少的左括号的数目);若不为空,出栈
  3. 遍历结束后,返回的结果要加上此时栈中元素的个数(记录的是缺少的右括号的个数)

对应程序:

// 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) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。

我的想法:

  1. 遍历数组,用容器存储数组中的每个值,在存入容器时做检查,若容器中已经存在该值,说明该值重复,返回该值
  2. 不推荐使用ArrayList.contains()方法,因为底层在做遍历List
  3. 可以使用也推荐使用Set,注意Set.add()方法是存在返回值的,类型是boolean,当返回值为true时说明插入成功,即集合中不存在该元素;若返回值为false,说明集合中已经存在该元素
  4. 还有一种方法是快慢指针法,推荐阅读: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
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

我的想法:

  1. 将第一个元素设为标志数字,遍历数组,从第二个元素开始,比较数组中的每个值和标志数字的大小,若标志数字小于或等于当前数组元素,满足条件,将当前数字设为标志数字,继续遍历;若标志数字大于当前数组元素,说明出现了不符合的情况,用计数器记录出现次数,若出现的次数已经为2,返回false,因为改一个值是一定不会让数组非递减的,也就是说,我们对第一次出现的不符合的情况是宽容的:当去掉不符合当前非递减趋势的值后,若数组还是非递减的,我们认为当前出现的情况是允许的,即可以通过修改一个值使得数组满足非递减;
  2. 出现问题的数字可能是我们的标志数字([-1,4,2,3]),也可能是我们的当前数组的值([2,3,3,2,4]),结合这两种情况我们发现,只要在标志数字和当前数组数字之间有一个满足当前的非递减趋势,那这种情况是可以接受的;
  3. 上面说的情况对数组下标要限制的,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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值