騰訊一面的時候,面試官給出了一道題,題目如下,當初由於是第一次面試,比較緊張沒做出來,現在回顧總結一下。
題目
小Q在週末的時候和他的小夥伴來到大城市逛街,一條步行街上有很多高樓,共有n座高樓排成一行。
小Q從第一棟一直走到了最後一棟,小Q從來都沒有見到這麼多的樓,所以他想知道他在每棟樓的位置處能看到多少棟樓呢?(當前面的樓的高度大於等於後面的樓時,後面的樓將被擋住)
輸入描述
輸入第一行將包含一個數字n,代表樓的棟數,接下來的一行將包含n個數字wi(1<=i<=n),代表每一棟樓的高度。
1<=n<=100000;
1<=wi<=100000;
輸出描述
輸出一行,包含空格分割的n個數字vi,分別代表小Q在第i棟樓時能看到的樓的數量。
輸入示例
6 5 3 8 3 2 5
輸出示例
3 3 5 4 4 4
說明
當小Q處於位置3時,他可以向前看到位置2,1處的樓,向後看到位置4,6處的樓,加上第3棟樓,共可看到5棟樓。當小Q處於位置4時,他可以向前看到位置3處的樓,向後看到位置5,6處的樓,加上第4棟樓,共可看到4棟樓。
解法
暴力解
將輸入數組逐個遍歷,將當前元素和數組傳給 getCanLooks()
方法處理,getCanLooks()
方法將返回給元素能看到的樓層數。
在 getCanLooks()
方法中,分別往index
的左邊和index
的右邊遍歷,遇到比 leftMax或者rightMax
大的,則將count++
。
暴力解思路上是對的,本地IDE
能跑,但是牛客上就超時了。
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int buildNum = scanner.nextInt();
int[] array = new int[buildNum];
for(int i=0;i<=buildNum-1;i++) {
array[i] = scanner.nextInt();
}
//暴力解
for(int i=0;i<=buildNum-1;i++) {
System.out.print(getCanLooks(array,i) + " ");
}
}
//獲取該位置能看到的樓數
public static int getCanLooks(int[] array, int index) {
int thisHeight = array[index];
int count = 1; //本身爲一棟,初始爲1
int left = index-1;
int leftMax = Integer.MIN_VALUE;
int right = index+1;
int rightMax = Integer.MIN_VALUE;
//從index位置往左遍歷
while(left >= 0) {
if(array[left] > leftMax) {
count++;
leftMax = array[left];
}
left--;
}
//從index位置往右遍歷
while(right <= array.length-1) {
if(array[right] > rightMax) {
count++;
rightMax = array[right];
}
right++;
}
return count;
}
}
使用單調棧
這種方法的思路:
創建一個棧Stack
用來記錄從左往右看或者從右往左看能看到的樓層的下標;
創建leftLook
數組用來記錄從右往左看能看到的樓層數;
創建rightLook
數組用來記錄從左往右看能看到的樓層數;
分別從左往右和從右往左遍歷一次,leftLook
和rightLook
分別記錄,最後兩者相加再+1
就得到當前下標能看到的樓層數。
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int len = sc.nextInt();
int[] arr = new int[len];
for(int i = 0 ; i < len ; i++){
arr[i] = sc.nextInt();
}
//棧記錄能看到的元素下標
Stack<Integer> stack = new Stack<>();
//記錄往左看能看到的數量
int[] leftLook = new int[len];
//記錄往右看能看到的數量
int[] rightLook = new int[len];
//從右往左看
for(int i=0;i<=len-1;i++) {
//棧大小就是當前位置往左能看到的數量
leftLook[i] = stack.size();
//棧不爲空,並且棧頂元素小於當前元素,將棧頂元素移除
while(!stack.isEmpty() && arr[i] >= arr[stack.peek()]) {
stack.pop();
}
//將當前元素下標入棧
stack.push(i);
}
//將棧清空
stack.clear();
//從左往右看
for(int i=len-1;i>=0;i--) {
rightLook[i] = stack.size();
while(!stack.isEmpty() && arr[i] >= arr[stack.peek()]) {
stack.pop();
}
stack.push(i);
}
//leftLook 和 rightLook相加,並加1
for(int i=0;i<=len-1;i++) {
leftLook[i] += 1+rightLook[i];
System.out.print(leftLook[i] + " ");
}
}
}