数据结构---第二章案例分析
案例分析与实现
数制的转换
分析:将一个十进制整数 N 转换为八进制数时,在计算过程中,把 N 与 8 求余得到的八进制数的各位依次进栈,计算完毕后将栈中的八进制数依次出栈输出,输出结果就是待求得的八进制数。
算法步骤
-
初始化一个空栈S
-
当十进制数N非零时, 循环执行以下操作:
- 把 N 与 8 求余得到的八进制数压入栈 S;
- N更新为N与8的商。
-
当栈S 非空时,循环执行以下操作
- 弹出栈顶元素e;
- 输出 e。
#include<iostream>
using namespace std;
#define MAXSIZE 100
typedef struct
{
int* base;
int* top;
int stacksize;
}SqStack;
void InitStack(SqStack& S)
{
S.base = new int[MAXSIZE];
if (!S.base) exit;
S.top = S.base;
S.stacksize = MAXSIZE;
}
int DestroyStack(SqStack& S)
{
delete S.base;
S.base = NULL;
S.top = NULL;
S.stacksize = 0;
return 0;
}
int StackEmpty(SqStack S)
{
if (S.top == S.base)
return 1;
else
return 0;
}
int Push(SqStack& S, int e)
{
if (S.top - S.base == S.stacksize) return 0;
*S.top= e;
S.top++;
return 1;
}
int Pop(SqStack& S, int& e)
{
if (S.top == S.base)
return 0;
S.top--;
e = *S.top;
return 1;
}
void conversion()
{
int i;
int e;
SqStack s;
InitStack(s);
cout<<"请输入一个整数:";
cin>> i;
while (i)
{
Push(s, i% 8);
i = i/ 8;
}
cout<<"转换后的八进制为:";
while (!StackEmpty(s))
{
Pop(s, e);
cout << e;
}
cout << endl;
DestroyStack(s);
}
int main()
{
conversion();
return 0;
}
括号匹配的检验
分析:借助一个栈, 每当读入一个左括号, 则直接入栈,等待相匹配的同类右括号;每当读入一个右括号,若与当前栈顶的左括号类型相同, 则二者匹配,将栈顶的左括号出栈,直到表达式扫描完毕,还要考虑括号不匹配出错的情况。
算法步骤
-
初始化一个空栈S。
-
设置一标记性变量 flag, 用来标记匹配结果以控制循环及返回结果, 1表示正确匹配, 0表示错误匹配, flag 初值为 1。
-
扫描表达式,依次读入字符ch, 如果表达式没有扫描完毕或 flag 非零, 则循环执行以下操作:
- 若 ch是左括号"[" 或"("' 则将其压入栈;
- 若 ch是右括号")"' 则根据当前栈顶元素的值分情况考虑:若栈非空且栈顶元素是 "(",则正确匹配, 否则错误匹配, flag 置为 0;
- 若 ch是右括号"]"' 则根据当前栈顶元素的值分情况考虑:若栈非空且栈顶元素是"["'则正确匹配, 否则错误匹配, flag 置为 0。
-
退出循环后, 如果栈空且 flag 值为1则匹配成功, 返回 true, 否则返回 false。
#include<iostream> using namespace std; #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define MAXSIZE 100 typedef int Status; typedef char SElemType; typedef struct { SElemType* base; SElemType* top; int stacksize; }SqStack; Status InitStack(SqStack &S) { S.base = new char[MAXSIZE]; if (!S.base)exit; S.top = S.base; S.stacksize = MAXSIZE; return OK; } Status DestroyStack(SqStack &S) { delete S.base; S.base = NULL; S.top = NULL; S.stacksize = 0; return OK; } Status StackEmpty(SqStack S) { if (S.top == S.base) return TRUE; else return FALSE; } Status Push(SqStack &S, SElemType e) { if (S.top - S.base == S.stacksize) return 0; *S.top = e; S.top++; return OK; Status GetTop(SqStack S, SElemType* e) { if (S.top == S.base) { return FALSE; } else { *e = *(S.top - 1); return OK; } } Status Pop(SqStack &S, SElemType &e) { if (S.top == S.base) { cout << "栈为空"; return ERROR; } S.top--; e = *S.top; return OK; } Status StackTraverse(const SqStack * S) { SElemType* p = S->base; if (S->base == S->top) { cout << "栈为空"; return FALSE; } while (p != S->top) { cout<< *p++; } cout << endl; return OK; } Status BracketMatch(SqStack &S, const char* string) { const char* flag = string; SElemType e; InitStack(S); while (*flag != '\0') { switch (*flag) { case '(': case '[': case '{': Push(S, *flag); break; case ')': if (FALSE == GetTop(S, &e)) return FALSE; if (e == '(') { if (ERROR == Pop(S, e)) return FALSE; } else return FALSE; break; case ']': if (FALSE == GetTop(S, &e)) return FALSE; if (e == '[') { if (ERROR == Pop(S, e)) return FALSE; } else return FALSE; break; case '}': if (FALSE == GetTop(S, &e)) return FALSE; if (e == '{') { if (ERROR == Pop(S, e)) return FALSE; } else return FALSE; break; default:; } flag++; } if (!StackEmpty(S)) return FALSE; return TRUE; } int main() { char String[6]; SqStack stack; if (!String) { cout<<"分配内存失败."<<'\n'; return 0; } while (1) { cout<<"请输入一行含括号的表达式(输入#退出):"; for (int i = 0; i < 5; i++) cin >> String[i]; if (String[0] == '#') break; if (TRUE == BracketMatch(stack, String)) { cout << "匹配成功!" << endl; } else { cout << "匹配失败!" << endl; } } return 0; }
时间和空间复杂度都是O(n),从头到尾依次扫描每个字符,并存储"[""(",匹配成功则出栈,所以栈所占空间不超过n
表达式求值
分析:任何一个表达式都是由操作数(常数和被说明为变量或常量的标识符)、运算符(算术运算符、关系运算符和逻辑运算符 )、界限符(左右括号和表达式结束符)。其中运算符和界限符统称算符,按照运算规则,先乘除后加减,从左算到右,先括号内再括号外来进行运算,这里为了实现优先算法可以使用两个工作栈,一个称做OPTR,用以寄存运算符;另一个称作OPND, 用以寄存操作数或运算结果。
算法步骤
-
初始化OPTR栈和OPND栈,将表达式起始符"#"压入OPTR栈。
-
扫描表达式,读入第一个字符ch, 如果表达式没有扫描完毕至"#"或OPTR的栈顶元素不为"#"时,则循环执行以下操作:
- 若ch不 是运算符,则压入OPND栈,读入下一字符ch;
- 若ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,做不同的处理:
- 若是小于,则ch压入OPTR栈,读入下一字符ch;
- 若是大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数, 进行相应运算,结果压入OPND栈;
- 若是等于,则OPTR的栈顶元素是 "(" 且ch是")"'这时弹出OPTR栈顶的 "(",相当于括号匹配功,然后读入下一字符ch。
-
OPND栈顶元素即为表达式求值结果,返回此元素。
Status In(ElemType c) //判断输入的算符是否符合规则
{
if (c == '+' || c == '-' || c == '*' || c == '/' || c == '#' || c == '(' || c == ')' || c == '[' || c == ']')
return 1;
else
return 0;
}
char Compare(ElemType a, ElemType b) //判断运算符栈的栈顶元素a和读入元素b的优先级
{
if (a == '+' || a == '-')
{
if (b == '+' || b == '-' || b == '>' || b == '#' || b == ')' || b == ']')
return '>';
else return '<';
}
if (a == '*' || a == '/')
{
if (b == '(' || b == '[')
return '<';
else return '>';
}
if (a == '(')
{
if (b == ')')
return '=';
else return '<';
}
if (a == '[')
{
if (b == ']')
return '=';
else return '<';
}
if (a == '#')
{
if (b == '#')
return '=';
else return '<';
}
}
ElemType Operate(ElemType a, ElemType x, ElemType b) //进行运算的函数
{
switch (x) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
}
}
时间空间复杂度均为O(n),从头到尾依次扫描,字符串的长度为n,将操作数和算符依次入栈,因为还会出栈,两个栈所占空间长度不会超过n
舞伴问题
分析:先入队的男士或女士先出队配成舞伴,因此设置两个队列分别存放男士和女士入队者。用数组记录男士和女士,然后依次扫描数组元素,根据性别决定是进入男队还是女队,构造完成后,将两队队头元素出队配成舞伴直至某队列为空,若有队伍剩余则作为下一轮第一个可获得舞伴者。
算法步骤
- 初始化 Mdancers 队列和 Fdancers 队列。
- 反复循环, 依次将跳舞者根据其性别插入 Mdancers 队列或 Fdancers 队列。
- 当 Mdancers 队列和 Fdancers 队列均为非空时, 反复循环, 依次输出男女舞伴的姓名。
- 如果 Mdancers 队列为空而 Fdancers 队列非空, 则输出 Fdancers 队列的队头女士的姓名。
- 如果 Fdancers 队列为空而 Mdancers 队列非空, 则输出 Mdancers 队列的队头男士的姓名。
#include<iostream>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define MAXQSIZE 100
typedef int Status;
typedef char QElemType;
typedef struct
{
char name[20];
char sex;
}Person;
typedef struct
{
Person* base;//队列中数据元素类型为Person
int front;
int rear;
}SqQueue;
Status InitQueue(SqQueue& Q)
{
Q.base = new Person[MAXQSIZE];
if (!Q.base) exit;
Q.front = Q.rear = NULL;
return 1;
}
Status EnQueue(SqQueue& Q, Person e)
{
if ((Q.rear + 1) % MAXQSIZE == Q.front)
return 0;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXQSIZE;
return 1;
}
Status DeQueue(SqQueue& Q, Person& e)
{
if (Q.front == Q.rear) return ERROR;
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXQSIZE;
return 1;
}
Person GetHead(SqQueue Q)
{
if (Q.front != Q.rear)
return Q.base[Q.front];
}
Status QueueEmpty(SqQueue& Q)
{
if (Q.front == Q.rear) return OK;
else return ERROR;
}
void DancePartner(Person dancer[], int num)//结构数组danccer中存放跳舞的男女,num是跳舞的人数
{
Person p;
SqQueue Mdancers, Fdancers;
InitQueue(Mdancers);
InitQueue(Fdancers);
for (int i = 0; i < num; i++)
{
p = dancer[i];
if (p.sex == 'F') EnQueue(Fdancers, p);
else EnQueue(Mdancers, p);
}
cout << "The dancing partners are:\n";
while (!QueueEmpty(Fdancers) && !QueueEmpty(Mdancers))
{
DeQueue(Fdancers, p);
cout << p.name<<"-";
DeQueue(Mdancers, p);
cout << p.name<<endl;
}
if (!QueueEmpty(Fdancers))
{
p = GetHead(Fdancers);
cout << "The first woman to get a partner is:" << p.name << endl;
}
else if (!QueueEmpty(Mdancers))
{
p = GetHead(Mdancers);
cout << "The first man to get a partner is:" << p.name << endl;
}
}
int main()
{
SqQueue Mdancers, Fdancers;
int n;
Person dancer[MAXQSIZE];
cout << "请输入跳舞的人数:";
cin >> n;
for (int i = 1; i <= n; i++)
{
cout << "请输入第" << i << "舞者的名字:" << endl;
cin >> dancer[i - 1].name;
cout << "请输入第" << i << "个人的性别(F/M):" << endl;
cin >> dancer[i - 1].sex;
while (dancer[i - 1].sex != 'F' && dancer[i - 1].sex != 'M')
{
cout << "输入错误,请重新输入:\n";
cout << dancer[i - 1].sex;
cout << "请输入第" << i << "个人的性别(F/M):" << endl;
cin >> dancer[i - 1].sex;
break;
}
}
DancePartner(dancer, n);
return 0;
}
时间空间复杂度均为O(n),从头到尾依次扫描,跳舞总人数为n,各自分别或进入女队或进入男队,两者之和不会超过n。