《数据结构(C语言版)第二版》第三章-栈和队列(3.6)

3.6 案例分析与实现

3.6.1 数制的转换

//算法3.20 数制的转换
//利用顺序栈

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

typedef int Status;

typedef struct
{
	int* base;
	int* top;
	int stacksize;
}SqStack;

#define STACK_INIT_SIZE 2
#define STACKINCREMENT 3
#define OK 1
#define NO 0
#define ERROR -1
#define OVERFLOW -2

void initstack(SqStack& S);
void Push(SqStack& S, int e);
Status stackempty(SqStack& S);
int pop(SqStack& S);

int main()
{
	//将十进制N转换为与其等值的八进制
	SqStack M = { NULL,NULL,0 };
	int N = 1348;

	initstack(M);

	while (N)
	{
		Push(M, N % 8);
		N = N / 8;
	}

	while (!stackempty(M))
	{
		printf("%d ", pop(M));
	}

	return 0;
}


void initstack(SqStack& S)
{
	S.base = (int*)malloc(sizeof(int) * STACK_INIT_SIZE);
	if (!S.base)
	{
		printf("初始化时,内存分配失败。");
		exit(OVERFLOW);
	}
	
	S.top = S.base;
	S.stacksize = STACK_INIT_SIZE;
	printf("初始化顺序栈成功。\n");
}

void Push(SqStack& S,int e)
{
	int* newbase = NULL;

	if (!S.base)
	{
		printf("将元素压入时,顺序栈不存在。\n");
		return;
	}

	if ((S.top-S.base) == S.stacksize)
	{
		printf("将元素压入时,顺序栈已满。将扩展内存空间。\n");

		newbase = (int*)realloc(S.base, sizeof(int) * (S.stacksize + STACKINCREMENT));
		
		if (!newbase)
		{
			printf("扩展内存空间时,内存分配失败。\n");
			exit(OVERFLOW);
		}

		S.base = newbase;
		S.top = S.base + S.stacksize;
		S.stacksize = S.stacksize + STACKINCREMENT;
		printf("扩展内存空间成功。\n");
	}

	*S.top = e;
	S.top++;
	printf("已将元素%d成功压入顺序栈中。\n", e);
}

Status stackempty(SqStack &S)
{
	if (!S.base)
	{
		printf("判断是否为空时,顺序栈不存在。\n");
		return ERROR;  //负数在IF、while等判断语句中为真
	}
	else if (S.base == S.top)
	{
		printf("\n顺序栈为空。\n");
		return OK;  //1,真
	}
	else if (S.base != S.top)
	{
		// printf("顺序栈不为空。\n");
		return NO;  //0,假
	}
}

int pop(SqStack& S)
{
	if (!S.base)
	{
		printf("弹出栈顶元素时,顺序栈不存在。\n");
		return ERROR;  //负数在IF、while等判断语句中为真
	}
	else if (S.base == S.top)
	{
		printf("弹出栈顶元素时,顺序栈为空。\n");
		return NO;
	}

	int e = *(S.top - 1);
	S.top--;
	return e;
}

在这里插入图片描述

3.6.2 括号匹配的检验

//算法3.21 括号的匹配
//利用链栈
#include <stdio.h>
#include <stdlib.h>

typedef int Status;

typedef struct StackNode
{
	char symbol;
	struct StackNode* next;
}*LinkStack;

#define OK 1
#define NO 0
#define MAXLENGTH 100

void initstack(LinkStack& S);
void  push(LinkStack& S, char s);
char pop(LinkStack& S);
Status StackEmpty(LinkStack& S);
char gettop(LinkStack& S);
Status Matching(LinkStack& S, char* ch);
int getsymble(char sym[], int limit);

int main()
{
	int status = 0;
	int i = 0;
	char sy[MAXLENGTH];
	int length = 0;
	
	//连续输入,并进行判断;直到输入结束符
	while ((length = getsymble(sy, MAXLENGTH)) > 0)
	{
		struct StackNode* N = NULL;
		status = Matching(N, sy);
		printf("%d\n", status);
	}

	return 0;
}

//使用char型数组,不是字符串,并对里面的内容进行分割
int getsymble(char sym[], int limit)
{
	int i, c;
	i = c = 0;

	for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		sym[i] = c;

	if (c == '\n')
	{
		sym[i] = c;
		++i;
	}

	sym[i] = '\0';
	return i;
}

//初始化
void initstack(LinkStack& S)
{
	S = NULL;
	printf("初始化链栈成功。\n");
}


//压入
void  push(LinkStack& S, char s)
{
	LinkStack p = NULL;

	p = (struct StackNode*)malloc(sizeof(struct StackNode));
	p->symbol = s;
	p->next = S;
	S = p;
	printf("成功将符号%c压入链栈。\n",s);
}

//弹出
char pop(LinkStack& S)
{
	if (S == NULL)
	{
		printf("删除栈顶元素时,栈为空。\n");
		return 'x';
	}

	struct StackNode* p = S;
	char s = S->symbol;
	S = S->next;
	free(p);
	return s;
}


//判空
Status StackEmpty(LinkStack& S)
{
	if (S==NULL)
	{
		return OK;  //是空的,返回1
	}
	else
	{
		return NO;   //不是空的,返回0
	}
}

char gettop(LinkStack& S)
{
	if (S == NULL)
	{
		printf("获取栈顶元素时,栈为空。\n");
		return 'x';
	}

	return S->symbol;
}


Status Matching(LinkStack& S, char* ch)
{
	//检验表达式中所含括号是否正确匹配,如果匹配,则返回true, 否则返回false 
	initstack(S);

	int flag = 1;
	char r = 'a';
	int i = 0;

	//假设表达式以"#"结束
	while (ch[i] != '#' && flag)
	{
		switch (ch[i])
		{
			case '(' :
				push(S, ch[i]);
				break;
			case '[':
				push(S, ch[i]);
				break;
			case '{':
				push(S, ch[i]);
				break;
			case ')':
				if (!StackEmpty(S) && gettop(S) == '(')
				{
					r = pop(S);
				}
				else
					flag = 0;
					break;
			case ']':
				if (!StackEmpty(S) && gettop(S) == '[')
				{
					r = pop(S);
				}
				else
					flag = 0;
				break;
			case'}':
				if (!StackEmpty(S) && gettop(S) == '{')
				{
					r = pop(S);
				}
				else
					flag = 0;
				break;
		}

		i++;
	}

	if (StackEmpty(S) && flag)
		return OK;
	else
		return NO;
}

在这里插入图片描述

3.6.3 表达式求值

//算法3.22 表达式求值
//链栈

#include <stdio.h>
#include <stdlib.h>

typedef struct stackNode
{
	char data;
	struct stackNode* next;
}*Linkstack;

typedef enum {da,xiao,deng,ERROR}pre; //ERROR表示错误状态,可加可不加
/* 枚举数据类型中的枚举元素,是常亮,不是变量,因此枚举元素又称为枚举常量。 
既然系统已经在声明枚举类型时,指定了ERROR是一种枚举元素(常量),
则如果再对其进行宏定义 #define ERROR 0,不管是否与枚举中定义的值相等,都会出现编译错误。*/

//#define ERROR -1  //不需要这句

#define MAXLENGTH 100

int getsymble(char sym[], int limit);
void InitStack(Linkstack& S);
void Push(Linkstack& S, char e);
char Pop(Linkstack& S);
char GetHead(Linkstack& S);
int In(char p);
pre Precede(char x, char y);
char Operate(char a, char x, char b);
char EvaluateExpression(char* sy);

int main()
{
	char sy[MAXLENGTH];
	int length = 0;

	//连续输入,并进行判断;直到输入结束符
	while ((length = getsymble(sy, MAXLENGTH)) > 0)
	{
		printf("结果是:%d\n", EvaluateExpression(sy)); 
		//主函数中必须使用%d输出数值,则函数EvaluateExpression中的计算结果就要提前进行相应转换
	}
	return 0;
}

//使用char型数组,不是字符串,并对里面的内容进行分割
int getsymble(char sym[], int limit)
{
	int i, c;
	i = c = 0;

	for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		sym[i] = c;

	if (c == '\n')
	{
		sym[i] = c;
		++i;
	}

	sym[i] = '\0';
	return i;
}

//初始化链栈
void InitStack(Linkstack& S)
{
	S = NULL;
	printf("初始化链栈成功。\n");
}

//针对最原始表达式中的操作数值,压入栈中的只能是[0,9]的一位数数字
void Push(Linkstack& S, char e)
{
	Linkstack p = (Linkstack)malloc(sizeof(struct stackNode));
	p->data = e;
	p->next = S;
	S = p;
	printf("已成功将元素%c压入栈中。\n", e);
}

char Pop(Linkstack& S)
{
	char e = 'a';  //初始化
	Linkstack r = S;
	e = S->data;
	S = S->next;
	free(r);
	return e;
}

//针对计算链栈的返回的栈顶、弹出的数值,只能返回[-128,127的]255个值。
//且大于一位的,在打印输出时要选择printf的参数是"%d",而不能是"%c".
char GetHead(Linkstack& S)  
{
	return S->data;
}

int In(char p)
{
	if (p == '+' || p == '-' || p == '*' || p == '/' || p == '(' || p == ')' || p == '#')
	{
		return 1; //是运算符返回1
	}
	else
	{
		return 0;  //不是运算符,返回0
	}
}

//输出结果的含义为:x>y  x<y  x=y
pre Precede(char x, char y)  //x,y是有序的
{
	//")"与 "("、"#"与")" 以及"("与"#"相继出现时,报错。
	if ((x == '(' && y == '#') || (x == ')' && y == '(') || (x == '#' && y == ')'))
	{
		return ERROR;  //枚举类型中的ERROR;
	}
	else if (x == '+' || x == '-') {
		if (y == '+' || y == '-' || y == ')' || y == '#')
			return da;
		else
			return xiao;
	}
	else if (x == '*' || x == '/') {
		if (y == '+' || y == '-' || y == '*' || y == '/' || y == ')' || y == '#')
			return da;
		else
			return xiao;
	}
	else if (x == '(') {
		if (y == ')')
			return deng;
		else
			return xiao;
	}
	else if (x == ')')
		return da;
	else { 
		if (y == '#')
			return  deng;
		else  //当x为空字符时,不论y是什么,都返回xiao
			return xiao;
	}
}

//针对数值,操作数a、b只能是[0,9]的一位数数字,
//计算结果返回值result只能是[-128,127的]区间内的255个值。且大于一位的,打印输出时要选择printf的参数是"%d",而不能是"%c".
char Operate(char a, char x, char b) 
{
	int c = a - '0';
	int d = b - '0';   
	//先将a,b由二进制中char型代表的个位数,转化为十进制中int型的个位数
	char result = '0';  //最后要把结果转换为二进制中的char型

	if (x == '+')
	{
		result = c + d + '0';
	}
	else if (x == '-')
	{
		result = c - d + '0';
	}
	else if (x == '*')
	{
		result = c * d + '0';
	}
	else
	{
		result = c / d + '0';
	}

	return result;
}

char EvaluateExpression(char *sy)
{
	Linkstack OPND = NULL;
	InitStack(OPND);

	Linkstack OPTR = NULL;
	InitStack(OPTR);

	char p = sy[0];
	int i = 0; //i是数组sy的下标

	//p是键盘输入的。在输入表达式时,不用输入起始符#
	Push(OPTR, '#');//表达式起始符#入栈

	char a = '0';
	char theta = '+';
	char b = '0';	
	//theta、a和b是算符和两个操作数(当输入为算符且优先级高时运算用:a theta b)
	
	while (p != '#' || GetHead(OPTR) != '#') {//表达式没有扫描完毕或OPTR的栈顶元素不为‘#’
		//如果是算符就比较优先级(当出现优先级高的算符就运算,优先级低的就入栈)
		if (In(p))
			switch (Precede(GetHead(OPTR), p))
			{
			case xiao://若优先级低,先把算符压入OPTR不计算
				Push(OPTR, p);
				p = sy[++i];
				break;
			case da://若优先级高,则进行运算,弹出OPTR栈顶的算符和OPND的两个数
				//输入)的情况下优先级总是高【(则等于,可消掉括号】,前一个算符参与后运算不输入新p,)会循环到下一次和前一个算符再比较优先级直到遇到(
				theta = Pop(OPTR);//theta为弹出的算符
				b = Pop(OPND);//先出的是后进的
				a = Pop(OPND);//后出的是先进的
				Push(OPND, Operate(a, theta, b));//运算结果压入OPND
				//Operate的计算结果为二进制中的char型
				break;
			case deng://若优先级相等(只有()和##两种情况)则括号匹配成功,消去栈顶的(/#并且不让p入栈,写新p
				//(##其实不会出现,当p为#且栈顶元素为#时就退出循环了已经)
				theta = Pop(OPTR);//theta为弹出废弃运算符
				p = sy[++i];
				break;
			default:
				return -1;
			}
		//如果是操作数就入栈OPND
		else {
			Push(OPND, p);
			p = sy[++i];
		}
	}
	int sum = GetHead(OPND) - '0';  
//主函数中必须使用%d输出数值,则此处要将计算结果转化为二进制中的char型,否则27会输出75,多出二进制char型字符'0'的48.
	return sum;
}

正常输入时:在这里插入图片描述

在这里插入图片描述

3.6.4 舞伴问题

利用队列的基本操作,完成舞伴问题——索儿呀

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。
指针数组的定义形式一般为:
dataType *arrayName[length]

单个指针的定义:
dataType *ptr
数据结构中的顺序表,采用动态分配的一维数组这一数据类型表示线性表。
其中,base定义的是单个指针。
根据数组名称与指向该数组基地址的指针的关系,base也是数组名称。

因为数组名base是指向该数组基地址的指针,而其被定义为指向Person的指针,因此数组中的数据类型为Person(而并非是指向Person的指针类型。).
#define MaxSize 100
typedef struct {
	Person* base;  //相当于Person base[MaxSize],但是如此定义系统会直接给该数组base分配MaxSize * sizeof(Person)大小的内存,再初始化分配内存会报错,不符合一般性的处理方式。
	int front;
	int rear;
}Seq;
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	char name[20];
	char sex; //F代表女性,M代表男性 
}Person;

#define MaxSize 100
typedef struct {
	Person* base;
	int front;
	int rear;
}Seq; //队列的顺序存储结构 

int Init(Seq& L);
int Enter(Seq& L, Person m);
int Out(Seq& L, Person& m);
int JudgeEmpty(Seq L);
Person GetHead(Seq L);
void DancePartner(Person* dancer, int num);

int main() {
	int num;
	Person dancer[MaxSize];
	printf("the number of dancers:");
	scanf_s("%d", &num);

	printf("依次输入舞者的信息(名字和性别,F代表女,M代表男)\n");
	for (int i = 0; i < num; i++) {
		printf("请输入第%d个舞者的姓名:", i + 1);
		scanf_s("%s", &(dancer[i].name), 20);


		printf("请输入第%d个舞者的性别:", i + 1);
		scanf_s(" %c", &(dancer[i].sex));  //前面有了一个scanf_s,这个前面的空格不能省略,否则会跳过第二个scanf_s
	}
	DancePartner(dancer, num);
	return 0;
}

int Init(Seq& L) {    //初始化
	L.base = (Person*)malloc(MaxSize * sizeof(Person));
	L.front = L.rear = 0;
	return 1;
}

int Enter(Seq& L, Person m) {     //入队列
	if ((L.rear + 1) % MaxSize == L.front) {
		printf("队列已满!");
		return 0;
	}
	L.base[L.rear] = m;
	L.rear = (L.rear + 1) % MaxSize;
	return 1;
}

int Out(Seq& L, Person& m) {    // 出队列
	if (L.rear == L.front) {
		printf("队列空!");
		return 0;
	}
	m = L.base[L.front];
	L.front = (L.front + 1) % MaxSize;
	return 1;
}

int JudgeEmpty(Seq L) {  //队列判空 
	if (L.rear == L.front) {
		return 0;
	}
	return 1;
}

Person GetHead(Seq L) { //取队列头元素 
	Person m = { '\0','\0' };

	if (L.rear == L.front) {
		printf("队列空!");
		return m;
	}

	m = L.base[L.front];
	return m;
}

void DancePartner(Person* dancer, int num) {
	Seq Male, Female;
	Person p1, p2;
	Init(Male);
	Init(Female);
	for (int i = 0; i < num; i++) { //男女依次入男队和女队 
		if (dancer[i].sex == 'F')
		{
			Enter(Female, dancer[i]);
		}
		else
		{
			Enter(Male, dancer[i]);
		}
	}

	printf("The dancing partners are:\n");
	while (JudgeEmpty(Male) && JudgeEmpty(Female)) {
		Out(Female, p1);
		Out(Male, p2);
		printf("(%s,%s)\n", p1.name, p2.name);
	}
	if (!JudgeEmpty(Male)) {
		p1 = GetHead(Female);
		printf("The first woman in next dance to get a partner is:%s\n", p1.name);
	}
	else if (!JudgeEmpty(Female)) {
		p2 = GetHead(Male);
		printf("The first man in next dance to get a partner is:%s\n", p2.name);
	}
}

在这里插入图片描述

栈和队列的存储结构

//顺序栈
#define MAXSIZE 100
typedef struct
{
	SElemType* base;
	SElemType* top;
	int stacksize;
}SqStack;

在这里插入图片描述

//链栈
typedef struct StackNode
{
	ElemType data;
	struct StackNode* next;
}StackNode,*LinkStack;

在这里插入图片描述

//循环队列
#define MAXQSIZE 100
typedef struct
{
	QElemType* base;
	int front;
	int rear;
}SqQueue;

在这里插入图片描述
在这里插入图片描述

//链队
typedef struct QNode
{
	QElemType data;
	struct QNode* next;
}QNode,*QueuePtr;
typedef struct
{
	QueuePtr front;  
	QueuePtr rear;
}LinkQueue;

在这里插入图片描述

链栈和链队
相同点:都有始终指向第一个结点的指针。

不同点
链栈:①没有头结点(初始化不需要分配内存),
②有一个栈顶指针S始终指向第一个结点;
③链栈的第一个结点中也会存储数据;
④一个链栈可由一个栈顶指针S唯一确定。

链队
①有头结点(初始化需要分配内存);
②头指针Q.front始终指向头结点;
③不往链队的头结点中储存数据;
④一个链队需要头指针Q.front和尾指针Q.rear,两个指针才能唯一确定。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值