面试经典150题0427
Leetcode011 盛最多水的容器
**双指针:**左指针指向左边界,右指针指向右边界;计算当前情况下的容积。然后比较左指针高度height[left]
和右指针高度height[right]
,哪一边的小就移动对应的指针。
public static int maxArea(int[] height){
int left = 0, right = height.length - 1;
int area = 0;
int areaMax = 0;
while (left < right){
area = Math.min(height[left], height[right]) * (right - left);
areaMax = Math.max(area, areaMax);
if(height[left] < height[right]){
left++;
}
else {
right--;
}
}
return areaMax;
}
Leetcode015 三数之和
双指针:
- 先将数组排序
- 然后固定一个元素位置
i
,该元素从[0, nums.length - 2)
遍历,为了避免重复答案,需要判断当前元素是否和前一个元素相同,相同则继续移动; - 否则,则指定左指针为
i+1
,右指针为数组右边界。 - 两个元素相加和
-nums[i]
进行比较,大于则移动右指针,小于则移动左指针。 - 当找到答案时,将答案添加到链表中,左右指针同时移动。
- 同时为了避免答案重复,需要比较
nums[left]==nums[left-1]
,相等则继续移动左指针;否则继续重复第4步。
public static List<List<Integer>> threeSum(int[] nums){
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0; i < nums.length - 2; i++){
while (i > 0 && i < nums.length - 2 && nums[i] == nums[i-1]){
i++;
}
int left = i + 1;
int right = nums.length - 1;
int target = -nums[i];
while (left < right){
if(nums[left] + nums[right] == target){
List<Integer> tmp = new ArrayList<>();
tmp.add(nums[i]);
tmp.add(nums[left]);
tmp.add(nums[right]);
res.add(tmp);
left++;
right--;
while (left < right && nums[left] == nums[left - 1]){
left++;
}
}
else if(nums[left] + nums[right] < target){
left++;
}
else {
right--;
}
}
}
return res;
}
Leetcode209 长度最小的子数组
滑动窗口:
- 使用两个指针维护一个滑动窗口,设置一个变量
sum
记录滑动窗口内的和 - 当
sum>=target
时,尝试更新窗口最小值minLen
,并移动左指针。 - 当
sum<target
时,移动右指针。
public static int minSubArrayLen(int target, int[] nums){
int left = 0, right = 0;
int sum = 0;
int minLen = Integer.MAX_VALUE;
while (right < nums.length){
sum += nums[right++];
while (sum >= target){
minLen = Math.min(minLen, right - left);
sum -= nums[left++];
}
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
Leetcode003 无重复字符的最长字串
- 双指针维护一个动态窗口,窗口中的内容为无重复的字串。
- 同时使用一个数组记录窗口中每个字符出现次数。
- 右指针移动增大窗口时,如果该字符在窗口内出现,则移动左指针,同时更改数组对应字符记录;如果字符没有在窗口内出现,则更新数组内对应字符记录。
- 数组可以设置为大小128,用来统计ASCII中字符。由题意可知
s
由英文字母、数字、符号和空格组成。
public static int lengthOfLongestSubstring(String s){
int[] array = new int[128];
int left = 0, right = 0;
int maxLen = 0;
while (right < s.length()){
while (right < s.length() && array[s.charAt(right)] == 0){
array[s.charAt(right++)]++;
maxLen = Math.max(maxLen, right - left);
}
array[s.charAt(left++)]--;
}
return maxLen;
}
Leetcode030 串联所有单词的子串
判断每个子串是否符合条件,子串的长度为words.length * words[0].length()
,符合的话就把下标保存起来。
如何判断子串包含所有单词?
-
首先,将所有的单词存放到HashMap中,
key
存储单词,value
存放单词出现的个数(因为给出的单词可能重复,其value
可能为1或者2等)。 -
然后扫描子串中的单词,如果当前扫描的单词在之前的HashMap中,就把该单词存放到新的HashMap中,并判断新的HashMap中该单词的
value
是否大于之前单词的value
-
如果大于,说明该子串不是我们要找的,接着判断下一个子串即可。
-
如果不大于,接着判断下一个单词的情况。
-
-
如果当前扫描单词不在之前的HashMap中,则直接判断下一个子串。
-
当子串扫描结束时,如果子串的全部单词都符合,那么该子串就是要找的其中一个。
public static List<Integer> findSubstring(String s, String[] words) {
Map<String, Integer> map = new HashMap<>();
List<Integer> res = new ArrayList<>();
for(String tmp : words){
map.put(tmp, map.getOrDefault(tmp,0)+1);
}
int wordLen = words[0].length();
int rightBound = s.length() - words.length * wordLen + 1;
for(int i = 0; i < rightBound; i++){
int start = i;
int end = wordLen + i;
int cnt = 0;
Map<String, Integer> tmpMap = new HashMap<>();
while (cnt < words.length){
String sub = s.substring(start, end);
if(map.containsKey(sub)){
// sub为words中的单词,判断已经出现的次数是否超过words中相同单词的次数
int subValue = tmpMap.getOrDefault(sub, 0) + 1;
if(subValue > map.get(sub)){
break;
}
tmpMap.put(sub, subValue);
}
else {
break;
}
start += wordLen;
end += wordLen;
cnt++;
}
if(cnt == words.length){
// 子串满足条件,由words中的单词组成
res.add(i);
}
}
return res;
}
Leetcode076 最小覆盖子串
滑动窗口
- 初始化
ansLeft = -1
和ansRight = m
,分别记录最短子串的左右端点,其中m
为字符串s
的长度。 - 使用一个数组
arrayT
统计``t中每个字符出现的次数,由于包含大小写,可以定义数组的大小为128. - 初始化
left
为0以及一个数组arrayS
,用来统计s
子串中每个字母的出现次数。 - 初始化
less
为字符串t
中不同字母的个数 - 遍历
s
,设当前枚举的子串右端点为right(右指针)
,把c = s[right]
出现的次数加一。如果arrayS[c] == arrayT[c]
,说明字符c
的出现次数在窗口内已经满足,这是将less--
- 如果
less=0
,说明arrayS
中的每个字母的出现次数都大于等于arrayT
中字母的出现次数。- 如果
right - left < ansRight - ansLeft
,则更新ansLeft = left, ansRight = right
. - 把字母
x = s[left]
的出现次数减一。减一前,如果arrayS[x] = arrayT[x]
,说明左指针left
移动后x
的出现次数不满足要求,需要把less++
;arrayS[x] > arrayT[X]
,把left
再右移。 - 重复步骤,直到
less > 0
,然后移动右指针right
增大窗口大小。
- 如果
- 最后,如果
ansLeft < 0
,说明没有找到符合要求的子串,返回空字符串;否则返回下标ansLeft
到下标ansRight
之间的子串。
public static String minWindow(String s, String t){
int[] arrayS = new int[128];
int[] arrayT = new int[128];
int ansLeft = -1, ansRight = s.length();
int less = 0;
for (int i = 0; i < t.length(); i++) {
if(arrayT[t.charAt(i)]++ == 0){
less++;
}
}
int left = 0, right = 0;
while (right < s.length()){
char c = s.charAt(right++);
// 字符c在窗口中出现的次数等于在字符串t中出现的次数,less--
if(arrayT[c] == ++arrayS[c]){
less--;
}
while (less == 0){
if(right - left < ansRight - ansLeft){
ansLeft = left;
ansRight = right;
}
char x = s.charAt(left++);
if(arrayS[x]-- == arrayT[x]){
// 字符x在窗口中出现的次数已经小于在字符串t中的次数,less++
less++;
}
}
}
return ansLeft == -1 ? "" : s.substring(ansLeft, ansRight);
}