GDUT 20 寒假集训专题4.数据结构
- 总结
- 题目题解
1.栈
栈又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
基本调用代码
#include <stack> //头文件
using namespace std;
stack < int > S ; //< >里面是栈存的数据的类型,这样就可以定义一个叫S的存整型的栈
//常用功能
S.size() ; //返回栈的元素数
S.top() ; //返回栈顶的元素
S.pop() ; //从栈中取出并删除元素
S.push(x); //向栈中添加元素x
S.empty(); //在栈为空时返回true
2.队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
基本调用代码
#include <queue> //头文件
using namespace std;
queue < int > Q ; //< >里面是队列存的数据的类型,这样就可以定义一个叫Q的存整型的队列
//常用功能
Q.size() ; //返回队列的元素数
Q.front(); //返回队头的元素
Q.pop() ; //从队列中取出并删除元素
Q.push(x) ;//向队列中添加元素x
Q.empty(); //在队列为空时返回true
3.动态数组
动态数组是指在声明时没有确定数组大小的数组,即忽略圆括号中的下标,使用动态数组的优点是可以根据用户需要,有效利用存储空间。
基本调用代码
#include <vector>
using namespace std;
vector <int > V;
V.size (); //返回向量的元素数
V.push_ back (x) ;//在向量末尾添加元素X
V.Pop_ back(); //删除向量的最后一-个元素
V.begin(); //返回指向向量开头的迭代器
V.end(); //返回指向向量末尾( 最后一个元素的后一个位置)的迭代器
4.链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
常用的代码
#include <list>
using namespace std;
list <int > L;
L.size (); //返回表的元素数
L.begin(); //返回指向表开头的迭代器
L.end() ; //返回指向表末尾(最后一个元素的后一个位置)的迭代器
L.push_ front (x) ; //在表的开头添加元素x
L.push_ back (x) ;//在表的末尾添加元素x
L.pop_ front () ;//删除位于表开头的元素
L.pop_ back () ;//删除位于表末尾的元素
L.insert(p, x) ;//在表的位置p处插入元素x
L.erase (p) ;//删除表中位置p的元素
L.clear () ;//删除表中所有元素
1.括号匹配问题
题目描述: 在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$“标注,不能匹配的右括号用”?"标注.
解题思路:考虑用栈来解决这个问题,遇到左括号就把左括号压栈,遇到右括号就查看栈里面是否有左括号,如果没有,那显然这个右括号是非法的,如果有,那就把栈顶的左括号pop出来。要注意一点就是最后要检查栈里面是否还有左括号,如果整个字符串都遍历完了,但是栈里面还留有一些左括号,那么显然这些左括号也是非法的,所以压栈的时候要记录一下压进去的左括号的位置,或者直接把位置压进去应该也是可行的,(但是我没这么做qwq)。
ac代码
#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <stack>
using namespace std;
struct node {
char ch;
int s;
}N;
int main()
{
string a;
char ans[105];
stack <node > S;
while(cin>>a){
for(int i=0;i<=104;i++){
ans[i]=' ';
}
for(int i=0;i<a.size() ;i++){
if(a[i]=='('){
N.ch='(';
N.s=i;
S.push(N);
}
else if(a[i]==')'){
if(S.empty()){
ans[i]='?';
}
else
S.pop() ;
}
}
while(!S.empty()){
N=S.top() ;
S.pop() ;
ans[N.s]='$';
}
cout<<a<<'\n';
for(int i=0;i<a.size() ;i++){
cout<<ans[i];
}
cout<<'\n';
}
return 0;
}
2.Smallest Substring
题目描述:
给定一个字符串S和一个整数K,你的任务是找到满足以下条件的字典上最小的字符串T:
-
T是S的子序列
-
T的长度是K。
解题思路:这里我是用链表做的,题目其实很明确,找最小字典序嘛…我的理解是,可以转换成找区间的最小值,大概就是,前n个数中找一个最小值,那第n个位置的最小字典序就是那个最小值(画图举例脑补一下下qwq),但是有一个问题,如果我这样做的话,那复杂度就是O(n²),那显然会超时,所以我用了其他方法,就是先用链表把n个数存下来,然后从第一个开始遍历,如果一个数比它后面那个数小的话,那就把这个数删了, 这里就发挥了链表的优越性了,如果用数组来存这些数,删起来会比较麻烦。一直删,直到n等于k为止那么留下来的k个数就是满足题意得最小字典序啦。
ac代码
#include <iostream>
#include <cmath>
#include <stdlib.h>
#include <stack>
#include <queue>
#include <list>
using namespace std;
int main()
{
list <char > L;
int k,n;
string a;
cin>>k;
cin>>a;
n=a.size() ;
for(int i=0;i<n;i++){
L.push_back(a[i]);
}
list <char >::iterator itn=L.begin();
list <char >::iterator it=L.begin();
while(n>k){
it++;
if(it==L.end()){
break;
}
if(*itn>*it){
L.erase(itn);
if(it!=L.begin()){
it--;
}
n--;
}
itn=it;
}
it=L.begin();
int i=0;
for(it;i<k;i++,it++){
cout<<*it;
}
return 0;
}