双指针核心思想
将双重for循环优化成O(n)
双指针模板
int left = 0, right = n-1;
while(l < r){//处理代码//left处理//right处理
}
滑动窗口模板
滑动窗口本质上也是双指针,不过i的处理时每次很多情况下都是加1,同时对窗口记录进行简单处理
for(int i = 0, j = i; i < n; i++){while(j < n && check(i,j)){//处理代码j++;}//核心代码//i, j处理代码 注意i在for循环中会i++
}
双指针例题
11. 盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
//暴力思路:双重遍历数组,确定左边,再遍历右边,找到最大值
//双指针:交替寻找左右指针的当前最大值,从而确定最大值
class Solution {public int maxArea(int[] height) {int left = 0;int right = height.length - 1;int maxArea = 0;while(left < right){int cur = Math.min(height[left], height[right]) * (right - left);maxArea = Math.max(cur, maxArea);//System.out.println(height[left] + " " +height[right]);if(height[right] > height[left]){left++;}else{right--;}} return maxArea;}
}
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。
返回所有和为 0 且不重复的三元组。
//双指针:先确定一个,再用双指针确定其他两个,注意处理重复值
//备忘:
//new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r])) 多个数转ArrayList
//ArrayList<List<Integer>> re = new ArrayList<>(set); set转ArrayList
class Solution {public List<List<Integer>> threeSum(int[] nums) {HashSet<List<Integer>> set = new HashSet<>();Arrays.sort(nums);int n = nums.length;for (int i = 0; i < n; i++) {while(i != 0 && i < n && nums[i] == nums[i-1]){i++;}int l = i + 1, r = n - 1;while(l < r){if(nums[i] + nums[l] + nums[r] == 0){set.add(new ArrayList<>(Arrays.asList(nums[i], nums[l], nums[r])));l++;r--;while(l < r && nums[l] == nums[l-1]) l++;while(l < r && nums[r] == nums[r+1]) r--;}else if(nums[i] + nums[l] + nums[r] < 0){l++;while(l < r && nums[l] == nums[l-1]) l++;}else{r--;while(l < r && nums[r] == nums[r+1]) r--;}}}ArrayList<List<Integer>> re = new ArrayList<>(set);return re;}
}
42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
//思路:leftMax:左边当前最高的柱子 rightMax:右边当前最高的柱子 height[i]表示当前柱子高度
// 如果 leftMax < rightMax,意味着当前这个位置的一定可以以leftMax作为左边边界,装下leftMax-height[left]容量水
// 如果 leftMax >= rightMax,意味着当前这个位置的一定可以以rightMax作为右边边界,装下rightMax-height[right]容量水
//为什么哪个小就可以确定下来呢?因为一个水桶的容积取决于最短那块板
class Solution {public int trap(int[] height) {int sum = 0;int n = height.length;int left = 0, right = n -1;int leftMax = height[left], rightMax = height[right];//通过比较左右最大值,确定当前的容器的可用高度,之后累加得到结果while(left < right){ //当左边最大值 小于 右边最大值,则left位置的容器的可用高度可以确定为leftMax;if(leftMax < rightMax){sum += (leftMax - height[left]);left++;}else{sum += (rightMax - height[right]);right--;}leftMax = Math.max(leftMax, height[left]);rightMax = Math.max(rightMax, height[right]);}return sum;}
}
滑动窗口例题
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度
//思路:确定i,j移动条件是,[i,j]区域内没有重复字符。出现重复字符,则i++
//这里面的窗口记录可以看作set
class Solution {public int lengthOfLongestSubstring(String s) {int max = 0;int n = s.length();char[] c = s.toCharArray();HashSet<Character> set = new HashSet<>();for(int i = 0, j = i; i < n; i++){while(j < n && !set.contains(c[j])){set.add(c[j]);j++;}max = Math.max(max, set.size());set.remove(c[i]);}return max;}
}
438. 找到字符串中所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)
//思路:确定i,j的移动条件是[i,j]中的字符与p中相同,且字母个数小于p中字母个数。否则移动i
//移动i的技巧,如果j的字母不存在于p中,i = j, j++;如果存在于p中,且i++,因为[i,j]还存在答案的可能
//其他技巧:字符串中仅有小写或大写,可以用int[]模拟map
//这里面的窗口记录可以看作map
class Solution {public List<Integer> findAnagrams(String s, String p) {List<Integer> ans = new ArrayList<>();int[] map = new int[26];int slen = s.length(), plen = p.length();char[] sc = s.toCharArray();for(char c : p.toCharArray()){map[c - 'a']++;}int[] tmap = Arrays.copyOf(map, 26);for(int i = 0, j = i; i < slen && j < slen; i++){//System.out.println(tmap[0] + " " + tmap[1] + " " + tmap[2]);while(j < slen && tmap[sc[j] - 'a'] > 0){tmap[sc[j] - 'a']--;j++;}// System.out.println(sc[i] + " " + sc[j]);if((j - i) == plen){ans.add(i);}if(j == slen) break;if(map[sc[j] - 'a'] <= 0) {i = j;j++;tmap = Arrays.copyOf(map, 26);}else{tmap[sc[i] - 'a']++;}}return ans;}
}