interview arithmetic
内容管理
数组算法续
滑动窗口的题目总结一下
滑动窗口的题目类似记忆化,就是不回溯,当右指针滑出边界的时候,就移动左指针至
https://leetcode-cn.com/problems/fruit-into-baskets/
ArrayList kindList = new ArrayList(); 这里达不到目的的,没有remove方法,使用Counter类,不是java类库中的,如果普通项目使用,要导入com.codahale.metrics
Set kindList = new HashSet<>();
所以最佳的集合还是使用Map最好,Map的键用来存储水果的种类,值用来存储水果的数量;因为每次遇到相同的键就要进行刷新,所以还是使用Map
但是这里的Map应该进行处理,使用内部类自定义一个HashMap,重写get方法,当键不存在的时候,返回0,而不是null; 并且加入add方法,add的时候就自动将现在的值进行加减操作
class Solution {
public int totalFruit(int[] fruits) {
int start = 0, end = 0;
int max = 0;
Counter kindList = new Counter();
while(end < fruits.length) {
//加入,键是Set,会去重,但是值记录了个数
kindList.add(fruits[end],1);
while(kindList.size() > 2) { //可以简单将滑动窗口当作一个弹性窗口,一直满足条件,先右边动,左边固定的时候左边动
//移除start位置的元素
kindList.add(fruits[start],-1);//减少一个数量
if(kindList.get(fruits[start]) == 0){
kindList.remove(fruits[start]);
}
start ++;
}
//每次满足的时候记录
max = Math.max(max,end + 1 - start);
end ++;
}
return max;
}
class Counter extends HashMap<Integer,Integer> {
//重写方法,就是获取元素的时候,如果没有,那么就是0,不是null
public int get(int k) {
return this.containsKey(k) ? super.get(k) : 0;
}
//add的时候是直接为value算术运算
public void add(int k, int v) {
this.put(k,this.get(k) + v); //每次插入的时候,因为是Set,就会自动更新值
}
}
}
这里的思路和之前的那道滑动窗口一样,都是弹性的窗口,end先跑,然后当不满足条件的时候,就移动start直到满足条件
这里可以不使用Map,因为创建对象、实例化Map需要时间
class Solution {
public int totalFruit(int[] fruits) {
int start = 0, end = 0;
int max = 0;
//这里水果种类是有限的,所以还是使用空间换时间
int count = 0;//水果种类
int[] kindList = new int[fruits.length];
//进行遍历
while(end < fruits.length) {
kindList[fruits[end]] ++; //数量增加 ,java会自动进行初始化
if(kindList[fruits[end]] == 1) {
//说明是新的,只有等于1的才是刚加上的
count ++;
}
while(count > 2) {
//移除start
kindList[fruits[start]] --;
if(kindList[fruits[start]] == 0) count --;
start ++;
}
max = Math.max(max,end + 1 -start);
end ++;
}
return max;
}
}
这里就是使用了数组来代替上面的HashMap来存储数据,这样子虽然空间复杂度为O(N),但是执行时间下降,不需要自定义一个Map,时间从72ms到7ms
这里再介绍一个类似的滑动窗口的题目
76. 最小覆盖子串 - 力扣(LeetCode) (leetcode-cn.com)
这里的题目还是比上面难一点,但是思路都是使用数组的pos代表种类,value代表数量
class Solution {
public String minWindow(String s, String t) {
//这个题和上面的菜篮问题相同,我们可以直接选择一个数组来作为存储种类的数据,数组pos代表种类,值代表个数
//char是8位的,那么字符就可以转换位整型,就是2^7 = 128
char[] chars = s.toCharArray(), chart = t.toCharArray();
//典型的滑动窗口,最开始先将所有的t中的字符串给挖坑,这样子方便比较呢,当满足条件的时候,如果第一个字符就是目标字符,那么久等到下一次找到字符的时候再移动,要保证一直满足条件
int[] kindList = new int[128];
int start = 0, end = 0, cnt = 0;
String res = "";
//挖坑
for(char c : chart) kindList[c]--;
//开始找滑动窗口
while(end < chars.length) {
kindList[chars[end]] ++;
if(kindList[chars[end]] <= 0) {
cnt ++;
}
//满足条件了,并且要找到新的满足条件的序列【替代品】的时候才移动start,不然乱了
while(cnt == chart.length && kindList[chars[start]] > 0) {
kindList[chars[start]]--;
start ++;
}
//判断
if(cnt == chart.length) {
if(res.equals("") || res.length() > end + 1 - start) {
res = s.substring(start,end + 1);
}
}
end ++;
}
return res;
}
}
28. 实现 strStr() - 力扣(LeetCode) (leetcode-cn.com)
这个题目再来说一下,那天过于忙碌,实际上就是很简单,首先可以使用java自带的方法indexOf,就可以了
class Solution {
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
}
一行代码就解决了。。。。。,但是自己手撕的话,除了克鲁德之外,最佳的算法就是KMP了
class Solution {
public int strStr(String haystack, String needle) {
int n = haystack.length(), m = needle.length();
if (m == 0) {
return 0;
}
int[] pi = new int[m];
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && needle.charAt(i) != needle.charAt(j)) {
j = pi[j - 1];
}
if (needle.charAt(i) == needle.charAt(j)) {
j++;
}
pi[i] = j;
}
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && haystack.charAt(i) != needle.charAt(j)) {
j = pi[j - 1];
}
if (haystack.charAt(i) == needle.charAt(j)) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}
}
这样就优化了很多,这篇博客将作为数组专题的总结,随时更新🎉