城市天际线可见建筑数量 —— 核心知识点(大二Java刷题笔记)
- 题目是什么?
从左往右看一排高低不同的建筑(高度数组), 求每一栋建筑,能向左看到多少栋楼。 被挡住的看不见,只有比它高的建筑才能挡住它。
- 本题本质
寻找每个元素左边第一个比它大的数 英文:Previous Greater Element
这是单调栈最经典的应用场景。
- 为什么要用「单调栈」?
- 普通遍历:O(n²),效率低
- 单调栈:O(n),每个元素只入栈、出栈一次
- 栈里只存下标,不存高度,方便计算距离
- 栈的规则(最重要)
栈内保持: 从栈底到栈顶,高度单调递减
- 遇到更高的楼:前面矮的全都没用了,弹出
- 遇到更矮的楼:直接入栈,继续维护递减
- 核心计算逻辑
对第 i 栋楼:
1. 把所有比它矮或相等的栈顶弹出 2. 弹出后:
- 栈空 → 能看到 i+1 栋
- 栈不空 → 能看到 i - 栈顶下标 栋 3. 把当前下标压入栈
- 本题最容易踩的 4 个坑
1. top 先++ 还是先赋值
- 必须:先判断 → 再 top++ → 再赋值 2. 栈满判断错误
- 满: top >= stack.length - 1 3. 栈空时直接 peek / pop
- 必须先判断 isEmpty() 4. 把 >= 写成 >
- 等高也要挡住,必须用 >=
- 一句话总结
矮的踢走,高的留下,算完距离再入栈。 左边第一个更大元素,用单调栈一遍过
8.完整代码展示【这里就懒得拆分了,直接上全部代码】
public class CityStack {
private static int[] heights;//城市建筑高度
private static int size;//栈内元素个数
private int top;//定义栈顶指针
private static int cnt;//统计可见建筑的数量
private int[] stack;
public CityStack() {
this.stack = new int[10];
size = 0;
top = -1;
cnt = 0;
}
//push方法
public void push(int index) {
if (size == 6) {
System.out.println("Stack is full");
return;
}
if(top >= stack.length-1) throw new RuntimeException("Stack is full");
top++;
stack[top] = index;//top属于栈,index属于高度数组的下标,两者不同才能赋值
size++;
}
//pop方法
public int pop() {//pop本身是弹出栈顶元素,不用传入参数
if (top == -1) {
System.out.println("Stack is empty");
throw new RuntimeException("Stack is empty");
} else {
return stack[top--];
}
}
public static void main(String[] args) {
int[] heights = {10, 6, 8, 5, 11, 9};
CityStack cs = new CityStack();
System.out.println(Arrays.toString(heights));
int[] ans = new int[heights.length];
for (int i = heights.length-1;i >= 0 ; i--) {
int cnt = 0;
while(cs.top != -1&&heights[i] >= heights[cs.stack[cs.top]]) {
//这里是拿高度数组里面的高度与栈内已存入的建筑高度比较
cs.pop();
cnt++;
}
ans[i] = cnt + 1;
cs.push(i);
}
System.out.println(Arrays.toString(ans));
}
}