为栈实现高效的max操作
题目: 请设计一个栈,除了提供push(压栈),pop(出栈),peak(取栈顶元素)操作以外,还能提供max(取栈中最大值)的功能,并使得时间复杂度最小。
注:请仔细思考后再看下面的答案
1. 方案一(不可行)
设计一个成员max,等于栈中最大的元素,在每次push操作后,新压入的元素与max比较,然后将max赋值为它们中的大的元素值。
这样,max函数则返回max。
问题:如果栈中等于max的元素pop出去后,该max就不是栈中的最大值了。
2. 方案二(可行,效率太低)
在【方案一】的基础上改进,每当栈中等于max的元素pop出去后,则把剩余元素依次pop到临时数组,找出最大值,赋值给max,然后再倒次把这些元素压回栈中。
问题:效率太低。
类似的方案还有:不使用max,而是每次执行max函数时,把栈中元素pop到临时数组,找出最大值,然后再压回栈中。
3. 方案三(最优方案)
设计两个栈,同步操作,每push一个元素X的时候,栈A正常压入,栈B则将X与B栈顶的元素BT比较,如果X大于BT,则push X到栈中;否则push BT;
执行pop操作时,两个栈同时pop。示意图如下:
这样的算法的时间复杂度只有O(1),你想出来了吗?具体的代码实现如下:
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
template <class T>
class Stack{
public:
Stack(int cap=0){
if(cap)
{
_stack_A.reserve(cap);
_stack_B.reserve(cap);
}
}
bool push(T value);
bool pop(T &value);
bool max(T &value);
bool full();
bool empty();
int size();
friend ostream& operator<< (ostream& os,const Stack<T>&st)
{
for(int i=0;i<st._stack_A.size();i++)
os<<st._stack_A[i]<<" ";
os<<endl;
for(int i=0;i<st._stack_B.size();i++)
os<<st._stack_B[i]<<" ";
return os;
}
private:
vector<T> _stack_A;
vector<T> _stack_B;
};
template <class T>
int Stack<T>::size()
{
return _stack_A.size();
}
template <class T>
bool Stack<T>::empty()
{
return _stack_A.empty();
}
template <class T>
bool Stack<T>::full()
{
return _stack_A.max_size()==_stack_A.size();
}
template <class T>
bool Stack<T>::max(T &value)
{
if(empty())
return false;
value=_stack_B.back();
return true;
}
template <class T>
bool Stack<T>::push(T value)
{
if(full())
return false;
if(empty())
{
_stack_B.push_back(value);
}
else
{
T temp;
T mx;
temp=_stack_B.back();
mx=(temp>value)?temp:value;
_stack_B.push_back(mx);
}
_stack_A.push_back(value);
return true;
}
template <class T>
bool Stack<T>::pop(T& value)
{
if(empty())
return false;
value=_stack_A.back();
_stack_A.pop_back();
_stack_B.pop_back();
return true;
}
int main()
{
int arr[]={1,3,2,4,1,9};
Stack<int> st(10);
for(int i=0;i<6;i++)
st.push(arr[i]);
cout<<st<<endl;
int a;
st.max(a);
cout<<"max:"<<a<<endl;
st.pop(a);
cout<<st<<endl;
st.max(a);
cout<<"max:"<<a<<endl;
}
关于代码中还有一点需要注意,就是这儿使用了模板, 在模板类中重载操作符时有可能出问题。
比如这儿是输出操作符:
首先在参数中Stack中要加上<T>给出类型。
其次要在类内实现这个方法,如果在声明外实现话会报错。
具体解释如下:
在模板类中输入流“>>”和输出流“<<”的重载,若使用友元在类内声明,在类外实现,那么连接时将会报错,但我们可以采用以下三种方式来实现输出流"<<"和"输入流>>"的重载。
一、将输出流"<<"和"输入流>>"重载的实现写在类中
#include <iostream>
using namespace std;
template<class T>
class Test
{
public:
Test(const T& t):data(t){}
//---------------------------------------------
friend ostream& operator<<(ostream& out,Test<T>& t) //输出流重载声明及实现
{
return out<<"data is "<<t.data;
} //--------------------------------------------
friend istream& operator>>(istream& in,Test<T>& t) //输入流重载声明及实现
{
return in>>t.data;
}//---------------------------------------------
private:
T data;
};//-----------------------------------------------------------------
int main()
{
Test<int> b(3);
cout<<b<<'\n';
cin>>b;
cout<<b<<'\n';
return 0;
}
那么输入输出流重载为什么不能在类内声明,类外实现呢??因为模板比较特殊,若果在模板类外实现重载的话:
ostream& operator<<(ostream& out,Test<T>& t)
{
return out<<"data is "<<t.data;
} //--------------------------------------------
上面正好是函数模板的定义,而我们知道操作符重载函数不是类的成员函数,因此此处相当于定义了一个新的函数模板(不同于类中的friend ostream& operator<<(ostream& out,Test<T>& t) )。但若去掉template<class T> ,函数中的参数Test<T>就不知是什么类型,所以不能在模板类内声明,类外实现操作符重载。
二、既然类外实现相当于重定义了一个函数模板,那么只要他不使用类的私用成员即可,因此重载的函数模板只有通过类的公有成员函数来实现对类的私有成员的操作,这样不必在类内声明它为友元,直接在类外重载即可。
#include <iostream>
using namespace std;
template<class T>
class Test
{
public:
Test(const T& t):data(t){}
T GetData()const{return data;}
void SetData(T &item){data=item;}
private:
T data;
};//-----------------------------------------------------------------
template<class T>
ostream& operator<<(ostream& out,Test<T>& t)
{
return out<<"data is "<<t.GetData();
} //--------------------------------------------
template<class T>
istream& operator>>(istream& in,Test<T>& t)
{
T item;
in>>item;
t.SetData(item);
return in;
}//---------------------------------------------
int main()
{
Test<int> b(3);
cout<<b<<'\n';
cin>>b;
cout<<b<<'\n';
return 0;
}
三、使用过渡函数
#include <iostream>
using namespace std;
template<class T>
class Test
{
public:
Test(const T& t):data(t){}
//---------------------------------------------
template<class CharT,class CharTraits>
basic_ostream<CharT,CharTraits>& Output(basic_ostream<CharT,CharTraits>& out)const //输出流过渡函数
{
return out<<"data is "<<data;
} //--------------------------------------------
template<class CharT,class CharTraits>
basic_istream<CharT,CharTraits>& Input(basic_istream<CharT,CharTraits>& in) //输入流过渡函数
{
return in>>data;
}//---------------------------------------------
private:
T data;
};//-----------------------------------------------------------------
template<class T,class CharT,class CharTraits>
basic_ostream<CharT,CharTraits>& operator<<(basic_ostream<CharT,CharTraits>& out,const Test<T>& t) //输出流重载
{
return t.Output(out);
}//------------------------------------------------------------------
template<class T,class CharT,class CharTraits>
basic_istream<CharT,CharTraits>& operator>>(basic_istream<CharT,CharTraits>& in,Test<T>& t) //输入流重载
{
return t.Input(in);
}//------------------------------------------------------------------
int main()
{
Test<int> b(4);
cout<<b<<'\n';
cin>>b;
cout<<b<<'\n';
return 0;
}
参考:http://ticktick.blog.51cto.com/823160/405582