设计型作业题目
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)。
后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。