Java_攒青豆|「青训营 X 码上掘金」主题创作活动

37 阅读2分钟

当青训营遇上码上掘金。
参加这个活动时,查看活动要求一眼便相中了这道编程题:“在空位中填满青豆不溺出”,我们可以记录每相邻两个柱体之间的青豆数,但是会忽略了非相邻柱体多出来的未统计的部分,没有规律可循。但是统计每一层可存放青豆的个数呢?这样的话就剔除了以上困扰(类似于画出最大的矩形填补空缺)。但实际上它和俄罗斯方块的原理类似。
相信大家都玩过俄罗斯方块,原理就是将每一层填满后消除该层。

举例&原理:

举例中考虑图片长度不够和右侧数组数值不同(每行数据相较于前一行数据减1,负值就是这个位置可以存放的青豆数)

青豆.jpg

好了,现在我们明数字实现原理,可以开始思考编写代码了:

  • 将数组存储到集合中里后,每一轮对每一个数值自减减。
  • 定义左右两个变量,左边的变量恒为0,右边的变量随着集合的长度而发生改变
  • 找到左右两边等于0的数作为“入口”,并暴露出来,以入口为起始点分别向左右边寻找大于零的数作为“出口”。
  • 剔除掉存储数组中0和绿框里的值,并将值分别保存在另外两个集合中
  • 最后只剩下一个元素(最高柱体自减后剩下的高度0或其他值)作为结束整体循环的条件
  • 分别遍历存储数据的两个集合,进行加和,输出结果

核心代码:

储存以零开头的集合中小于0的数值,后将arrayList中删除相应元素。
1)当移除右边的元素时,集合长度减小,要判断前面一个数据是否大于0,就要将“右指针”先自减减
2)当移除左边的元素时,集合长度同样减小,这是判断后面的数据是否大于0时,不需要改变“左指针”,因为集合长度减小后每一个第“0”号元素都是未删除集合前的第“1”号元素

if (arrayList.get(right) == 0) {
    do {
        listB.add(arrayList.get(right));
        arrayList.remove(right);    //移除末尾的元素right-1
    } while (arrayList.get(--right) < 0);
    System.out.println("listR:" + listB);
}
if (arrayList.get(left) == 0) {
    do {
        listA.add(arrayList.get(left));
        arrayList.remove(left); //相当于left--
        temp++;
    } while (arrayList.get(left) < 0);
    System.out.println("listL:" + listA);
}

注意事项:

  1. 当删除左边的元素时,集合整体向左移动,右指针还要对应着集合的最后一个元素,这时“右指针”的位置就要减去移动的位数(在处理左边的数据时,定义一个变量记录删除左边数据集合移动的位数)
  2. 对集合中的元素自减减时,注意for循环终止条件为变化的集合长度,不能定义为数组的长度;
    即:
    for (int i = 0; i < arrayList.size(); i++) {
        arrayList.set(i, arrayList.get(i) - 1);
    }

由于使用的是:

 public E set(int index,E element)//用指定的元素替换此列表中指定位置的元素。

所以要定义初始的集合数据,即:

int arr[] = {4, 0, 4, 2, 0, 3, 0, 4};
ArrayList<Integer> arrayList = new ArrayList<>();
for (int i : arr) {
    arrayList.add(i);
}

很遗憾没有处理开头和结尾数值为0的情况

完整代码: