题目
我的思路
题目的大致含义是:求数组中某一子序列中的数字个数,这个子序列中只能有两种数字
我的思路:
用LinkedList存储,LinkedList中最多只有两个元素,也就是正在遍历的子序列中的最多那两个元素
遍历fruit数组
如果当前元素不包含在list中,且,list的size()小于2,那么就还能往list中添加元素,并计算该元素在子序列中出现的次数
如果当前元素fruit[i]不包含在list中,且,当前list的size()等于2,那么就需要把先前计算的元素个数与maxValue进行比较,保存较大的值
cnt置为1,表示计入fruit[i]位置的数
然后fruit[i-1]肯定也是下一个子序列中需要计数的一种元素(在fruit[i-1]前面,可能有多个位置,值和fruit[i-1]相等)。
定义一个变量j,j从i-2开始,一致往前找,如果fruit[i-1]与fruit[j]相等,则说明要计算的子序列还可以继续向前扩展,直到退出while循环。
退出while循环的有两种可能:
-
一种是:j<0,那么,说明fruit[i]之前的元素都是同一种元素,这种情况应该是不存在的,因为进入这个while循环的前提条件就是list的size等于2。说明前面至少已经有两种元素了。
-
那么只有可能是另一种,也就是遍历到了fruit[j]和fruit[i-1]不一样的位置,在遍历的过程中不断计数,fruit[i-1]作为新的子序列中的一个元素,要计算它出现的次数。
最后,在list中删除fruit[j]这个元素,需要注意的是,list.remove它有重载方法
- public boolean remove(Object o)
- public E remove(int index)
你只是把fruit[j]这个数字传进去,删除的是索引位置为fruit[j]的元素,而不是值为fruit[j]的元素,所以,我们要主动装箱,让它调用参数是Object类型的那个remove方法
import java.util.LinkedList;
class Solution {
public int totalFruit(int[] fruits) {
LinkedList<Integer> list = new LinkedList<>();
int i = 0;
int cnt = 0;
int maxValue = -1;
while (i < fruits.length) {
if (!list.contains(fruits[i])) {
if (list.size() == 2) {
maxValue = Math.max(maxValue, cnt);
cnt = 1;
int j = i - 2;
while (j >= 0 && fruits[j] == fruits[i - 1]) {
j--;
cnt++;
}
//if (j >= 0) {
// list.remove(new Integer(fruits[j]));
//}
list.remove(new Integer(fruits[j]));
}
list.addLast(fruits[i]);
}
cnt++;
i++;
}
return Math.max(maxValue, cnt);
}
}
官方思路 滑动窗口
我们可以使用滑动窗口解决本题,left和right分别表示满足要求的窗口的左右边界,同时使用这个窗口内的数以及出现的次数
我们每次将right移动一个位置,并将fruit[right]加入哈希表。如果此时哈希表不满足要求(即哈希表中出现超过两个键值对),那么,需要不断移动left,对应的fruit[left]的值要减一,如果键为fruit[left]所对应的值为0,那么在hashMap中移除fruit[left]的值,直到哈希表满足要求位置(哈希表中的键值对不超过两个)。
import java.util.HashMap;
class Solution {
public int totalFruit(int[] fruits) {
HashMap<Integer, Integer> map = new HashMap<>();
int left = 0, right = 0;
int ans = -1;
while (right < fruits.length) {
map.put(fruits[right], map.getOrDefault(fruits[right], 0) + 1);
while (map.size() > 2) {
map.put(fruits[left], map.get(fruits[left]) - 1);
if (map.get(fruits[left]) == 0) {
map.remove(fruits[left]);
}
left++;
}
ans = Math.max(ans, right - left + 1);
right++;
}
return ans;
}
}
其他解法
根据题目描述,我们只可以拿两种类型水果,并且获取的顺序是按照数组fruits的顺序从左到右的,那么我们可以选择采用滑动窗口的方式对窗口内的两种类型的水果数量进行计算,并且针对每个窗口中的水果数量进行计算,最终选取最大值返回即可
startIndex:表示窗口的起点
diffIndex:每当发现相邻两个水果不同,则将其指向发生不同的那个水果,当遍历发现有第三种水果的时候,用于将其作为新窗口的起点。
从头开始遍历,每当发现遍历到的这个水果种类fruits[i]与curFruit不同时,就说明我们拿到了新品种,那么pickNums加一,当超过2种的时候,我们就通过i-startIndex对窗口内的水果数量进行计算,然后移动窗口,将新窗口的起点设定为diffIndex指向的位置,然后继续遍历水果,以此类推。
class Solution {
public int totalFruit(int[] fruits) {
int startIndex = 0, diffIndex = 0, pickNums = 0, curFruit = 0, ans = 0;
boolean[] pickRecords = new boolean[fruits.length];
for (int i = 0; i < fruits.length; i++) {
if (!pickRecords[fruits[i]]) {
if (pickNums == 2) {
ans = Math.max(ans, i - startIndex);
pickRecords[fruits[diffIndex - 1]] = false;
startIndex = diffIndex;
pickNums--;
}
pickNums++;
pickRecords[fruits[i]] = true;
}
if (curFruit != fruits[i]) {
curFruit = fruits[i];
diffIndex = i;
}
}
return Math.max(ans, fruits.length - startIndex);
}
}