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,两个指针才能唯一确定。