力扣 队列中可以看到的人数

题目

有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同heights[i] 表示第 i 个人的高度。

一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人  。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], ..., heights[j-1]) 。

请你返回一个长度为 n 的数组 answer ,其中 answer[i] 是第 i 个人在他右侧队列中能 看到 的 人数 。

示例 1:

力扣 队列中可以看到的人数_第1张图片

输入:heights = [10,6,8,5,11,9]
输出:[3,1,2,1,1,0]
解释:
第 0 个人能看到编号为 1 ,2 和 4 的人。
第 1 个人能看到编号为 2 的人。
第 2 个人能看到编号为 3 和 4 的人。
第 3 个人能看到编号为 4 的人。
第 4 个人能看到编号为 5 的人。
第 5 个人谁也看不到因为他右边没人。

示例 2:

输入:heights = [5,1,2,3,10]
输出:[4,1,1,1,0]

提示:

  • n == heights.length
  • 1 <= n <= 105
  • 1 <= heights[i] <= 105
  • heights 中所有数 互不相同 。

提交代码
提交代码1
//队列中可以看到的人数

//向右看
//如果这个人比他右边的人矮,则能看到1个人
//实际上就是向右找递增的一组身高序列,直至遇到一个比这个人高的 
#include
#include
#define MAX_NUM 100000
using namespace std;

vector heights;//n个人的身高

int n;//队列人数

struct Person{
	int seeNum;//向右看见的人数
	int stopPerson;//停在第几号人就看不到了
	int stopHeight;//停的第几号人的身高
};

Person person[MAX_NUM];//记忆化数组 

//找右边能看见几个人
void find(int p){
	//printf("\n%d\n",p);
	//至少能看见一个人 
	person[p].seeNum = 1;
	
	//判断和右边的人谁高
	if(heights[p] <= heights[p + 1]){
		//比右边的人矮时 
		person[p].stopPerson = p + 1;
		person[p].stopHeight = heights[p + 1];
		return;
	}
	
	//比右边的人高
	//判断和右边的人能看见的最右边的人谁高
	//若这个人更高,接着和最右边的那个人能看见的最右边的人比
	int t = p + 1;
	while(t < n){
		person[p].seeNum += person[t].seeNum;
		person[p].stopPerson = person[t].stopPerson;
		person[p].stopHeight = person[t].stopHeight; 
		
		if(heights[p] <= person[t].stopHeight){
			person[p].seeNum -= person[t].seeNum;
			int temp = heights[t];
			for(int i = t + 1;i <= person[t].stopPerson;i++){
				if(temp < heights[i]){
					temp = heights[i];
					person[p].seeNum++;
					person[p].stopPerson = i;
					person[p].stopHeight = heights[i];
				}
			}
			break;
		}else{
			//判断是否t能看见但p看不见
			int temp = 0;
			for(int i = t + 1;i <= person[t].stopPerson && i < n;i++){
				if(heights[i] < heights[t]){
					if(temp < heights[i]){
						temp = heights[i];
						person[p].seeNum--;
					}
				}else{
					break;
				}
			} 
		}
		
		/*if(heights[t] > person[t].stopHeight){
			person[p].seeNum -= person[t].seeNum;
			person[p].stopPerson = t;
			person[p].stopHeight = heights[t];
			break;
		}
		
		int temp = heights[t];
		
		if(heights[t] <= person[t].stopHeight){
			//比右边的人能看见的最右边的人矮时
			person[p].seeNum -= person[t].seeNum;
			for(int i = t + 1;i <= person[t].stopPerson;i++){
				if(temp < heights[i]){
					temp = heights[i];
					person[p].seeNum++;
					person[p].stopPerson = i;
					person[p].stopHeight = heights[i];
				}else{
					//person[p].seeNum++;
					//person[p].stopPerson = i;
					//person[p].stopHeight = heights[i];
					//break;
					continue;
				}	
			}
		}
		
		if(heights[p] < temp){
			break;
		}*/
		
		t = person[t].stopPerson;
	}
	//printf("%d\n",person[p].seeNum);
} 

int main(){
	//输入每个人的身高
	int h;//存储每个人的身高
	//scanf()成功接收输入的时候返回值为1 
	//用scanf()循环读入遇到回车无法停下来 
	/*while(scanf("%d",&h) == 1){
		heights.push_back(h);
	}*/
	//std::cin.peek()来查看输入流中的下一个字符
	while(cin.peek() != '\n'){
		scanf("%d",&h);
		heights.push_back(h);
	}
	
	//-------------------------------
	
	n = heights.size();//队列人数
	
	//初始化最右边的人
	person[n - 1].seeNum = 0;
	person[n - 1].stopPerson = n;
	person[n - 1].stopHeight = heights[n - 1];
	
	//从右向左找 
	for(int i = n - 2;i >= 0;i--){
		find(i);
	}
	
	//输出结果
	for(int i = 0;i < n;i++){
		printf("%d ",person[i].seeNum);
	} 
	
	return 0;
} 

上述代码逻辑正确,但在测试34/42个用例的时候出现了TLE问题,需要优化时间复杂度。

一次性输入数组后回车方面,可以使用借助cin.peek()判断是否输入了'\n',来决定是否停止输入,每个输入依旧用scanf()就行。

 提交代码2
//队列中可以看到的人数

//向右看
//如果这个人比他右边的人矮,则能看到1个人
//实际上就是维护一个单调栈,栈中的身高是单调的,栈的栈底的身高是最高的 
//每次从栈顶弹出的个数就是能看见的人的个数 
#include
#include
//包含栈数据结构 
#include
#define MAX_NUM 100000
using namespace std;

vector heights;//n个人的身高

int n;//队列人数

//借助单调栈 
stack s;//身高单调栈,用于从右向左推理每个人可以看到的右边的人的身高

vector result;//结果容器 

//找右边能看见几个人
void find(int p){
	//单调栈中的元素个数 
	//int N = s.size();//本人能看见的人的个数 
	int count = 0;//统计弹出单调栈的元素的个数,即结果 
	
	//错误思路 
	//存储结果
	//result.push_back(N);
	
	//编译出错:栈stack不能像数组一样用下标访问 
	//调整单调栈中原本的身高——出栈
	/*for(int i = N - 1;i >= 0;i--){
		if(heights[p] >= s[i]){
			s.pop();
			count++;
		}else{
			break;
		}
	}*/
	while(!s.empty()){
		if(heights[p] >= s.top()){
			s.pop();
			count++;
		}else{
			//第一个不用出栈的也能看见
			count++; 
			break;
		}
	}
	
	//将p号人的身高压入栈
	s.push(heights[p]);
	
	//存储结果
	result.push_back(count);
} 

int main(){
	//输入每个人的身高
	int h;//存储每个人的身高
	//scanf()成功接收输入的时候返回值为1 
	//用scanf()循环读入遇到回车无法停下来 
	/*while(scanf("%d",&h) == 1){
		heights.push_back(h);
	}*/
	//std::cin.peek()来查看输入流中的下一个字符
	while(cin.peek() != '\n'){
		scanf("%d",&h);
		heights.push_back(h);
	}
	
	//-------------------------------
	
	n = heights.size();//队列人数
	
	//初始化最右边的人
	//result.push_back(0);
	
	//从右向左找 
	for(int i = n - 1;i >= 0;i--){
		find(i);
	}
	
	//输出结果
	for(int i = n - 1;i >= 0;i--){
		printf("%d ",result[i]);
	} 
	
	return 0;
} 

解题思路:

        利用单调栈,栈中元素是单调递增或递减的。

        从右向左依次判断每个人向右能看见的人,始终保持单调栈中的元素是单调递减的,即单调栈里的身高序列是一个未被覆盖遮挡的身高序列,对于不符合这一规则的身高序列是没必要存储的。

        当判断p号人时,判断栈顶身高是否高于p的身高,若小于p的身高,则出栈,判断新栈顶,出栈的这个人就是p号人能看见的,直至遇到一个大于等于p的身高(这个人不出栈但也能看见)或栈为空。

总结

1.C++中std提供了reverse() ,实现元素序列的反转,可以反转vector、string等,如reverse(vector.begin(),bector.end());

2.头文件提供了数据结构stack,实现了栈的一些基本功能,声明方法:

        #include

        stack s;

3.不能像数组一样使用下标访问栈,只能通过top()访问栈顶元素,不能随机访问。

4.stack的常用方法:

        ①push()

        ②pop()

                没有返回值

        ③top()

                返回栈顶元素

        ④empty()

                返回true或false

        ⑤size()

                返回栈中元素个数

5.单调栈

你可能感兴趣的:(leetcode,算法,数据结构)