考研复习实况追踪blog

题记

我看了很多资料,迫切寻找一个只有我自己知道的答案。——“请问八月开始考研是否来得及?”
在不知道试题结构,不知道考察重点,不知道自己相对基础和水平地准备考研就好比在暗夜的大海上行船。这是每个考研人共同的困境。
这不是成功后的经验帖,只是每天工作内容的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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值