微软面试百题002——看上去貌似很简单实则暗藏杀机 优先栈
1.问题描述:
设计一个栈,添加min函数,可以让我们在O(1)时间复杂度内完成push,pop以及min操作(min操作返回栈内权值最小的元素)
首先我们先来复习一下栈这个数据结构的性质:
栈作为一种先进先出的数据结构,pop出去的一定是刚刚才push进来的元素
但是本题中又牵扯到了min函数,又让我们感觉到了一丝一丝的优先队列的感觉
2.思维发展导图
1.首先刚开始想到既然是优先化一个栈的话,我已开始是这么考虑的,近顺序的存储结构很难来进行min的操作,可不可以采用链栈的方式呢
我是这么考虑的,链栈的话我们只需要额外开辟一个节点指向最小的元素节点就可以了,这不就轻松搞定了吗
显然这么做少考虑了一点,如果最小的成为栈顶的话,没有保存倒数第二小节点地址的我们就绝对会出错,导致我们之后的min的跟踪失败,而要是依旧采用链栈的话,我们为了解决这个问题就必须要开辟大量的节点保存最小信息,反而将问题复杂化了
2.再次开辟一个内存空间,实现的内容是反向优先队列,用来保存递减的顺序
在这里可能就有人会问难道不会出现和上面一样的错误码,实际上我们可以证明,是不会出现那样的错误的,因为这个优先队列始终是保持递减有序的,我们pop的时候如果遇到在优先队列中的元素的话,同步pop,否则优先队列不变,这样做的话,一个优先队列中的节点如果存在一个比他先出现的且比他大的元素是一定在优先队列中比他更靠前的位置中的
3.
动态规划的应用:这个是参考了大神的笔记后发现还可以采用动态规划的思想,虽然一样占用了和第二种方法一样大的内存空间,但是这样子做的话就少了对另一种数据结构的维护,并且动态规划的思想也是非常的而优秀的
我们对于每一个节点不仅开辟内存保留原本应该保留的键值,我们还开辟内存保存该节点之前的栈的元素的最小值,那么根据动态规划的原理,我们只需要维护当前的节点就好
3.代码实现:(C++类封装)
3.1动态规划的思想:
#include"iostream"
#include"cstdio"
#include"cstdlib"
#define N 100
using namespace std;
template<typename T>
struct node
{
T key;
T min;
};
template<typename T>
class prestack
{
public:
prestack()
{
memset(content,0,sizeof(content));
top=0;
}
void push(T);
T pop();
T min();
private:
struct node<T> content[N];
int top;
};
template<typename T>
void prestack<T>::push(T p)
{
if(top==0)
{
content[++top].key=p;
content[1].min=p;
}
else //动态规划的思想
{
if(p<content[top].min) content[++top].min=p;
else content[++top].min=content[top-1].min;
content[top].key=p;
}
}
template<typename T>
T prestack<T>::pop() //小心prestack神明的时候必须要加<T>
{
return content[top--].key;
}
template<typename T>
T prestack<T>::min()
{
return content[top].min;
}
int main()
{
prestack<int> my;
my.push(-2);
my.push(5);
my.push(9);
my.push(4);
my.push(6);
my.push(8);
my.push(-4);
my.push(-2);
cout<<my.min()<<endl;
cout<<my.pop()<<endl;
cout<<my.pop()<<endl;
cout<<my.min()<<endl;
return 0;
} //输出结果是-4,-2,-4,-2,结果正确
3.2优先队列+栈实现
#include"iostream"
#include"cstdlib"
#include"cstring"
#define N 100
using namespace std;
template<typename T>
class prestack
{
public:
prestack()
{
memset(data,0,sizeof(data));
memset(heap,0,sizeof(heap));
top=heapnum=0;
}
void push(T);
T pop();
T min();
private:
T data[N];
T heap[N];
int top;
int heapnum;
};
template<typename T>
void prestack<T>::push(T p)
{
data[++top]=p;
if(p<heap[heapnum]) heap[++heapnum]=p;
}
template<typename T>
T prestack<T>::pop()
{
T help;
help=data[top--];
if(help==heap[heapnum]) heapnum--;
return help;
}
template<typename T>
T prestack<T>::min()
{
return heap[heapnum];
}
int main()
{
prestack<int> my;
my.push(-2);
my.push(5);
my.push(9);
my.push(4);
my.push(6);
my.push(8);
my.push(-4);
my.push(-2);
cout<<my.min()<<endl;
cout<<my.pop()<<endl;
cout<<my.pop()<<endl;
cout<<my.min()<<endl;
return 0;
}
4.疑惑点:
1.为什么C++中结构体模板不能对typedef进行作用,很是奇怪,特定语法吗?时为什么要提出这种语法结构的呢
2.当要再追加问题:请在O(1)时间内min还可以返回出该最小元素在栈内的位置的时候,除了再开辟一内存保存地址之外,还有什么更好的方法吗