第3章 栈和队列综合习题(leetcode+vjudge)
2017-10-06 10:17
405 查看
这里选择了一些栈和队列的练习,一部分来自leetcode,也有一部分通过vjudge
取自其他平台。没有选择综合性较强的题目,单纯使用栈、队列、线性表的基本操作和思想就可以解决了。
对于双端队列和优先队列,这里仅仅使用STL进行解决,说明一下使用他们进行解决的办法,不提及物理实现及相关问题。双端队列的物理实现可以使用带尾结点的链表;而优先队列需要使用二叉堆(binary heap)实现,这在第10章内排序再写总结。
要求只能使用基本的栈操作(
栈是LIFO特性,而队列是FIFO特性。也就是说队列的出列,实际上要取栈底元素。因此这里可以使用两个栈来完成,需要取栈底元素,只需要将所有元素放入第二个栈,取其
栈顶就可以了。
参考解答:
要求只能使用基本的队列操作(
和上一道题较为类似,一样需要处理LIFO和FIFO的关系。也需要使用两个队列,取最后元素时,则已经取出的元素的个数总是比总数少1。将第二个队列作为临时空间,同时维护一个计数器就可以了。
这是《数据结构与算法分析C语言描述》的一道练习题。在常数时间内完成取最小值操作,可以考虑维护一个
字符串有着如下描述:
例如
这道题是使用栈的一道很好的题目;这道题目也能展现递归程序非递归化的一些内容。
与双栈法直接求表达式值类似,这里也使用了2个栈,一个用来存放k的值,一个用来存放当前层解码后的字符串。扫描到数字时,需要将其转化成正整数k,扫描到左括号,将深度增加一层,当前层处理后的字符串入栈,并且将k入栈(k指的是下一层需要的重复次数)。扫描到右括号,将当前层的字符串重复k次(取自栈顶),然后与前一层的字符串进行拼接。最后只需要返回第一层的字符就可以了。
如果从递归的角度来看,体现了保存状态-自我调用-恢复状态的过程。只不过在递归方法,依赖于一定的硬件体系结构(栈帧模型),编译器为我们自动完成了这些操作。
我们关心指定位置的任务需要多久才能出结果,你需要回答这个问题。假设处理每个任务需要一分钟,其他操作不占用时间。
比如
又比如
这是一到使用双端队列、优先队列解决问题的题目,主要是队列的模拟用法。使用优先队列存储优先级,而双端队列模拟整个过程。
输入数据包含多组测试,请处理到文件结束。
每组数据第一行有一个正整数N (0<N<2000)表示发生事件的数目。
接下来有N行分别表示发生的事件。
一共有两种事件:
1:
2:
对于每个
诊治人的编号ID的定义为:在一组测试中,
例如
这道题同样是优先队列的使用。优先队列的使用,关键之一在于自定义比较函数。这里可以很容易根据题目要求构造出比较函数(STL的仿函数),然后直接使priority_queue就可以了。
详细描述见POJ。
题目描述提示使用了双栈实现,实际上这是一个类似共享栈模型的问题。使用顺序存储结构很容易实现。VISIT操作,将整个顺序表扩容,当前页面指针也同时向后移动,同时将整个栈顶移动;BACK操作将当前指针前移就可以了。
取自其他平台。没有选择综合性较强的题目,单纯使用栈、队列、线性表的基本操作和思想就可以解决了。
对于双端队列和优先队列,这里仅仅使用STL进行解决,说明一下使用他们进行解决的办法,不提及物理实现及相关问题。双端队列的物理实现可以使用带尾结点的链表;而优先队列需要使用二叉堆(binary heap)实现,这在第10章内排序再写总结。
用栈实现队列(Implement Stack using Queues)
需要使用栈完成一个队列,队列支持如下操作:操作 | 描述 |
---|---|
push(x) | 将x进入队列 |
pop() | 将队首元素出列 |
peek() | 返回队首元素 |
empty() | 判断队列是否为空 |
push、
pop、
empty、
top)。
栈是LIFO特性,而队列是FIFO特性。也就是说队列的出列,实际上要取栈底元素。因此这里可以使用两个栈来完成,需要取栈底元素,只需要将所有元素放入第二个栈,取其
栈顶就可以了。
参考解答:
class MyQueue { public: stack<int> st1, st2; 4000 /** Initialize your data structure here. */ MyQueue() { } /** Push element x to the back of queue. */ void push(int x) { st1.push(x); } /** Removes the element from in front of queue and returns that element. */ int pop() { while(!st1.empty()){ st2.push(st1.top()); st1.pop(); } int result = st2.top(); st2.pop(); while(!st2.empty()){ st1.push(st2.top()); st2.pop(); } return result; } /** Get the front element. */ int peek() { while(!st1.empty()){ st2.push(st1.top()); st1.pop(); } int result = st2.top(); while(!st2.empty()){ st1.push(st2.top()); st2.pop(); } return result; } /** Returns whether the queue is empty. */ bool empty() { return st1.empty(); } };
用队列实现栈(Implement Stack using Queues )
需要使用队列完成一个栈,栈支持如下操作:操作 | 描述 |
---|---|
push(x) | 将x入栈 |
pop() | 将栈顶元素出栈 |
top() | 返回栈顶元素 |
empty() | 判断栈是否为空 |
push、
pop、
empty、
front)。
和上一道题较为类似,一样需要处理LIFO和FIFO的关系。也需要使用两个队列,取最后元素时,则已经取出的元素的个数总是比总数少1。将第二个队列作为临时空间,同时维护一个计数器就可以了。
class MyStack { public: queue<int> qu1; queue<int> qu2; int ptr; /** Initialize your data structure here. */ MyStack() { } /** Push element x onto stack. */ void push(int x) { qu1.push(x); } /** Removes the element on top of the stack and returns that element. */ int pop() { int counter = 0; while(!qu1.empty()){ qu2.push(qu1.front()); qu1.pop(); counter++; } counter--; //将计数器自减,以便于取出队尾元素 while(counter > 0){ qu1.push(qu2.front()); qu2.pop(); counter--; } int result = qu2.front(); qu2.pop(); return result; } /** Get the top element. */ int top() { int counter = 0; while(!qu1.empty()){ qu2.push(qu1.front()); qu1.pop(); counter++; } counter--; while(counter > 0){ qu1.push(qu2.front()); qu2.pop(); counter--; } int result = qu2.front(); qu2.pop(); qu1.push(result); return result; } /** Returns whether the stack is empty. */ bool empty() { return qu1.empty(); } };
最小栈(Min Stack)
要求设计一个栈,除了支持最基本的栈操作,还需要支持在O(1)时间内完成的取最小元素的操作。这是《数据结构与算法分析C语言描述》的一道练习题。在常数时间内完成取最小值操作,可以考虑维护一个
min变量,指向最小元素的下标。在入栈和出栈时维护这个特性即可。下面的解法,能够保证插入O(1),删除O(n)。
class MinStack { public: /** initialize your data structure here. */ vector<int> _data; int min; MinStack() { min = -1; } void push(int x) { if(_data.empty()){ min = 0; } _data.push_back(x); min = _data.back() < _data[min] ? _data.size() - 1 : min; } void pop() { if(_data.empty()){ return; } _data.pop_back(); min = 0; for(int i = 1; i < _data.size(); ++i){ if(_data[i] < _data[min]){ min = i; } } } int top() { return _data.back(); } int getMin() { return _data[min]; } };
字符串解码(Decode String)
按照如下规则解码字符串:字符串有着如下描述:
k[encoded_string],表示将
encoded_string重复k次。特别的,当
k = 1时,
k和中括号
[]省略不写。可以嵌套。字符串
encoded_string不包含小写字母以外的其他字符。整个字符串不包括空格等其他字符。
例如
s = "3[a]2[bc]", return "aaabcbc". s = "3[a2[c]]", return "accaccacc". s = "2[abc]3[cd]ef", return "abcabccdcdcdef".
这道题是使用栈的一道很好的题目;这道题目也能展现递归程序非递归化的一些内容。
与双栈法直接求表达式值类似,这里也使用了2个栈,一个用来存放k的值,一个用来存放当前层解码后的字符串。扫描到数字时,需要将其转化成正整数k,扫描到左括号,将深度增加一层,当前层处理后的字符串入栈,并且将k入栈(k指的是下一层需要的重复次数)。扫描到右括号,将当前层的字符串重复k次(取自栈顶),然后与前一层的字符串进行拼接。最后只需要返回第一层的字符就可以了。
如果从递归的角度来看,体现了保存状态-自我调用-恢复状态的过程。只不过在递归方法,依赖于一定的硬件体系结构(栈帧模型),编译器为我们自动完成了这些操作。
class Solution { public: string decodeString(string s) { stack<int> st; stack<string> st_s; int num = 0; int counter; string dstr = "", tmp; for(auto it = s.begin(); it != s.end(); ++it){ if(*it >= '0' && *it <= '9'){ num = num * 10 + (*it - '0'); }else if(*it == '['){ st_s.push(dstr); dstr = ""; st.push(num); num = 0; }else if(*it == ']'){ counter = st.top(); st.pop(); tmp = st_s.top(); while(counter--){ tmp += dstr; } st_s.pop(); dstr = tmp; }else{ dstr.push_back(*it); } } return dstr; } };
打印机队列(Printer Queue,POJ 3125,NWERC 2006)
每个任务都有不同的优先级,数字越大越紧急。打印机需要这样处理这些任务:第一个任务如果不是当前所有任务中最紧急的,则放到最后; 否则,处理并移除该任务。
我们关心指定位置的任务需要多久才能出结果,你需要回答这个问题。假设处理每个任务需要一分钟,其他操作不占用时间。
比如
Input: 4 2 1 2 3 4 Output: 2
又比如
Input: 6 0 1 1 9 1 1 1 Output: 5
这是一到使用双端队列、优先队列解决问题的题目,主要是队列的模拟用法。使用优先队列存储优先级,而双端队列模拟整个过程。
#include<cstdio> #include<algorithm> #include<deque> #include<queue> using namespace std; struct person { int level; bool me; person(int lv,int m):level(lv),me(m){} }; int main() { int T; scanf("%d",&T); while(T--) { priority_queue<int> pq; deque<person> dq; int n,mypos,i; scanf("%d%d",&n,&mypos); for(i=0;i<n;i++) { int tmp; scanf("%d",&tmp); if(i==mypos) dq.push_back(person(tmp,true)); else dq.push_back(person(tmp,false)); pq.push(tmp); } int cnt=1; while(!pq.empty()) { if(dq.front().level!=pq.top()) { dq.push_back(dq.front()); } else { if(dq.front().me) break; else { pq.pop(); cnt++; } } dq.pop_front(); } printf("%d\n",cnt); } return 0; }
看病要排队(HDU 1873, 2008浙江大学研究生复试全真模拟 )
0068所去的医院有三个医生同时看病。而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。输入数据包含多组测试,请处理到文件结束。
每组数据第一行有一个正整数N (0<N<2000)表示发生事件的数目。
接下来有N行分别表示发生的事件。
一共有两种事件:
1:
IN A B,表示有一个拥有优先级B的病人要求医生A诊治。(0<A<4,0<B<11)
2:
OUT A,表示医生A进行了一次诊治,诊治完毕后,病人出院。(0<A<4)
对于每个
OUT A事件,请在一行里面输出被诊治人的编号ID。如果该事件时无病人需要诊治,则输出
EMPTY。
诊治人的编号ID的定义为:在一组测试中,
IN A B事件发生第K次时,进来的病人ID即为K。从1开始编号。
例如
[Input] 7 IN 1 1 IN 1 2 OUT 1 OUT 2 IN 2 1 OUT 2 OUT 1 2 IN 1 1 OUT 1 [Output] 2 EMPTY 3 1 1
这道题同样是优先队列的使用。优先队列的使用,关键之一在于自定义比较函数。这里可以很容易根据题目要求构造出比较函数(STL的仿函数),然后直接使priority_queue就可以了。
#include<cstdio> #include<queue> #include<cstring> using namespace std; struct patient { int priority; int time; patient(int p, int t) { priority = p; time = t; } }; struct mycmp { bool operator()(patient a, patient b) { if (a.priority == b.priority) return a.time > b.time; else return a.priority < b.priority; } }; int main() { int T; while (scanf("%d", &T) != EOF) { priority_queue<patient, vector<patient>, mycmp> doctors[4]; int id = 0; while (T--) { char ops[10]; scanf("%s", ops); if (!strcmp(ops, "IN")) { int d, p; scanf("%d %d", &d, &p); doctors[d].push(patient(p, ++id)); } else { int d; scanf("%d", &d); if (doctors[d].empty()) printf("EMPTY\n"); else { printf("%d\n", doctors[d].top().time); doctors[d].pop(); } } } } return 0; }
网页浏览器(Web Navigation,POJ 1028)
模拟实现浏览器的前进和后退操作。VISIT 表示浏览某地址 FORWARD 表示前进 BACK 表示后退
详细描述见POJ。
题目描述提示使用了双栈实现,实际上这是一个类似共享栈模型的问题。使用顺序存储结构很容易实现。VISIT操作,将整个顺序表扩容,当前页面指针也同时向后移动,同时将整个栈顶移动;BACK操作将当前指针前移就可以了。
#include<iostream> #include<string> #include<vector> #include<stack> using namespace std; int main() { int cptr = 0, maxsz; string list[200]; string istr; list[0] = "http://www.acm.org/"; while (cin >> istr) { switch (istr[0]) { case 'V': cin >> istr; list[++cptr] = istr; maxsz = cptr; cout << istr << endl; break; case 'B': if (cptr > 0) { cout << list[--cptr] << endl; } else { cptr = 0; cout << "Ignored" << endl; } break; case 'F': if (cptr == maxsz) { cout << "Ignored" << endl; } else { cout << list[++cptr] << endl; } break; case 'Q': return 0; } } return 0; }
相关文章推荐
- 第4章 字符串综合习题(leetcode)
- 第五章 递归综合习题(leetcode)
- 队列相关习题及详解(选择题和综合题) ——数据结构
- 栈和队列-第3章-《数据结构题集》习题解析-严蔚敏吴伟民版
- 栈和队列-第3章-《数据结构题集》习题解析-整理
- 第2章 线性表综合习题(leetcode)
- leetcode习题解答:6. ZigZag Conversion
- LeetCode 225 Implement Stack using Queues(用队列来实现栈)(*)
- 【Leetcode】Symmetric Tree (Tree Judge)
- 算法导论10.1-6习题解答(用两个栈实现一个队列)
- LeetCode 451. Sort Characters By Frequency ***** map按值排序转vector,优先队列,频率当下标
- 【单调队列】leetcode MinStack
- LeetCode 栈和队列的互换
- 数据结构学习笔记 --- 栈、队列 (习题)
- [PeterDLax著泛函分析习题参考解答]第3章 Hahn-Banach 定理
- leetcode 657 Judge Route Circle
- LeetCode习题笔记——Remove Duplicates from Sorted List II
- LeetCode Interview Questions Online Judge
- Leetcode---线性数据结构(栈,链表,队列)的
- leetcode -- 队列总结