题记
我看了很多资料,迫切寻找一个只有我自己知道的答案。——“请问八月开始考研是否来得及?”
在不知道试题结构,不知道考察重点,不知道自己相对基础和水平地准备考研就好比在暗夜的大海上行船。这是每个考研人共同的困境。
这不是成功后的经验帖,只是每天工作内容的blog,用以劝勉自己。
7.31
我发现严蔚敏教材在栈和队列一章的习题非常喜欢考后缀算式,不管是转化还是计算,之前做的很少,在这里记录一下。
后缀式算式计算
总体思路:
先判断输入是操作符还是操作数:
如果是操作数,则压栈
如果是操作符,则出栈两个元素,根据操作符进行计算之后再压栈
代码实现:
代码实现用C++还是可以的,我不熟悉C语言,直接放标准答案
/*
没想到C语言没有定义栈,只能自己写数据结构
在伪代码里用了initstack()函数假装声明栈,算了,要跑起来写C++吧,靠放弃了,
能看就行
*/
double postfix() {
ininstack(OPND);
double num=0.0;
char ch=getchar();
while(ch!='$') {
int i=0;
char data[50];
while(ch>='0'&&ch<='9'||ch=='.')
{//拼数,将读入的数字或小数点依次保存在字符数组
data[i]=ch;
i++;
ch=getchar();
}
double num=atof(data); //字符串转换为浮点数
OPND.push(num);
switch(ch) {
case' ':break;
case'+': pop(OPND,b);pop(OPND,a);push(OPND,a+b);break;
case'-': pop(OPND,b);pop(OPND,a);push(OPND,a-b);break;
case'*': pop(OPND,b);pop(OPND,a);push(OPND,a*b);break;
case'/': pop(OPND,b);pop(OPND,a);push(OPND,a/b);break;
}
ch=getchar();
}
return gettop(OPND);
}
数制转化
严教材在栈方面给出的应用实例很多:
1.数制转化 √此处解决这个问题
2.括号匹配
3.表达式求值(中缀)
4.舞伴问题
教材给出的写法很优美
stack<int> S;
void conversion(int N) {
while(N) {
S.push(N%8);
N/=8;
}
while(!S.empty()) {
int num=S.top();
cout<<num;
S.pop();
}
}
中缀式转化后缀式(编译原理突然开始攻击我?)
严教材在栈方面给出的应用实例很多:
1.数制转化
2.括号匹配
3.表达式求值(中缀) √此处解决这个问题
4.舞伴问题
来源:ChanJose的博客 原文链接:https://blog.youkuaiyun.com/chuanzhouxiao/article/details/85729975
1.用字符数组存储整行输入的中缀表达式;
2.接着从字符数组的0位置开始判断字符,如果是数字,那就要判断后面是否是数字,如果是就不断扫描组成一个整数(暂不考虑负数和小数),最终组成一个整数,然后输出这个数(因为不用计算,所以直接输出即可);
3.如果是左括号,直接进符号栈;
4.如果是操作运算符,与符号栈的栈顶元素比较优先级:如果高就压入栈;低,就取出符号栈顶的元素输出;接着,再判断符号栈顶的元素和当前的运算符号继续比较优先级,重复前面步骤,直到栈空或者当前的符号优先级高;
5.如果是右括号,把符号栈栈顶的元素取出,如果不是左括号,把取出的运算符输出,接着取符号栈栈顶的元素,直到符号栈中取出的符号是左括号;
6.当扫描完字符数组时,判断符号栈是否为空:不为空,把符号栈栈顶的元素取出,输出到窗口,直到符号栈为空。
代码实现:
// 中缀表达式转后缀表达式
// 操作符:+、-、*、/、%
// 输入:可以用cin.getline(arr, 250)或者cin.get(ch) && ch != '\n'
// 测试数据:输入格式:(注意:不能有中文的操作符)
// 2+(3+4)*5
// 16+2*30/4
// 输出格式:
// 2 3 4 + 5 * +
// 16 2 30 * 4 / +
#include<bits/stdc++.h>
using namespace std;
// 判断是否是操作符
bool isOperator(char ch) {
if(ch == '+' || ch == '-' || ch == '*' || ch == '/')
return true;
return false; // 否则返回false
}
// 获取优先级
int getPriority(char ch) {
int level = 0; // 优先级
switch(ch) {
case '(':
level = 1;
break;
case '+':
case '-':
level = 2;
break;
case '*':
case '/':
level = 3;
break;
default:
break;
}
return level;
}
int main(int argc, const char * argv[]) {
// insert code here...
int num;
char arr[250]; // 一个一个的读取表达式,直到遇到'\0'
std::stack<char> op; // 栈op:存储操作符
while(1) {
std::cin.getline(arr,250);
int len, i;
char c; // c存储从栈中取出的操作符
len = (int)strlen(arr); // strlen()输出的是:unsigned long类型,所以要强制转换为int类型
i = 0;
while(i < len) {
if(isdigit(arr[i])) { // 如果是数字
num = 0;
do {
num = num * 10 + (arr[i] - '0'); // ch - 48根据ASCAII码,字符与数字之间的转换关系
i++; // 下一个字符
}while(isdigit(arr[i]));
std::cout << num << " ";
} else if(arr[i] == '(') { // (:左括号
op.push(arr[i]);
i++;
} else if(isOperator(arr[i])) { // 操作符
if(op.empty()) {// 如果栈空,直接压入栈
op.push(arr[i]);
i++;
}
else {
// 比较栈op顶的操作符与ch的优先级
// 如果ch的优先级高,则直接压入栈
// 否则,推出栈中的操作符,直到操作符小于ch的优先级,或者遇到(,或者栈已空
while(!op.empty()) {
c = op.top();
if(getPriority(arr[i]) <= getPriority(c)) {
// 优先级低或等于
std::cout << c << " ";
op.pop();
} else // ch优先级高于栈中操作符
break;
} // while结束
op.push(arr[i]); // 防止不断的推出操作符,最后空栈了;或者ch优先级高了
i++;
} // else
} else if(arr[i] == ')') { // 如果是右括号,一直推出栈中操作符,直到遇到左括号(
while(op.top() != '(') {
std::cout << op.top() << " ";
op.pop();
}
op.pop(); // 把左括号(推出栈
i++;
} else // 如果是空白符,就进行下一个字符的处理
i++;
} // 第二个while结束
while(!op.empty()) { // 当栈不空,继续输出操作符
std::cout << op.top() << " ";
op.pop();
}
std::cout << std::endl;
flush(std::cout);
} // 第一个while结束
return 0;
}
大佬强啊orz
补充一下,教材原本给了一张算符优先的运算表,依据表中结果确定当前是否进行运算。与本题处理方式大同小异
8.1
害,我真是服了这个老六教材配套练习了,整的我好难受。这个代码为什么这么写,看着好silly
不知道让用C++还是C语言,差不太多,我用c++吧,反正说是伪代码,c++或许可以作为c语言的伪代码?(bushi)
第三章第五题(2)
/*我的答案*/
bool islegal(char str[],int length){//传入字符数组和长度
int num=0;//记录栈中元素个数
for(int i=0;i<length;i++) {
if(str[i]=='I') num++;
else num--;
if(num<0) return false;
}
if(num>0) return false;//终态不为0
return true;//剩下情况都对
}
/*配套练习写得很拉,我不想抄*/
3.6 贴上来的原因:读完不知道题目想干嘛,怎么判别一个结点是不是头结点,要是没法判别不就完全没办法搞了
大概看了眼代码,嗯,链表的长度是可以变化的,我悟了
/*
初始一个头节点,此时尾指针指向这个结点
*/
//定义结点结构
typedef struct my_node{
Qelemtype data;
struct my_node *next;
}my_node,*my_queueptr;//我不理解这个参数表的意思
//定义链表结构
typedef struct{
my_queueptr rear;
}linkqueue;
//清空 QwQ呜呜呜,我要C++,我不要C语言
void initqueue(linkqueue *Q) {
Q->next=Q->rear->next;/*不是只有一个尾指针吗,找到头
(这里我不理解为啥不新搞个指针,不怕队尾找不到了吗)*/
while(Q->next!=Q->rear->next) {
struct my_node* s=Q->rear->next;
Q->rear->next=s->next;
delete(s);
}
}
//判断队列是否为空
bool emptyqueuq(linkqueue *Q) {//重申,严蔚敏教材配套习题质量堪忧
return Q->rear->next->next->next==Q->rear->next;//表示头块的后继结点是头块本身
}
//插入
status enqueue(linkqueue *Q,qelemtype e) {
struct my_node p=new queuenode;
p->data=e;
p->next=Q->rear->next;
Q->rear->next=p;
Q->rear=p;
return true;
}
//出队
bool queue(linkqueue *Q,elemtype &e) {//传入队列指针,传入目标容器
if(Q->rear->next==Q->rear->next->next) return false;//队空
struct my_node *p=Q->rear->next->next; //p在队首,第一个元素
e=p->data;
if(p==Q->rear) { //尾指针指向的元素是第一个元素
Q->rear=Q->rear->next;//尾指针挪到头块
Q->rear->next=Q->rear;//头块后继指向头块本身
}
Q->rear->next->next=p->next; //头块指向的第一个元素为p的后继元素
delete(p);
return true;
}
KMP算法
今天看到一位大佬的文章,讲KMP算法比书上清晰明了。放出链接和代码。
http://t.csdn.cn/SFmtX
//next数组求法
typedef struct
{
char data[MaxSize];
int length; //串长
} SqString;
//SqString 是串的数据结构
//typedef重命名结构体变量,可以用SqString t定义一个结构体。
void GetNext(SqString t,int next[]) //由模式串t求出next值
{
int j,k;
j=0;k=-1;
next[0]=-1;//第一个字符前无字符串,给值-1
while (j<t.length-1)
//因为next数组中j最大为t.length-1,而每一步next数组赋值都是在j++之后
//所以最后一次经过while循环时j为t.length-2
{
if (k==-1 || t.data[j]==t.data[k]) //k为-1或比较的字符相等时
{
j++;k++;
next[j]=k;
//对应字符匹配情况下,s与t指向同步后移
//通过字符串"aaaaab"求next数组过程想一下这一步的意义
//printf("(1) j=%d,k=%d,next[%d]=%d\n",j,k,j,k);
}
else
{
k=next[k];
**//我们现在知道next[k]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
//也表示该处字符不匹配时应该回溯到的字符的下标
//这个值给k后又进行while循环判断,此时t.data[k]即指最长相等前缀后一个字符**
//为什么要回退此处进行比较,我们往下接着看。其实原理和上面介绍的KMP原理差不多
//printf("(2) k=%d\n",k);
}
}
}
//next数组对应的KMP算法
int KMPIndex(SqString s,SqString t) //KMP算法
{
int next[MaxSize],i=0,j=0;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if (j==-1 || s.data[i]==t.data[j])
{
i++;j++; //i,j各增1
}
else j=next[j]; //i不变,j后退,现在知道为什么这样让子串回退了吧
}
if (j>=t.length)
return(i-t.length); //返回匹配模式串的首字符下标
else
return(-1); //返回不匹配标志
}
//nextval 修订数组求法
void GetNextval(SqString t,int nextval[])
//由模式串t求出nextval值
{
int j=0,k=-1;
nextval[0]=-1;
while (j<t.length)
{
if (k==-1 || t.data[j]==t.data[k])
{
j++;k++;
if (t.data[j]!=t.data[k])
//这里的t.data[k]是t.data[j]处字符不匹配而会回溯到的字符
//为什么?因为没有这处if判断的话,此处代码是next[j]=k;
//next[j]不就是t.data[j]不匹配时应该回溯到的字符位置嘛
nextval[j]=k;
else
nextval[j]=nextval[k];
//这一个代码含义是不是呼之欲出了?
//此时nextval[j]的值就是就是t.data[j]不匹配时应该回溯到的字符的nextval值
//用较为粗鄙语言表诉:即字符不匹配时回溯两层后对应的字符下标
}
else k=nextval[k];
}
}
//修订后的KMP算法
int KMPIndex1(SqString s,SqString t)
//修正的KMP算法
//只是next换成了nextval
{
int nextval[MaxSize],i=0,j=0;
GetNextval(t,nextval);
while (i<s.length && j<t.length)
{
if (j==-1 || s.data[i]==t.data[j])
{
i++;j++;
}
else j=nextval[j];
}
if (j>=t.length)
return(i-t.length);
else
return(-1);
}