栈和队列的一些算法

     看本文之前,推荐本博客的http://blog.csdn.net/lsjseu/article/details/9351141,熟悉一下STL中序列容器的接口以及内部实现结构。

     我先谈一谈栈和队列的基本情况:栈是一种后进先出的结构,STL里面为我们编好了栈,但是我们也可以自己实现。通常有两种实现方式,一种是静态数组或者动态数组的实现方式(在这里,实现的时候,注意如果栈空间满了以后,我们提供扩从容量的策略会比较好),第二种是同过链表的实现方式。运用到栈的算法有哪些呢:括号匹配问题,中缀表达式转后缀表达式问题,迷宫路径搜索问题,非递归遍历问题,回溯搜索问题,递归转非递归。

     再谈谈队列:队列是一种先进先出的结构,队列有双端队列,优先级队列,循环队列。队列也有两种简单的实现方式,一种是通过静态数组或者动态数组,一种是通过链表实现。那么队列被哪些算法用到了:杨式三角,迷宫最短路径搜索(注意这里是最短路径,而用栈的话只能搜索路径,不一定是最短),广度优先遍历问题,层次遍历问题(其实这是广度优先的一个特例),进程调度(优先级队列,堆),


     本文搜集了一下关于栈和队列的一些算法。

(1)用两个栈构成一个队列。

     算法很简单,一个栈负责“插入”,一个栈负责“弹出”。当弹出的栈没有元素的时候,要从插入的栈把元素全部搬过来。

class MyQueue{
private:
	stack<int> insertStack;
	stack<int> popStack;
public:
	int Dequeue(){
		if(popStack.empty()){
			if(insertStack.empty())
				throw new runtime_error("Null Queue");
			while(!insertStack.empty()){//把元素挪过来
				popStack.push(insertStack.top());
				insertStack.pop();
			}
		}
		int result = popStack.top();
		popStack.pop();
		return result;
	}
	void Enqueue(int elem){
		insertStack.push(elem);
	}
}; 
     我这里只是简单的实现了pop和push两种操作,没有去实现STL里面关于队列的其他接口。详细参见本博客:

http://blog.csdn.net/lsjseu/article/details/9108711

     记得在“July微软面试百题系列”里面有个在多线程下实现队列的pop和push操作。我的理解是在pop和push操作的时候需要“加锁”。  
  

(2)两个队列实现一个栈

     这个算法需要用两个队列来回倒腾。下面简单的实现以下。看不懂的话同样可以参考:http://blog.csdn.net/lsjseu/article/details/9108711

class MyStack{
private:
	queue<int> q1;
	queue<int> q2;
public:
	int Pop(){
		if(q1.empty() && q2.empty())
			throw new runtime_error("Null Stack");
		int result;
		if(q1.empty()){
			while(q2.size() != 1){
				q1.push(q2.front());
				q2.pop();
			}
			result = q2.front();
			q2.pop();
			return result;
		}
		if(q2.empty()){
			while(q1.size() != 1){
				q2.push(q1.front());
				q1.pop();
			}
			result = q1.front();
			q1.pop();
			return result;
		}
	}

	void Push(int elem){
		if(q1.empty() && q2.empty()){//两者都为空,随便插进那个都无所谓
			q1.push(elem);
		}
		else if(q1.empty())
			q2.push(elem);
		else
			q1.push(elem);
	}
};


(3)包含min函数的栈(剑指offer)

      剑指offer上给出的解答是构造一个辅助的栈去存储最小值,同样,在编程之美上也给出了同样的答案,只不过编程之美上给出的答案是保持最小值的索引。下面简单的实现了下:

template<class T>
class MinStack{
private:
	stack<T> st;
	stack<T> minst;
public:
	void Push(T t){
		st.push(t);
		if(minst.empty() || (t<minst.top()))//要求T重载了"<"
			minst.push(t);
		else
			minst.push(minst.top());
	}
	T Pop(){
		int popNum;
		if(!st.empty()){
			popNum = st.top();
			st.pop();
			minst.pop();
		}
		else
			throw new runtime_error("Empty Stack");
		return popNum;
	}
	T GetMin(void)const{
		return minst.top();
	}
	T IsEmpty()const{
		return st.empty();
	}
};

(4)包含min函数的队列(编程之美)

    其实队列和栈和上面是一样的,我们不能简单的用上面的这种方案来解答。想想就知道了。对此编程之美给出了两种答案:

     第一种方案:构造一个特殊的最大堆,但是这个堆包含有队列的指针,能够以o(lgn)去插入和删除,得到最小值时间复杂度为o(1);

     第二种方案:利用上面的构造含有min函数的栈来构造这个特殊的队列。

template<class T>
class MinStack{
private:
	stack<T> st;
	stack<T> minst;
public:
	void Push(T t){
		st.push(t);
		if(minst.empty() || (t<minst.top()))//要求T重载了"<"
			minst.push(t);
		else
			minst.push(minst.top());
	}
	T Pop(){
		int popNum;
		if(!st.empty()){
			popNum = st.top();
			st.pop();
			minst.pop();
		}
		else
			/*throw std::runtime_error("Empty Stack");*/
			cout<<"Null Stack"<<endl;
		return popNum;
	}
	T GetMin(void)const{
		return minst.top();
	}
	T IsEmpty()const{
		return st.empty();
	}
};

template<class T>
class MinQueue{
private:
	MinStack<T> sta;
	MinStack<T> stb;
public:
	T GetMin()const{
		if(sta.IsEmpty() && stb.IsEmpty())
			throw new runtime_error("Null Stack");
		else if(!sta.IsEmpty() && stb.IsEmpty())
			return sta.GetMin();
		else if(sta.IsEmpty() && !stb.IsEmpty())
			return stb.GetMin();
		else return min(sta.GetMin(),stb.GetMin());
	}
	void Enqueue(T t){
		sta.Push(t);
	}
	T Dequeue(){
		if(stb.IsEmpty()){
			while(!sta.IsEmpty()){
				stb.Push(sta.Pop());
			}
		}
		return stb.Pop();
	}
};


(4)打印堆栈

#include <iostream>
#include <stack>
using namespace std;

void PrintStack(stack<int>& st)
{
	if(st.empty())
		return;
	int top = st.top();
	cout<<top<<" ";
	st.pop();
	PrintStack(st);
	st.push(top);
}
int main()
{
	stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	PrintStack(st);
	cout<<endl;
	PrintStack(st);//打印两遍,验证堆栈有没有被改变
	system("pause");
	return 0;
}

(5)给堆栈中的元素排序

     啥也不说了,直接看程序。当然这里可以转化为非递归的方法去实现。

#include <iostream>
#include <stack>
using namespace std;

void PrintStack(stack<int>& st)
{
	if(st.empty())
		return;
	int top = st.top();
	cout<<top<<" ";
	st.pop();
	PrintStack(st);
	st.push(top);
}

void SortStack(stack<int>& st)
{
	if(st.empty())return;

	int topNum = st.top();
	st.pop();
	SortStack(st);//顶元素弹出,将下面的排好
	stack<int> tempSt;
	while(!st.empty() && st.top()>topNum){
		tempSt.push(st.top());
		st.pop();
	}
	st.push(topNum);
	while(!tempSt.empty()){
		st.push(tempSt.top());
		tempSt.pop();
	}
}

int main()
{
	stack<int> st;
	st.push(1);
	st.push(4);
	st.push(3);
	PrintStack(st);
	SortStack(st);
	cout<<endl;
	PrintStack(st);//打印两遍,验证堆栈有没有被改变
	system("pause");
	return 0;
}


     按照第五题的思路,我们平时还应该碰到这个问题,用递归的方法去写插入排序。那么我们很快就能写出代码:

#include <iostream>
#include <stack>
#include <cassert>
using namespace std;

void PrintArr(int arr[],int len){
	assert(arr && len>=0);
	for(int i=0; i<len; ++i){
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}

void InsertSortRecursively(int arr[],int index){
	assert(arr && index>=0);

	if(index == 0) return;
	int base = arr[index];
	InsertSortRecursively(arr,index-1);//我们假设前面排好序了
	int i = index-1;
	while(i>=0 && arr[i]>base){
		arr[i+1] = arr[i];
		i--;
	}
	arr[i+1] = base;
}

int main()
{
	const int LEN = 4;
	int arr[LEN] = {3,2,4,5};
	PrintArr(arr,LEN);
	InsertSortRecursively(arr,LEN-1);
	PrintArr(arr,LEN);
	system("pause");
	return 0;
}

(6)栈的逆序问题(准确的说是不能用额外的栈作为辅助空间)

#include <iostream>
#include <stack>
using namespace std;

void PrintStack(stack<int>& st)
{
	if(st.empty())
		return;
	int top = st.top();
	cout<<top<<" ";
	st.pop();
	PrintStack(st);
	st.push(top);
}

void MoveButtomToTop(stack<int>& st)//将栈底部元素挪到顶部
{
	if(st.empty())return;

	int top1 = st.top();//接下来把栈顶元素弹出
	st.pop();
	if(!st.empty()){
		MoveButtomToTop(st);//这里我们假设将栈底部元素挪上来了
		int top2 = st.top(); 
		st.pop();
		st.push(top1);
		st.push(top2);//此时将top1和top2交换就完成了我们的操作
		return;
	}
	st.push(top1);
}

void ReverseStack(stack<int>& st)
{
	if(st.empty())return;
	MoveButtomToTop(st);
	int topNum = st.top();
	st.pop();
	ReverseStack(st);
	st.push(topNum);
}

int main()
{
	stack<int> st;
	st.push(1);
	st.push(4);
	st.push(3);
	PrintStack(st);
	ReverseStack(st);
	cout<<endl;
	PrintStack(st);
	system("pause");
	return 0;
}

(7)将栈顶元素移到栈底部(借鉴上面的算法)

#include <iostream>
#include <stack>
using namespace std;

void PrintStack(stack<int>& st)
{
	if(st.empty())
		return;
	int top = st.top();
	cout<<top<<" ";
	st.pop();
	PrintStack(st);
	st.push(top);
}

void MoveTopToButtom(stack<int>& st){
	if(st.empty())return;

	int firstTop = st.top();
	st.pop();
	if(!st.empty()){
		int secondTop = st.top();
		st.pop();
		st.push(firstTop);
		MoveTopToButtom(st);
		st.push(secondTop);
		return;
	}
	st.push(firstTop);
}


int main()
{
	stack<int> st;
	st.push(1);
	st.push(4);
	st.push(3);
	PrintStack(st);
	MoveTopToButtom(st);
	cout<<endl;
	PrintStack(st);
	system("pause");
	return 0;
}


(8)栈的逆序问题(准确的说是不能用额外的栈作为辅助空间)

      上面第(6)题其实已经实现了一个栈的翻转,但是本题想借鉴(7)来实现栈的翻转。

#include <iostream>
#include <stack>
using namespace std;

void PrintStack(stack<int>& st)
{
	if(st.empty())
		return;
	int top = st.top();
	cout<<top<<" ";
	st.pop();
	PrintStack(st);
	st.push(top);
}

void MoveTopToButtom(stack<int>& st){  //把栈顶元素挪到栈底部
	if(st.empty())return;

	int firstTop = st.top();
	st.pop();
	if(!st.empty()){
		int secondTop = st.top();
		st.pop();
		st.push(firstTop);
		MoveTopToButtom(st);
		st.push(secondTop);
		return;
	}
	st.push(firstTop);
}

void ReverseStack(stack<int>& st){
	if(st.empty())return;

	int topNum = st.top(); //先将顶部元素弹出
	st.pop();
	ReverseStack(st);//假设除了顶部元素外,其他都被翻转了
	st.push(topNum);//把栈顶元素压进去
	MoveTopToButtom(st);//接下来的工作就是把栈顶元素挪到栈的底部
}

int main()
{
	stack<int> st;
	st.push(1);
	st.push(2);
	st.push(3);
	PrintStack(st);
	ReverseStack(st);
	cout<<endl;
	PrintStack(st);
	system("pause");
	return 0;
}

(9)给定栈的入栈序列,判断给定的序列是否可能是该序列的出栈序列。(剑指offer134页)

       这个题用辅助栈来解答。

#include <iostream>
#include <stack>
#include <cassert>
#include <string>
using namespace std;

bool IsPopOrder(int pushSeq[],int popSeq[],int len)
{
	assert(pushSeq && popSeq && len>0);

	stack<int> st;
	int i,j = 0;

	for(i=0; i<len; ++i){
		int popNum = popSeq[i];
		while(st.empty() || st.top()!=popNum){
			st.push(pushSeq[j++]);
			if(j == len)break;
		}
		if(st.top() != popNum)
			break;
		st.pop();
	}
	if(st.empty() && i==len)
		return true;
	else return false;
}


int main()
{
	const int SIZE = 5;
	int pushSeq[SIZE] = {1,2,3,4,5};
	int popSeq[SIZE] = {4,5,3,2,1};
	string result = IsPopOrder(pushSeq,popSeq,SIZE)?"True":"False";
	cout<<result<<endl;
	system("pause");
	return 0;
}

这里说明一下:给定栈的入栈序列,出栈的序列可能有多少种。(这是一个卡特兰数)类似的问题,包括给定前序遍历,中序遍历的有多少种?括号匹配问题。1,2,5......详细参考:http://blog.csdn.net/lsjseu/article/details/11827109


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值