剑指Offer(数据结构与算法面试题精讲)C++版——day13
剑指Offer(数据结构与算法面试题精讲)C++版——day13
- 题目一:矩阵中的最大矩形
- 题目二:滑动窗口的平均值
- 题目三:最近请求次数
- 附录:源码gitee仓库
题目一:矩阵中的最大矩形
请在一个由 0、1 组成的矩阵中找出最大的只包含 1 的矩形并输出它的面积。例如,在图 6.6 的矩阵中,最大的只包含 1 的矩阵如阴影部分所示,它的面积是 6。
这道题和day12中第三题——直方图最大矩形面积,比较相似,都是采用单调栈来解决。只是在这里寻找最大矩形,还需要对矩形进行一轮拆解,我们分析发现,可以对二维矩阵降维,设置从最后一行向第一行看的视角,一开始取第一行,得到的矩形高度如图(a)所示,接下来分析第2行,从第2行向前看,如果上面还有1那么进行累加,如果本身为0,那么直接标记为0,如果其后出现了0那么就停止累加,因为这里意味着矩形在视角方向不再连续。我们以最后一行为例分析,由于第4行第一列元素为1,并且其后紧跟一个1元素,那么将其加一,之后由于其后出现了0,那么停止累加,对于第4行第2、3、5列元素,由于一开始为0,所以直接标记为0,对于第4行第4列元素,分析可以得到应该标记为3,于是得到如图(d)所示的标记矩阵。
相当于对于每一行以及其上方的行都进行了考虑(当然这里如果视角改成从左到右分析每一列也是可以的)。换句话说,对于矩阵中的任意一个由 1 组成的矩形,无论它的位置在哪里,它的底部所在行一定会在逐行分析过程中被处理,其高度也会在高度计算中被准确反映,并且通过单调栈能计算出以它为基础的最大矩形面积。最终得到如下代码:
int getMaxLineSquare(vector<int> arr) {//同day12题三,利用严格单调栈根据矩形柱计算最大矩形面积算法stack<int> st;int result=0,size=arr.size(),top=0,height=0,width=0;for(int i=0; i<size; ++i) {while(!st.empty()&&arr[st.top()]>=arr[i]) {height=arr[st.top()];//获取要被弹出去的元素高度 st.pop();//1 1 2 3 3 2width=st.empty() ? i:(i-st.top()-1);//计算矩形宽度,如果栈为空说明左侧没有比它矮的 if(width*height>result) {result=width*height;}}st.push(i);}while(!st.empty()) {top=st.top();height=arr[top];st.pop();width=st.empty()?(arr.size()):(arr.size()-st.top()-1);if(width*height>result) {result=width*height;}}return result;
}
int getMaxRectSquare(vector<vector<int>> arr) {int result=0,tmp=0;for(int i=0,size=arr.size(); i<size; ++i) {int len=arr[i].size();vector<int> height(len,0);for(int k=0; k<len; ++k) {for(int j=i; j>=0; --j) {if(arr[j][k]) {height[k]++;} else {break;}}}for(int i=0; i<len; i++) {cout<<height[i]<<" ";}cout<<endl;tmp=getMaxLineSquare(height);if(tmp>result) {result=tmp;}}return result;
}
题目二:滑动窗口的平均值
请实现如下类型MovingAverage,计算滑动窗口中所有数字的平均值,该类型构造函数的参数确定滑动窗口的大小,每次调用成员函数next时都会在滑动窗口中添加一个整数,并返回滑动窗口中所有数字的平均值。
class MovingAverage {public MovingAverage(int size);public double next(int val);
}
相比于前面的单调栈,这里就容易很多了,由于采用了滑动窗口,窗口每次新增一个元素,那么由于窗口大小固定,那么窗口中第一个元素就应该离开窗口范围。此时就构成了一种先进先出结构,很容易想到使用栈来实现。由于原版使用的Java,这里给替换成C++,得到如下代码:
class MovingAverage {public://这里为了省略了get/set直接使用publicqueue<int> qu;int size;double sum=0;public:MovingAverage(int size) {this->size=size;}double getAverage() {return this->sum/this->size;}double next(int val) {if(this->qu.size()<this->size) {this->qu.push(val);} else {this->sum-=this->qu.front();this->qu.pop();this->qu.push(val);}this->sum+=val;}
};
题目三:最近请求次数
请实现如下类型 RecentCounter,它是统计过去 3000ms 内的请求次数的计数器。该类型的构造函数 RecentCounter 初始化计数器,请求数初始化为 0;函数 ping(int t)在时间 t 添加一个新请求(t 表示以毫秒为单位的时间),并返回过去 3000ms 内(时间范围为 [t - 3000, t])发生的所有请求数。假设每次调用函数 ping 的参数 t 都比之前调用的参数值大。
class RecentCounter{public RecentCounter();public int ping(int t);
}
这里同样满足先进先出的特点,也可以使用队列来实现,于是可以得到如下代码:
class RecentCounter {public:queue<int> qu;int size;int sum=0;int curTime=0;public:RecentCounter(int size) {this->size=size;}int getCount() {return this->qu.size();}int ping(int t) {this->curTime=t;this->qu.push(t);while(!this->qu.empty()&&this->qu.front()+this->size<curTime){this->qu.pop();}}
};
附录:源码gitee仓库
考虑到有些算法需要模拟数据,如果全部放进来代码显得过长,不利于聚焦在算法的核心思路上。于是把所有的代码整理到了开源仓库上,如果想要看详细模拟数据,可在开源仓库自取:【凌空暗羽/剑指offer-C++版手写代码】。
我是【Jerry说前后端】,本系列精心挑选的算法题目全部基于经典的《剑指 Offer(数据结构与算法面试题精讲)》。在如今竞争激烈的技术求职环境下,算法能力已成为前端开发岗位笔试考核的关键要点。通过深入钻研这一系列算法题,大家能够系统地积累算法知识和解题经验。每一道题目的分析与解答过程,都像是一把钥匙,为大家打开一扇通往高效编程思维的大门,帮助大家逐步提升自己在数据结构运用、算法设计与优化等方面的能力。
无论是即将踏入职场的应届毕业生,还是想要进一步提升自己技术水平的在职开发者,掌握扎实的算法知识都是提升竞争力的有力武器。希望大家能跟随我的步伐,在这个系列中不断学习、不断进步,为即将到来的前端笔试做好充分准备,顺利拿下心仪的工作机会!快来订阅吧,让我们一起开启这段算法学习之旅!