1.用两个栈实现队列
题目:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
思路:
加入队尾 appendTail()函数: 将数字 val 加入栈 A 即可。
删除队首deleteHead()函数: 有以下三种情况。
- 当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
- 否则,当 A 为空: 即两个栈都为空,无元素,因此返回 -1−1 。
- 否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
class CQueue {
public:
stack<int> s1;
stack<int> s2;
CQueue() {
}
void appendTail(int value) {
s1.push(value);
}
int deleteHead() {
int res;
if(!s2.empty()){
res=s2.top();
s2.pop();
}
else{
if(s1.empty()) return -1;
while(!s1.empty()){
s2.push(s1.top());
s1.pop();
}
res=s2.top();
s2.pop();
}
return res;
}
};
2.包含Min函数最小的栈
题目:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof
思路:
将 min() 函数复杂度降为 O(1) ,可通过建立辅助栈实现;
- 数据栈 A : 栈 A 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。
- 辅助栈 B : 栈 B 中存储栈 A 中所有 非严格降序 的元素,则栈 A 中的最小元素始终对应栈 B 的栈顶元素,即 min() 函数只需返回栈 B 的栈顶元素即可。
class MinStack {
public:
stack<int> s1;
stack<int> s2;
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
s1.push(x);
if(s2.empty() || s2.top() >= x){
s2.push(x);
}
}
void pop() {
int tmp=s1.top();
s1.pop();
if(tmp==s2.top()){
s2.pop();
}
}
int top() {
return s1.top();
}
int min() {
return s2.top();
}
};
3.最小的k个数
题目:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof
思路:
(1)堆排序
//借助priority_queue
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> res;
if(k==0) return res;
priority_queue<int> bigHeap;
for(int i=0; i<k; i++) bigHeap.push(arr[i]);
for(int i=k; i<arr.size(); i++){
if(arr[i]<bigHeap.top()){
bigHeap.pop();
bigHeap.push(arr[i]);
}
}
for(int i=0; i<k; i++){
res.push_back(bigHeap.top());
bigHeap.pop();
}
return res;
}
};
(2)快速选择排序(快排变种)
我们的目的是寻找最小的 k 个数。假设经过一次 partition 操作,枢纽元素位于下标 m,也就是说,左侧的数组有 m 个元素,是原数组中最小的 m 个数。那么:
- 若 k = m,我们就找到了最小的 k 个数,就是左侧的数组;
- 若 k<m ,则最小的 k 个数一定都在左侧数组中,我们只需要对左侧数组递归地 parition 即可;
- 若 k>m,则左侧数组中的 m 个数都属于最小的 k 个数,我们还需要在右侧数组中寻找最小的 k-m 个数,对右侧数组递归地 partition 即可。
//快速选择
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
int len=arr.size();
int start=0, end=len-1;
if(len<k || k<=0 || len<=0) return {};
int idx=quickSort(arr, start, end);
while(idx!=k-1){
if(idx > k-1) end=idx-1;
if(idx < k-1) start=idx+1;
idx=quickSort(arr, start, end);
}
return vector<int>(arr.begin(), arr.begin()+k);
}
int quickSort(vector<int>& arr, int l, int r){
int pivot=arr[l];
int i=l,j=r;
while(i<j){
while(i<j && arr[j]>=pivot) j--;
while(i<j && arr[i]<=pivot) i++;
if(i<j){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
}
arr[l]=arr[i];
arr[i]=pivot;
return i;
}
};
快速选择算法的几点局限性:
第一,算法需要修改原数组,如果原数组不能修改的话,还需要拷贝一份数组,空间复杂度就上去了。
第二,算法需要保存所有的数据。如果把数据看成输入流的话,使用堆的方法是来一个处理一个,不需要保存数据,只需要保存 k 个元素的最大堆。而快速选择的方法需要先保存下来所有的数据,再运行算法。当数据量非常大的时候,甚至内存都放不下的时候,就麻烦了。所以当数据量大的时候还是用基于堆的方法比较好。
4.滑动窗口的最大值
题目:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof
思路:
- 遍历数组,将数存放在双向队列中,并用L,R来标记窗口的左边界和右边界。
- 队列中保存的并不是真的数,而是该数值对应的数组下标位置,并且数组中的数要从大到小排序。
- 如果当前遍历的数比队尾的值大,则需要弹出队尾值,直到队列重新满足从大到小的要求。
- 刚开始遍历时,L和R都为0,有一个形成窗口的过程,此过程没有最大值,L不动,R向右移。
- 当窗口大小形成时,L和R一起向右移,每次移动时,判断队首的值的数组下标是否在[L,R]中,如果不在则需要弹出队首的值,当前窗口的最大值即为队首的数。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
deque<int> tmp;
if(nums.empty() || nums.size()<k){
return res;
}
//遍历nums
for(int i=0; i<nums.size(); i++){
//保证从大到小,如果前面小,就pop,直至满足条件
while(!tmp.empty() && nums[i]>=nums[tmp.back()]){
tmp.pop_back();
}
//判断当前队首是否有效
if(!tmp.empty() && tmp.front()<=i-k){
tmp.pop_front();
}
//添加当前值对应下标
tmp.push_back(i);
//当窗口长度为k时,保存当前窗口中最大值
if(i+1>=k){
res.push_back(nums[tmp.front()]);
}
}
return res;
}
};
上题包含Min函数最小的栈,其使用 单调栈 实现了随意入栈、出栈情况下的 O(1) 时间获取 “栈内最小值” 。本题同理,不同点在于 “出栈操作” 删除的是 “列表尾部元素” ,而 “窗口滑动” 删除的是 “列表首部元素” 。
窗口对应的数据结构为 双端队列 ,本题使用 单调队列 即可解决以上问题。
5.队列的最大值
题目:https://leetcode-cn.com/problems/dui-lie-de-zui-da-zhi-lcof
思路:和上题思路基本一样
class MaxQueue {
public:
deque<int> res;
queue<int> q;
MaxQueue() {
}
int max_value() {
if(res.empty()) return -1;
return res.front();
}
void push_back(int value) {
while(!res.empty() && res.back()<value){
res.pop_back();
}
res.push_back(value);
q.push(value);
}
int pop_front() {
if(q.empty()) return -1;
int ans=q.front();
if(ans==res.front()){
res.pop_front();
}
q.pop();
return ans;
}
};