题目描述:
小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)
输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n),代表每一栋楼的高度。
样例输出:
6
5 3 8 3 2 5
样例输出:
3 3 5 4 4 4
图解题目:
如图即是样例给出的五栋楼(是不是清晰多了)
题目的意思是什么呢?
当你站在下标为0的楼层时,你可以看到几栋楼呢?
首先你可以看到脚下的自己(废话),然后向右看(左边没有)肯定能看到1号楼,也能看到2号楼,但是3,4,5号楼就看不见了,因为都被2号楼挡住了
值得注意的是
当你站在最高层2号楼的时候,也不是所有楼层都能看见哦;4号楼你就看不见 ,因为被三号楼挡住了(此处不是很符合现实)
解题思路:
以中间3号楼为例,如果从最左0号楼到自己 一直是递减的那么 从0号楼到自己这一路上的楼层都能看见,但是一旦有一个比前面楼层高的 那么肯定会遮挡住,如2号楼就会遮挡住0,1号楼,到最后只能看见2号楼;
同理如果三号楼最右的5号楼到自己 一直是递减的话,那么一路的楼自己都能看到
有了这个思路,我们就不难理解使用栈这个想法了。我们利用两个栈,来分别找到某栋楼左面能看到的楼层,和右面能看到的楼层;然后用一个数组分别加上就好了;
如:先把0加入栈,此时栈中1个元素,那么1号楼向左看就只能看到一栋楼(不包括自己的情况下);
然后看1号楼,1号楼的高度小于栈顶,所以加入栈,栈中此时是53,所以2号楼向左看肯定能看到两栋楼;
然后碰到了2号楼,2号楼的高度大于栈顶元素3(3号楼肯定看不见1号楼了),所以我们删除栈顶元素3,然后栈顶元素5还是小于2号楼高度8,所以再次删除栈顶元素,然后栈中加入8,此时栈中只有8,3号楼向左只能看见一栋楼;
向右看也是同理,接下来让我们看看代码(有注释);
public int[] findBuild(int[] heights) {//heights是楼的高度数组(例:5,3,8,3,2,5)
Stack<Integer> leftBuild = new Stack();//描述的是第i栋楼,向左能看到的楼的数量
Stack<Integer> rightBuild = new Stack();//描述的是第i栋楼,向右能看到的楼的数量
int n = heights.length;//一共有多少栋楼
int[] res = new int[n];//结果,第i栋楼能看到的楼的总数量(左+右+自身)
Arrays.fill(res,1);//自己能看见自己
//从左向右,找到第i+1栋楼向左可以看见的楼的个数
for(int i = 0; i < n - 1; i++) {
//如果为空的话直接加栈里就行,while循环将小于等于heights[i]的都删除(都档上了)
while (!leftBuild.isEmpty() && heights[i] >= leftBuild.peek()) {
leftBuild.pop();
}
//第i + 1栋楼 左面能看到的楼的数量;
res[i + 1] += leftBuild.size();//注意是累加的
}
//从右向左,找到第i-1栋楼向右可以看见的楼的个数
for (int i = n - 1; i > 0; i--) {
while (!rightBuild && heights[i] > rightBuild.peek()) {
rightBuild.pop();
}
rightBuild.push(heights[i]);
res[i - 1] += rightBuild.size();
}
return res;
}