1、18. 四数之和
难点还是在于去重。
借鉴之前做的三数之和的思路,三数之和是一层循环,两个指针。
那么这个题就可以两层循环,两个指针。
这题和三数之和有什么区别呢?三数之和是判断和是否为0,这个题是判断和是否为target,那么就不能简单地认为nums[i]>0就break了。因为可能是例如-4 -1 0 0,target=-5这种情况。所以剪枝的时候必须要求target>0,nums[i]>target才行。
剩下的就没啥区别了。
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
ArrayList list = new ArrayList<>();
int m = 0, n = 0;
Arrays.sort(nums);
for(int i = 0; i < nums.length-2; i++){
//第一层剪枝
if(nums[i] > target && target > 0) break;
//第一层去重
if(i > 0 && nums[i] == nums[i-1]) continue;
for(int j = i+1; j < nums.length-1; j++){
//第二层剪枝
if(nums[i]+nums[j] > target && target > 0) break;
//第二层去重
if(j > i+1 && nums[j] == nums[j-1]) continue;
m = j+1;
n = nums.length-1;
while(m < n){
if((long)nums[i]+nums[j]+nums[m]+nums[n] > target){
n--;
}
else if((long)nums[i]+nums[j]+nums[m]+nums[n] < target){
m++;
}
else{
list.add(List.of(nums[i], nums[j], nums[m], nums[n]));
while(m<n && nums[m] == nums[m+1]) m++;
while(n>m && nums[n] == nums[n-1]) n--;
m++;
n--;
}
}
}
}
return list;
}
}
2、344. 反转字符串
水题。
经典做法:只需要遍历字符串长度的一半,然后计算一下当前字符对应后半部分是哪个字符,交换二者即可,从图中看出,和奇偶无关。
class Solution {
public void reverseString(char[] s) {
for(int i = 0; i < (s.length>>>1); i++){
int j = s.length-1-i;
char c = s[i];
s[i] = s[j];
s[j] = c;
}
}
}
3、541. 反转字符串 II
分两步处理,先想想怎么获取需要反转的字符串,再想想反转字符串需要怎么做。
怎么获取需要反转的字符串:
i代表需要反转的字符串的第一个位置,每次加2k。那么需要反转的位置就是从i到i+k,也就是[i, i+k),那么如果不足呢,如果长度不足加k,那么就反转从i到总长度的位置就行,要是不足2k不用处理,因为会默认处理i到i+k,剩下的部分不会进入循环。
再想想反转字符串需要怎么做:
跟刚才反转字符串的题一样,我先把它抽取成一个方法,需要反转的开始和结束位置。
i需要遍历到end+begin除以2,如图,这就是它的中间值。
然后j是end-(i-begin)-1,i-begin的取值会是0,1,···,所以j会不断缩小。
class Solution {
public String reverseStr(String s, int k) {
char[] chars = s.toCharArray();
for(int i = 0; i < chars.length; i+=2*k){
if(i+k > chars.length){
reverseString(chars, i, chars.length);
break;
}
reverseString(chars, i, i+k);
}
return new String(chars);
}
public void reverseString(char[] s, int begin, int end) {
for(int i = begin; i < ((end+begin)>>>1); i++){
int j = end-(i-begin)-1;
char c = s[i];
s[i] = s[j];
s[j] = c;
}
}
}