您的位置:首页 > 理论基础 > 数据结构算法

[ACM训练] 算法初级 之 数据结构 之 栈stack+队列queue (基础+进阶+POJ 1338+2442+1442)

2016-10-24 23:15 309 查看
再次面对像栈和队列这样的相当基础的数据结构的学习,应该从多个方面,多维度去学习。

首先,这两个数据结构都是比较常用的,在标准库中都有对应的结构能够直接使用,所以第一个阶段应该是先学习直接来使用,下一个阶段再去探究具体的实现,以及对基本结构的改造!

C++标准库中的基本使用方法:

栈: #include<stack>

定义栈,以如下形式实现: stack<Type> s; 其中Type为数据类型(如 int,float,char等)

常用操作有:

s.push(item);    //将item压入栈顶
s.pop();        //删除栈顶的元素,但不会返回
s.top();       //返回栈顶的元素,但不会删除,,,,,,,,,在出栈时需要进行两步,即先top()获得栈顶元素,再pop()删除栈顶元素
s.size();      //返回栈中元素的个数
s.empty();     //检查栈是否为空,如果为空返回true,否则返回false

最基本的用法就是:

stack<int> st;---------------------------------->栈

int first=1;

st.push(first);//入栈1

int second=2;

st.push(second);//入栈2

first=st.top();//first变成了2

st.pop();//出栈

队列:#include<queue>

queue<int> q; //定义一个 int 型的队列 ---------------------------------->队列

q.empty()//如果队列为空返回true,否则返回false

q.size() //返回队列中元素的个数

q.push() //在队尾压入新元素


q.front() //返回队首元素的值,但不删除该元素


q.pop() //删除队列首元素但不返回其值

q.back()//返回队列尾元素的值,但不删除该元素

另外在队列中还有优先队列,priority_queue<.........>---------------------------------->优先队列

要包含头文件:

#include<functional>
#include<queue>

优先队列支持的操作有:

q.empty()    //如果队列为空,则返回true,否则返回false
q.size()     //返回队列中元素的个数
q.pop()    //删除队首元素,但不返回其值
q.top()    //返回具有最高优先级的元素值,但不删除该元素,注意与传统队列的不同以及和栈的相同点
q.push(item) //在基于优先级的适当位置插入新元素

优先队列是我们比较不熟悉的一种结构,下面整体上做一个总结学习:

优先队列是队列的一种,不过它可以按照自定义的一种方式(数据的优先级)来对队列中的数据进行动态的排序,每次的push和pop操作,队列都会动态的调整,以达到我们预期的方式来存储。

例如:我们常用的操作就是对数据排序,优先队列默认的是数据大的优先级高,所以我们无论按照什么顺序push一堆数,最终在队列里总是top出最大的元素。

所以,优先队列在一些定义了权重的地方很有用,priority_queue特别之处在于,允许用户为队列中存储的元素设置优先级。这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。标准库默认使用<操作符来确定对象之间的优先级关系,所以如果要使用自定义对象,需要重载 < 操作符。

使用上有这么几种类型:

1 priority_queue<int>que;//采用默认优先级构造队列
2
3 priority_queue<int,vector<int>,cmp1>que1;//最小值优先 ,,,,,这里定义底层实现以vector实现
4 priority_queue<int,vector<int>,cmp2>que2;//最大值优先
5
6 priority_queue<int,vector<int>,greater<int> >que3;//最小值优先,另外需注意“>>”会被认为错误,
7 priority_queue<int,vector<int>,less<int> >que4;//最大值优先
8
9 priority_queue<number1>que5; //最小优先级队列 --->针对自定义的数据结构
10 priority_queue<number2>que6; //最大优先级队列


1、默认优先级队列,默认的优先级是按照数据的大小来决定的。默认为最大值优先的。

2、设置的最小值或者最大值优先队列,使用一个比较结构来标识来表明比较的方式,这里的cmp为:

1 struct cmp1
2 {
3     bool operator ()(int &a,int &b)
4     {
5         return a>b;//最小值优先  
6     }
7 };
8
9 struct cmp2
10 {
11     bool operator ()(int &a,int &b)
12     {
13         return a<b;//最大值优先
14     }
15 };


3、采用头文件"functional"内定义的优先级,即greater<int>/less<int>,来标识,除此之外可以不用包含此头文件。

4、或者使用自定义的结构,但结构内需重载操作符<,比如这里的number1和number2:

//自定义数据结构
struct number1
{
int x;
bool operator < (const number1 &a) const
{
return x>a.x;//最小值优先
}
};
struct number2
{
int x;
bool operator < (const number2 &a) const
{
return x<a.x;//最大值优先
}
};


总结,能直接进行数据大小比较的,就是默认为最大值优先a<b,如果要更改,就是用一个cmp,更改为a>b就变成了最小值优先了,而对于无法直接进行比较的数据结构,就自定义一个<运算符重载,制定一个元素进行比较,默认的按照最大值优先,即x<a.x,若要更改就改成x>a.x即可变成最小值优先。

基本用法就是这个样子,需要从题目中进行锻炼,对于栈和队列基本都比较简单,但是对于优先队列就需要重点去掌握了!!!

针对于优先级队列,有人总结出几点常用的功能:(其实大致就是上面的四种类型的用法)

1、优先队列最基本的功能就是出队时不是按照先进先出的规则,而是按照队列中优先级顺序出队

  知识点:1、一般存放实型类型,可比较大小

      2、默认情况下底层以Vector实现

      3、默认情况下是大顶堆,也就是大者优先级高,可以自定义优先级比较规则

1 priority_queue<int> Q;
2 Q.push(2);
3 Q.push(5);
4 Q.push(3);
5 while(!Q.empty())
6 {
7        cout<<Q.top()<<endl;
8        Q.pop();
9 }//这样就是一个按照顺序排序的输出


2、可以将一个存放实型类型的数据结构转化为优先队列,这里跟优先队列的构造函数相关,

使用的是 priority_queue(InputIterator first,InputIterator last)

给出了一个容器的开口和结尾,然后把这个容器内容拷贝到底层实现(默认vector)中去构造出优先队列。

1 int a[5]={3,4,5,2,1};
2 priority_queue<int> Q(a,a+5);
3 while(!Q.empty())
4 {
5       cout<<Q.top()<<endl;
6       Q.pop();
7 }


3、可以定义了一个(Node),底层实现以vector实现(第二个参数),优先级为小顶堆(第三个参数)。

前两个参数没什么说的,很好理解,其中第三个参数,默认有三写法:

小顶堆:greater<TYPE>

大顶堆:less<TYPE> --------->需要使用头文件:#include<functional>

如果想自定义优先级而TYPE不是基本类型,而是复杂类型,例如结构体、类对象,则必须重载其中的operator(),即cmp

经典例题:Ugly Numbers http://poj.org/problem?id=1338

Input

Each line of the input contains a postisive integer n (n <= 1500).Input is terminated by a line with n=0.
Output

For each line, output the n’th ugly number .:Don’t deal with the line with n=0.
Sample Input

1
2
9
0

Sample Output

1
2
10


此题只使用优先级队列不太能解决问题,解决问题需要两个条件从小到大的排序+无重复,只使用优先级队列可以解决排序,而无重复只能使用自定义逻辑来判断,个人认为使用set能更优雅的解决问题。
下面给出代码,使用set解决问题:


1 #include <iostream>
2 #include <cstdio>
3 #include <stack>
4 using namespace std;
5
6 template <typename T>//使用模板类T
7 class StackWithMin
8 {
9 public:
10     stack<T> dataStack;
11     stack<T> minStack;
12
13     void push(T t)//入栈,数据栈直接入栈,辅助栈需要比较大小后入栈
14     {
15         dataStack.push(t);
16         if(minStack.size()==0 || t<minStack.top())
17             minStack.push(t);
18         else
19             minStack.push(minStack.top());
20     }
21
22     T top()
23     {
24         return dataStack.top();
25     }
26
27
28     void pop()//出栈时数据栈和辅助栈同时操作即可,仍然设计遵循标准用法
29     {
30         dataStack.pop();
31         minStack.pop();
32     }
33
34     T getMin()
35     {
36         return minStack.top();
37     }
38 };
39
40
41 int main()
42 {
43     StackWithMin<int> mstack;
44
45     int eles[4] = {3,4,2,5};
46     for (int i=0;i<4;i++)
47         mstack.push(eles[i]);
48
49     cout<<mstack.getMin()<<endl;//2
50     mstack.pop();//5
51     mstack.pop();//2
52     cout<<mstack.getMin()<<endl;//3
53     mstack.push(1);
54     cout<<mstack.getMin()<<endl;//1
55
56     return 0;
57 }


View Code

拓展:带取最小值的队列

实现一个队列,带有出队(deQueue),入队(enQueue),取最小元素(getMin)三个方法。要保证这三个方法的时间复杂度都尽可能小。

与上面相似,实现为:

没有想好实现方式,先暂时留空。

下面是牛客网关于队列和栈的学习相关总结
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: