《数据结构(A)》第3章研究型作业--栈与队的应用

设计型作业题目

3.8  英国人格思里于1852年提出四色问题(four colour problem,亦称四色猜想),即在为一个平面或一球面的地图着色时,假定每一个国家在地图上是一个连通域,并且有相邻边界线的两个国家必须用不同的颜色,问是否只要四种颜色就可完成着色。现在给定一张地图,要求对这张地图上的国家用不超过四种的颜色进行染色。

要求建立地图的邻接矩阵存储结构,输入国家的个数和相邻情况,输出每个国家的颜色代码。

3.9  (《数据结构题集(C 语言版)》,第25页,第3.21题,第3.22题,第3.23题;难度系数分别为3,3,5。难度系数5的题目很具有挑战性。)

(1)假设表达式由单字母变量和双目四则运算算符构成。试写一个算法,将一个通常书写形式且书写正确的表达式转换为逆波兰式。

(2)再接着写一个算法,对以逆波兰式表示的表达式求值。

(3)最后,写一个算法,判断给定的非空后缀表达式是否为正确的逆波兰式(即后缀表达式),如果是,则将它转换为波兰式(即前缀表达式)。

(4)分析前缀表达式、中缀表达式、后缀表达式在表达能力,以及相互区别。

研究型作业题目解答

【第3.8题解答】

解题思路:

  用邻接矩阵来存储地图,如果i区域和j区域相邻, 那么[i][j]、[j][i]赋值为1。每一个区域的染色用0,1,2,3逐个尝试,如果染色与相邻区域颜色不同,就将颜色入栈。如果染色与相邻区域相同,则尝试下一个颜色,如果四种颜色都用完了颜色仍然相同,则退栈至上一个区域,看此区域是否可以染其他颜色,如能,换色后对下一个区域继续染色,若不能,再退栈至上一个区域,看是否能染其他颜色,如此循坏,直至所有区域都染色成功。

源代码:

main.cpp

#include <stdio.h>
#define N 6
void FourColor(int d[N][N], int s[N]) {
  int area, k, i, color;
  s[0] = 1;//从第一个地图上色
  area = 1;//从第二个区域开始
  color = 1;//从以一种颜色开始
  while (area < N) {//终止条件
    while (color <= 4) {
      k = 0;//每个区域都从第一个区域开始检测
      while ((k < area) && s[k] * d[k][area] != color) {
        //循坏条件终止
        //1、前面的结点全部试过
        //2、当area和第k个区域不重色,d[k][area]表示结点关联
        k++;
      }
      if (k >= area) {
        //从循环走出来,都没有出现重色的
        s[area] = color;
        area++;//进入下一个颜色的循坏
        color = 1;//颜色重置
        if (area >= N)
          break;
      }
      else
        //出现重色,修改当前颜色
        color++;
    }
    if (color > 4) {
      //所有颜色都试过仍然重色,退栈
      area = area - 1;
      color = s[area] + 1;//颜色不够用,颜色加1
    }
  }
  for (i = 0; i < N; i++) {
    printf("第%d个区域的颜色为:", i + 1);
    switch (s[i]) {
    case 1:printf("红色\n"); break;
    case 2:printf("蓝色\n"); break;
    case 3:printf("绿色\n"); break;
    case 4:printf("黄色\n"); break;
    }
  }
}
int main() {
  int d[N][N] = {
    {0,1,1,1,1,1},//相邻矩阵,[i][j]、[j][i]=1
    {1,0,1,1,0,0},
    {1,1,0,1,1,1},
    {1,0,1,0,0,1},
    {1,1,1,0,0,1},
    {1,0,1,1,1,0},
  };
  int s[N] = { 0 };//第i个点所涂的颜色
  FourColor(d, s);
  return 0;
}

【第3.9题解答】

实验思路:

设立运算符栈-->设表达式的结束符为“#”,将其压入栈底-->若当前字符是操作符,则直接发送给后缀式。若当前运算符的优先级高于栈顶运算符,则进栈,否则,退出栈顶运算符给后缀式-->“(”对它之前后的运算符其隔离作用,“)”可视为自相应左括号开始的表达式的结束符。

源代码:

main.cpp

#include "Chapter3_9.h"
int main() {
  char str[100] = { 0 };
  do {
    gets_s(str);
  } while (!str[0]);
  printf("%s",RPExpression(str));
  return 0;
}

Chapter3_9.h

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define _CRT_SECURE_NO_WARNINGS
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0
typedef char SElemType; // 栈Stack的元素类型
typedef int Status;
typedef struct {
  char ch;
  int* base;
  int* top;
  int stacksize;
}SqStack;
char* RPExpression(char* e);// 返回表达式e的逆波兰式 
Status InitStack(SqStack* s);
Status Push(SqStack* s, SElemType e);
Status Pop(SqStack* s, SElemType* e);
Status StackEmpty(SqStack s);
SElemType Top(SqStack s);

Chapter3_9.cpp

#include "Chapter3_9.h"
Status InitStack(SqStack* S) {
  S->base = (int*)malloc(STACK_INIT_SIZE * sizeof(int));
  if (!S->base) {
    exit(-1);
  }
  S->top = S->base;
  S->stacksize = STACK_INIT_SIZE;
}

Status Push(SqStack* S, SElemType e) {
  if (S->top - S->base >= S->stacksize) {
    S->base = (int*)realloc(S->base, (S->stacksize + STACKINCREMENT)
      * sizeof(int));
    if (!S->base) {
      exit(-1);
    }
    S->top = S->base + S->stacksize;
    S->stacksize += STACKINCREMENT;
  }
  *S->top++ = e;
  return OK;
}

Status Pop(SqStack* S, SElemType* e) {
  if (S->top == S->base) exit(-1);
  *e = *(--S->top);
  return OK;
}

Status StackEmpty(SqStack S) {
  if (S.top != S.base) return FALSE;
  else return TRUE;
}

SElemType Top(SqStack S) {
  char* e=NULL;
  *e = *(S.top - 1);
  return *e;
}
char* RPExpression(char* e) {
  SqStack S1, S2;
  //栈s1用于存放运算符,栈s2用于存放逆波兰式
  InitStack(&S1);
  InitStack(&S2);
  //假设字符'#'是运算级别最低的运算符,并压入栈s1中
  Push(&S1, '#');
  //p指针用于遍历传入的字符串,ch用于临时存放字符,length用于计算字符串长度
  char* p = e, ch;
  int length = 0;
  for (; *p != '\0'; p++) {
    switch (*p) {
    //遇'('则直接入栈s1
    case '(':Push(&S1, *p); break;
      //遇')'则将距离栈s1栈顶的最近的'('之间的运算符,
      //逐个出栈,依次送入栈s2,此时抛弃'('
    case ')':
      while (Top(S1) != '(') {
        Pop(&S1, &ch);
        Push(&S2, ch);
      }
      Pop(&S1, &ch);
      break;
      /*遇下列运算符,则分情况讨论:
      1.若当前栈s1的栈顶元素是'(',则当前运算符直接压入栈s1;
      2.否则,将当前运算符与栈s1的栈顶元素比较,
        若优先级较栈顶元素大,则直接压入栈s1中,
       否则将s1栈顶元素弹出,并压入栈s2中,
 直到栈顶运算符的优先级别低于当前运算符,然后再将当前运算符压入栈s1中*/
    case '+':
    case '-':
      for (ch = Top(S1); ch != '#'; ch = Top(S2)) {
        if (ch == '(') break;
        else {
          Pop(&S1, &ch);
          Push(&S2, ch);
        }
      }
      Push(&S1, *p);
      length++;
      break;
      //遇操作数则直接压入栈s2中
    default:
      Push(&S2, *p);
      length++;
    }
  }
  while (!StackEmpty(S1) && Top(S1) != '#') {
    Pop(&S1, &ch);
    Push(&S2, ch);
  }
  //最后将栈s2输出,逆序排列成字符串;
  char* result;
  result = (char*)malloc(sizeof(char) * (length + 1));
  result += length;
  *result = '\0';
  result--;
  for (; !StackEmpty(S2); result--) {
    Pop(&S2, &ch);
    *result = ch;
  }
  ++result;
  return result;
}

(2)再接着写一个算法,对以逆波兰式表示的表达式求值。

运行结果:

 源代码:

#include <stdio.h>
#include <stdlib.h>
#define MaxSize 199
struct SNode {
  int Data[MaxSize];
  int Top;
};
typedef struct SNode* Stack;
Stack CreateStack() {
  Stack p;
  p = (Stack)malloc(sizeof(struct SNode));
  p->Top = -1;
  return p;
}
void push(Stack S, int x) {
  if (S->Top == MaxSize) {
	printf("Stack Full\n");
  }
  else {
	S->Data[++S->Top] = x;
  }
}
int pop(Stack S) {
  if (S->Top == -1) {
	printf("Stack is Empty!\n");
  }
  else {
	int t;
	t = S->Data[S->Top];
	S->Top--;
	return t;
  }
}
int mypow(int x, int y) {
  int r = 1, i;
  for (i = 1; i <= y; i++) {
	r *= x;
  }
  return r;
}
int main() {
  Stack S;
  S = CreateStack();
  char ch, c[20]; ch = getchar();
  int a = 0, b = 0, an = 0, t, count, i, f;
  while (ch != EOF) {
	//if(ch==' '){
	//	ch=getchar();continue;
	//}
	if (ch >= '0' && ch <= '9') {
	  push(S, ch - '0');
	  ch = getchar();//是空格则吸收 ,否则要是还是数字进入while循环 
	  while (ch >= '0' && ch <= '9')//0-9压入堆栈 
	  {
		t = pop(S);
		push(S, t * 10 + ch - '0');
		ch = getchar();
	  }
	}
	else if (ch == '+' || ch == '-' || ch == '*') {//遇到运算符,弹出顶上两个运算数,并将运算结果压入堆栈 
	  a = pop(S); b = pop(S);
	  switch (ch) {
	  case '+':an = a + b; break;
	  case '-':an = b - a; break;
	  case '*':an = a * b; break;
	  }
	  push(S, an);
	}
	else if (ch == '\n')break;
	ch = getchar();
  }
  printf("%d\n", pop(S));
}

(4)分析前缀表达式、中缀表达式、后缀表达式在表达能力,以及相互区别。

中缀表达式是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。中缀表达式是人们常用的算术表示方法。

前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。

后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值