7.1 栈
7.2 队列
栈和队列最经典的题目就是下面两个,中缀转前缀表达式实现计算器的功能,第二个是判断表达式是否正确;
第一个上次囫囵的看了一遍,其实也没咋懂
⭐⭐⭐⭐问题 A: 简单计算器
问题描述:读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。
- 输入
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。
- 输出
在这里插入代码片对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。```
- 样例输入
```cpp
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
- 样例输出
12178.21
参考网址:https://www.cnblogs.com/zhengxunjie/p/10372329.html
⭐⭐⭐⭐问题 B: Problem E
问题描述:请写一个程序,判断给定表达式中的括号是否匹配,表达式中的合法括号为”(“, “)”, “[", "]“, “{“, ”}”,这三个括号可以按照任意的次序嵌套使用。
- 输入
有多个表达式,输入数据的第一行是表达式的数目,每个表达式占一行。
- 输出
对每个表达式,若其中的括号是匹配的,则输出”yes”,否则输出”no”。
- 样例输入
4
[(d+f)*{}]
[(2+3))
()}
[4(6]7)9
- 样例输出
yes
no
no
no
这里是直接计算中缀表达式的方法
#include<iostream>
#include<string>
#include<stack>
using namespace std;
int getPriority(char ch)
{
//获取优先级
if (ch == '(') return 1;
else if (ch == '+' || ch == '-') return 2;
else if (ch == '*' || ch == '/') return 3;
else return 4;
}
void calculate(stack<double>& mystack, char operation)
{
double num1, num2, num3;
num2 = mystack.top();
mystack.pop();
num1 = mystack.top();
mystack.pop();
if (operation == '+') {
num3 = num1 + num2;
}
else if (operation == '-') {
num3 = num1 - num2;
}
else if (operation == '*') {
num3 = num1 * num2;
}
else if (operation == '/') {
num3 = num1 / num2;
}
mystack.push(num3);
}
double calculator(string str)
{
//计算中缀表达式,默认输入是合法的
stack<double> mystack_number;
stack<char> mystack_operation;
int i = 0, j;
int size = str.size();
char tmp_operation;
string tmp_num;
while (i < size) {
if (str[i] >= '0' && str[i] <= '9') {
j = i;
while (j < size && str[j] >= '0' && str[j] <= '9') { j++; }
tmp_num = str.substr(i, j - i);
mystack_number.push(atoi(tmp_num.c_str()));
i = j;
}
else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {
if (mystack_operation.empty()) {
mystack_operation.push(str[i]);
}
else {
while (!mystack_operation.empty()) {
tmp_operation = mystack_operation.top();
if (getPriority(tmp_operation) >= getPriority(str[i])) {
//计算
calculate(mystack_number, tmp_operation);
mystack_operation.pop();
}
else break;
}
mystack_operation.push(str[i]);
}
i++;
}
else {
if (str[i] == '(') mystack_operation.push(str[i]);
else {
while (mystack_operation.top() != '(') {
tmp_operation = mystack_operation.top();
//计算
calculate(mystack_number, tmp_operation);
mystack_operation.pop();
}
mystack_operation.pop();
}
i++;
}
}
//遍历完后,若栈非空,弹出所有元素
while (!mystack_operation.empty()) {
tmp_operation = mystack_operation.top();
//计算
calculate(mystack_number, tmp_operation);
mystack_operation.pop();
}
return mystack_number.top();
}
int main()
{
string str;
while (getline(cin, str), str != "0")
{
for (string::iterator it = str.begin(); it != str.end(); it++)
{
if (*it == ' ') str.erase(it);
}
//cout << "中缀表达式为:" << endl << str << endl;
double num_res = calculator(str);
printf("%.2f\n",num_res);
}
return 0;
}
这份代码是错的,不知道原因:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <cstdio>
#include <string>
#include <stack>
#include<queue>
#include<map>
using namespace std;
struct node {
double num;
char op;
int flag;//flag=0表示操作符,=1表示操作数
};
string str;
stack<node> s;
queue<node> q;
map<char, int> op;
//生成后缀表达式,默认输入的中缀表达式是合法的
void Change()
{
double num;
node temp;
for (int i = 0; i < str.length();)
{
if (str[i] >= '0' && str[i] <= '9')
{
temp.flag = 1;
temp.num = str[i++] - '0';
//printf("%c-%d\n",str[i],temp.num);
while (i < str.length() && str[i] >= '0' && str[i] <= '9')
{
temp.num = temp.num * 10 + (str[i] - '0');
i++;
}
q.push(temp);//将数字直接放进队列中
}
else
{
temp.flag = 0;
while (!s.empty() && op[str[i]] <= op[s.top().op])//比较操作符的优先级
{
q.push(s.top());//不断把栈顶的操作符弹出到队列中
// printf("%c",s.top().op);
s.pop();
}
temp.op = str[i];
s.push(temp);
i++;
}
while (!s.empty())//如果还有操作符,全都放到队列中
{
q.push(s.top());
s.pop();
}
}
}
double Cal()//计算后缀表达式
{
double temp1, temp2;
node cur, temp;
while (!q.empty())
{
cur = q.front();
q.pop();
if (cur.flag == 1) s.push(cur);
else
{
temp2 = s.top().num;//弹出第二操作数
s.pop();
temp1 = s.top().num;
s.pop();//弹出第一操作数
temp.flag = 1;
if (cur.op == '+') temp.num = temp1 + temp2;
else if (cur.op == '-') temp.num = temp1 - temp2;
else if (cur.op == '*') temp.num = temp1 * temp2;
else temp.num = temp1 / temp2;
s.push(temp);
}
}
return s.top().num;
}
int main()
{
op['+'] = op['-'] = 1;
op['*']=op['/']=2;
while (getline(cin,str),str!="0")
{
for (string::iterator it=str.begin(); it != str.end(); it++)
{
if (*it == ' ') str.erase(it);
}
while (!s.empty()) s.pop();//初始化栈s
// cout << str;
// printf("\n");
Change();
printf("%.2f\n",Cal());
}
return 0;
}
7.3 链表
怎样判断何时使用顺序表何时使用链表呢?就要看它们的特点了。顺序表的特点是随机存取、随机访问,也就是说如果存取和查询比较频繁的话使用顺序表比较合适;链表的特点是插入和删除时不必移动其后的节点,如果插入和删除操作比较频繁的话使用链表比较合适。
链表的基本操作
1.创建链表
2.查找元素
3.删除元素
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
struct node {
int data;
node* next;
};
//创建链表
node* create(int Array[])
{
node* p, * pre, * head;
head = new node;
head->next = NULL;
pre = head;
for (int i = 0; i < 5; i++)
{
p = new node;
p->data = Array[i];
p->next = NULL;
pre->next = p;
pre = p;
}
return head;
}
int search(node* head, int x)
{
int count = 0;
node* p = head->next;
while (p != NULL)
{
if (p->data == x)
{
count++;
}
p = p->next;
}
return count;
}
void del(node* head, int x)
{
node* p = head->next;
node* pre = head;
while (p != NULL)
{
if (p->data == x)
{
pre->next = p->next;
delete(p);
p = pre->next;
}
else
{
pre = p;
p = p->next;
}
}
}
int main()
{
int Array[5] = { 5,4,3,2,1 };
node* head = create(Array);
node* temp;
temp = head;
temp = temp->next;
while(temp !=NULL){
printf("%d ", temp->data);
temp = temp->next;
}
//下面删除3
temp = head;
del(temp,3);
temp = head;
while (temp != NULL) {
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
静态链表
一般使用结构体数组存放静态链表
PAT A1032 Sharing (25 分)
问题描述:
- 输入
每个输入文件包含一个测试用例。对于每种情况,第一行包含两个节点地址和一个正N (≤10
5
),其中两个地址是两个字的第一个节点的地址,并且 N是节点总数。节点地址为5位正整数,NULL表示为−1.
然后 N 下面几行,每行描述一个节点的格式:
Address Data Next
其中Address是节点的位置,Data是该节点包含的字母是从{ az, AZ } 中选择的一个英文字母,Next是下一个节点的位置。
- 输出
对于每种情况,只需输出公共后缀的 5 位起始位置。如果这两个词没有共同的后缀,则-1改为输出。
- 样例输入
11111 22222 9
67890 i 00002
00010 a 12345
00003 g -1
12345 D 67890
00002 n 00003
22222 B 23456
11111 L 00001
23456 e 67890
00001 o 00010
- 样例输出
67890
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
const int maxn = 100010;
struct Node {
char data;//存放的字母
int next;//下一个节点的地址
int flag;
}node[maxn];
int main()
{
int i;
for (i = 0; i < maxn; i++)
{
node[i].flag = 0;
}//所有节点的flag初始化为0,表示没有出现过
int s1, s2, n;
scanf("%d %d %d", &s1, &s2, &n);
int address, next;
char data;
for (i = 0; i < n; i++)
{
scanf("%d %c %d",&address,&data,&next);
node[address].data = data;
node[address].next = next;
}
int p ;
for (p = s1; p != -1; p = node[p].next)
{
node[p].flag = 1;
}
for (p = s2; p != -1; p = node[p].next)
{
if (node[p].flag == 1)
break;
}
if (p != -1)
printf("%05d\n", p);
else
printf("-1\n");
return 0;
}
PAT A1052链表排序(25分)
问题描述:链表由一系列结构组成,这些结构在内存中不一定相邻。我们假设每个结构都包含一个整数key和一个Next指向下一个结构的指针。现在给定一个链表,您应该根据它们的键值按递增顺序对结构进行排序。
- 输入
每个输入文件包含一个测试用例。对于每种情况,第一行包含一个正数N (<10
5
) 和头节点的地址,其中 N是内存中节点的总数,节点的地址是一个 5 位正整数。NULL 表示为−1.
然后 N 下面几行,每行描述一个节点的格式:
Address Key Next
其中Address是节点在内存中的地址,Key是[中的整数−10 5 ,10 5 ],Next是下一个节点的地址。保证所有的key都是不同的,从头节点开始的链表中没有循环。
- 输出
对于每个测试用例,输出格式与输入格式相同,其中 N 是列表中的节点总数,所有节点必须排序。
- 样例输入
5 00001
11111 100 -1
00001 0 22222
33333 100000 11111
12345 -1 33333
22222 1000 12345
- 样例输出
5 12345
12345 -1 00001
00001 0 11111
11111 100 22222
22222 1000 33333
33333 100000 -1
这题说是静态链表,其实就是结构体的排序啦,注意区别
1.判断节点在不在链表里面,有可能题目给的节点不在链表里面
2.在排好序之后,链表内每个节点的后继节点还没有改掉,这时候可以省事的直接输出node[i+1].address,也可以改node[i].next为node[i+1].address
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct Node {
int next;//下一个节点的地址
int key;//键值
int flag;//=0表示不在链表里面,=1表示在链表里面
int address;//本节点的地址
}node[maxn];
bool cmp(Node a,Node b){
if (a.flag == 0 || b.flag == 0)
return a.flag > b.flag;
else
return a.key < b.key;
}
int main()
{
int s1, n;
int i;
int address, key, next;
scanf("%d %d",&n,&s1);
for (i = 0; i < maxn; i++)
{
node[i].flag = 0;//表示都不在链表里面
}
for (i = 0; i < n; i++)
{
scanf("%d %d %d", &address, &key, &next);
node[address].key = key;
node[address].next = next;
node[address].address = address;
}
int p = s1;
int count = 0;
while (p != -1)
{
node[p].flag = 1;
count++;
p = node[p].next;
}
if (count == 0)
printf("0 1");
else
{
sort(node, node + maxn, cmp);
printf("%d %05d\n", count, node[0].address);
for (i = 0; i < count-1; i++)
{
node[i].next = node[i + 1].address;
}
node[count - 1].next = -1;
for (i = 0; i < count-1; i++)
{
printf("%05d %d %05d\n", node[i].address, node[i].key, node[i].next);
}
printf("%05d %d %d\n", node[count-1].address, node[count-1].key, node[count-1].next);
}
return 0;
}
问题 A: 算法2-8~2-11:链表的基本操作
大量的查找与删除操作需要使用链表,比较节省时间。
问题描述:
- 输入
输入数据只有一组,第一行有n+1个整数,第一个整数是这行余下的整数数目n,后面是n个整数。这一行整数是用来初始化列表的,并且输入的顺序与列表中的顺序相反,也就是说如果列表中是1、2、3那么输入的顺序是3、2、1。
第二行有一个整数m,代表下面还有m行。每行有一个字符串,字符串是“get”,“insert”,“delete”,“show”中的一种。如果是“get”或者“delete”,则其后跟着一个整数a,代表获得或者删除第a个元素;如果是“insert”,则其后跟着两个整数a和e,代表在第a个位置前面插入e;“show”之后没有整数。
- 输出
如果获取成功,则输出该元素;如果删除成功则输出“delete OK”;如果获取失败或者删除失败,则输出“get fail”以及“delete fail”。如果插入成功则输出“insert OK”,否则输出“insert fail”。如果是“show”则输出列表中的所有元素,如果列表是空的,则输出“Link list is empty”。注:所有的双引号均不输出。
- 样例输入
3 3 2 1
21
show
delete 1
show
delete 2
show
delete 1
show
delete 2
insert 2 5
show
insert 1 5
show
insert 1 7
show
insert 2 5
show
insert 3 6
show
insert 1 8
show
get 2
- 样例输出
1 2 3
delete OK
2 3
delete OK
2
delete OK
Link list is empty
delete fail
insert fail
Link list is empty
insert OK
5
insert OK
7 5
insert OK
7 5 5
insert OK
7 5 6 5
insert OK
8 7 5 6 5
7
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct node {
int data;
node* next;
};
//创建链表
node * create(int n)
{
node* p;
int i;
node* head;
head = new node;
head->next = NULL;
for (i = n; i > 0; i--)
{
p = new node;
scanf("%d",&p->data);
//printf("%d\n",p->data);
p->next = head -> next;
head->next = p;
}
return head;
}
void show(node* head, int num)
{
if (num == 0)
{
printf("Link list is empty\n");
}
else
{
node* p;
p = head->next;
for (int i = 0; i < num - 1; i++)
{
printf("%d ", p->data);
p = p->next;
}
printf("%d\n", p->data);
}
return;
}
void insert(node* head, int a,int e,int num)
{
if (a > num + 1)
{
printf("insert fail\n");
}
else
{
node* p = head->next;
node* pre=head;
for (int i = 0; i < a - 1; i++)
{
pre = p;
p = p->next;
}
node* q;
q = new node;
q->data = e;
q->next = p;
pre->next = q;
printf("insert OK\n");
}
return;
}
void get(node* head, int x,int num)
{
if (x > num)
{
printf("get fail\n");
}
else
{
node* p = head->next;
for (int i = 0; i < x - 1; i++)
{
p = p->next;
}
printf("%d\n",p->data);
}
return;
}
void del(node* head, int x,int num)//删除第x号元素
{
if(x>num)
{
printf("delete fail\n");
return;
}
node* p = head->next;
node* pre = head;
for (int i = 0; i < x-1; i++)
{
pre = p;
p = p->next;
}
pre->next = p->next;
delete(p);
printf("delete OK\n");
return;
}
int main()
{
int n, m;
scanf("%d", &n);
int i,j;
char s[10];
node* head;
int num;
int temp;
int temp2;
head=create(n);//创建链表
num = n;
scanf("%d", &m);
for (i = 0; i < m; i++)
{
scanf("%s",s);
if (strcmp(s,"show")==0)
{
//printf("!!!");
show(head,num);
}
else if (strcmp(s, "delete") == 0)
{
scanf("%d", &temp);
del(head, temp,num);
num--;
}
else if (strcmp(s, "get") == 0)
{
scanf("%d", &temp);
get(head, temp,num);
}
else if (strcmp(s, "insert") == 0)
{
scanf("%d %d", &temp,&temp2);
insert(head, temp, temp2,num);
num++;
}
}
return 0;
}
问题 B: C语言-链表排序
问题描述:已有a、b两个链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,按学号升序排列。
- 输入
第一行,a、b两个链表元素的数量N、M,用空格隔开。 接下来N行是a的数据 然后M行是b的数据 每行数据由学号和成绩两部分组成
- 输出
按照学号升序排列的数据
- 样例输入
2 3
5 100
6 89
3 82
4 95
2 10
- 样例输出
2 10
3 82
4 95
5 100
6 89
静态链表+结构体排序
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct student {
int id;
int scores;
int flag;
}stu[maxn];
bool cmp(student a, student b)
{
if (a.flag == 0 || b.flag == 0)
return a.flag > b.flag;
else
return a.id < b.id;
}
int main()
{
int n, m;
scanf("%d %d", &n,&m);
int sum = m + n;
int i;
int id, scores;
for (i = 0; i < maxn; i++)
{
stu[i].flag = 0;
}
for (i = 0; i < sum; i++)
{
scanf("%d %d",&id,&scores);
stu[id].id = id;
stu[id].scores = scores;
stu[id].flag = 1;
}
sort(stu, stu + maxn, cmp);
for (i = 0; i < sum; i++)
{
printf("%d %d\n",stu[i].id,stu[i].scores);
}
return 0;
}
问题 C: 最快合并链表(线性表)
问题描述:知L1、L2分别为两循环单链表的头结点指针,m,n分别为L1、L2表中数据结点个数。要求设计一算法,用最快速度将两表合并成一个带头结点的循环单链表。
- 输入
m=5
3 6 1 3 5
n=4.
7 10 8 4
- 输出
3 6 1 3 5 7 10 8 4
- 样例输入
7
3 5 1 3 4 6 0
5
5 4 8 9 5
- 样例输出
3 5 1 3 4 6 0 5 4 8 9 5
偷懒写法(逃
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
int main()
{
int num;
int i, n;
while (scanf("%d", &n) != EOF)
{
for (i = 0; i < n; i++)
{
scanf("%d",&num);
printf("%d ",num);
}
//printf("!!!");
}
return 0;
}
问题 D: 链表查找(线性表)
问题描述:线性表(a1,a2,a3,…,an)中元素递增有序且按顺序存储于计算机内。要求设计一算法完成:
(1) 用最少时间在表中查找数值为x的元素。
(2) 若找到将其与后继元素位置相交换。
(3) 若找不到将其插入表中并使表中元素仍递增有序。
- 输入
输入:x=3
输入长度:9
输入数据:2 3 5 7 12 15 17 23 45
- 输出
相同元素为:3
交换后的链表为:2 5 3 7 12 15 17 23 45
- 样例输入
4
9
2 3 5 7 12 15 17 23 45
- 样例输出
no
2 3 4 5 7 12 15 17 23 45
使用set,自动有序,如果找到了该元素,就先输出后面的元素,再输出该元素
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<set>
using namespace std;
int main()
{
int num;
int n;
int temp,temp1;
int i;
set<int> st;
scanf("%d%d",&num,&n);//计算得到Num和n
for (i = 0; i < n; i++)
{
scanf("%d",&temp);
st.insert(temp);
}
set<int>::iterator it = st.find(num);
if (it == st.end())//如果没有找到该元素
{
printf("no\n");
st.insert(num);
for (it = st.begin(); it != st.end(); it++)
{
printf("%d ",*it);
}
}
else//如果找到了该元素
{
//printf();
for (set<int>::iterator it1 = st.begin(); it1 != st.end(); it1++)
{
if (it1 == it)
{
it1++;
printf("%d ", *it1);
printf("%d ", *it);
}
else
{
printf("%d ", *it1);
}
}
}
return 0;
}
问题 E: 算法2-24 单链表反转
问题描述:根据一个整数序列构造一个单链表,然后将其反转。
例如:原单链表为 2 3 4 5 ,反转之后为5 4 3 2
- 输入
输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开
- 输出
针对每组测试数据,输出包括两行,分别是反转前和反转后的链表元素,用空格隔开
如果链表为空,则只输出一行,list is empty
- 样例输入
5 1 2 3 4 5
0
- 样例输出
1 2 3 4 5
5 4 3 2 1
list is empty
基础题,我对链表的信心猛涨(一定是错觉)
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<set>
using namespace std;
const int maxn = 100010;
int a[maxn];
int main()
{
int i;
int n;
while (scanf("%d", &n) != EOF)
{
if (n == 0)
printf("list is empty\n");
else
{
for (i = 0; i < n; i++)
{
scanf("%d",&a[i]);
printf("%d ",a[i]);
}
printf("\n");
for (i = n-1; i >=0; i--)
{
printf("%d ", a[i]);
}
printf("\n");
}
}
return 0;
}
问题 F: 算法2-25 有序单链表删除重复元素
问题描述:根据一个递增的整数序列构造有序单链表,删除其中的重复元素
- 输入
输入包括多组测试数据,每组测试数据占一行,第一个为大于等于0的整数n,表示该单链表的长度,后面跟着n个整数,表示链表的每一个元素。整数之间用空格隔开
- 输出
针对每组测试数据,输出包括两行,分别是删除前和删除后的链表元素,用空格隔开
如果链表为空,则只输出一行,list is empty
- 样例输入
5 1 2 3 4 5
5 1 1 2 2 3
0
- 样例输出
1 2 3 4 5
1 2 3 4 5
1 1 2 2 3
1 2 3
list is empty
基础题,同样用set保证序列递增且去重
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<set>
using namespace std;
int main()
{
int i;
int n;
int num;
while (scanf("%d", &n) != EOF)
{
if (n == 0)
printf("list is empty\n");
else
{
set<int> st;
for (i = 0; i < n; i++)
{
scanf("%d",&num);
printf("%d ",num);
st.insert(num);
}
printf("\n");
for (set<int>::iterator it = st.begin(); it != st.end(); it++)
{
printf("%d ", *it);
}
printf("\n");
}
}
return 0;
}
★,°:.☆( ̄▽ ̄)/$:.°★ 第七章完结撒花花!