《数据结构》上机实验(第三章)Ⅱ参考书目:《数据结构教程(第5版)》李春葆 主编

1. 反映病人到医院排队看医生的情况。在病人排队过程中主要重复两件事:

(1)病人到达诊室,将病历本交给护士,排到等待队列中候诊。
(2)护士从等待队列中取出下一位病人的病历,该病人进入诊室就诊。

要求模拟病人等待就诊这一过程。程序采用菜单方式,其选项及功能说明如下:

1:排队一输入排队病人的病历号,加入到病人排队队列中;
2:就诊一病人排队,队列中最前面的病人就诊,并将其从队列中删除;
3:查看排队一从队首到队尾列出所有的排队病人的病历号;
4:不再排队,余下依次就诊—从队首到队尾列出所有的排队病人的病历号,并退出运行;
5:下班退出运行。

#include <stdio.h>
# include <malloc.h> 
typedef struct qnode
{
	int data; //病历号
	struct qnode* next; //下一个结点指针
}QNode; //链队结点类型

typedef struct
{
	QNode* front, * rear;
}QuType; //声明链队类型
	
void Destroyqueue(QuType*& qu) //释放链队
{
	QNode* pre, * p;
	pre = qu->front; //若链队不空
	if (pre != NULL)
	{
		p = pre->next;
		while (p != NULL) //释放队列中所有数据结点
		{
			free(pre);
			pre = p;
			p = p->next;
		}
		free(pre);
	}
	free(qu); //释放链队结点
}

bool exist(QuType* qu, int no) //队列中是否有no病历号的病人
{
	bool find = false;
	QNode* p = qu->front;
	while (p != NULL && !find)
	{
		if (p->data == no) find = true;
		else p=p->next;
	}
	return find;
}

void Seedoctor() //模拟病人看病的过程
{
	int sel, no;
	bool flag = true;
	QuType* qu;
	QNode* p;
	qu = (QuType*)malloc(sizeof(QuType)); //创建空队
	qu->front = qu->rear = NULL;
	while (flag) //循环执行
	{
		printf(">1:排队 2:就诊 3:査看排队 4.不再排队,余下依次就诊 5:下班 请选择:");
		scanf("%d", &sel);
		switch (sel)
		{
		case 1: //排队
			printf("输入病历号:");
			while (true)
			{
				scanf("%d", &no);
				if (exist(qu, no)) printf("输入的病历号重复,重新输入:");
				else break;
			};
			p = (QNode*)malloc(sizeof(QNode)); //创建结点
			p->data = no;
			p->next = NULL;
			if (qu->rear == NULL) qu->front = qu->rear = p; //第一个病人排队	
			else
			{
				qu->rear->next = p; //将p结点进队
				qu->rear = p;
			}
			break;
		case 2: //就诊
			if (qu->front == NULL) printf("没有排队的病人!\n"); //队空
			else //队不空
			{
				p = qu->front;
				printf(">>病人%d就诊\n", p->data);
				if (qu->rear == p) qu->front = qu->rear = NULL; //只有一个病人排队的情况
				else qu->front = p->next;
				free(p);
			}
			break;
		case 3: //查看排队
			if (qu->front == NULL) printf("没有排队的病人!\n"); //队空
			else //队不空
			{
				p = qu->front;
				printf(" >>排队病人:");
				while (p != NULL)
				{
					printf("%d ", p->data);
					p = p->next;
				}
				printf("\n");
			}
			break;
		case 4: //不再排队,余下依次就诊
			if (qu->front == NULL)	printf(">>没有排队的病人!\n"); //队空
			else //队不空
			{
				p = qu->front;
				printf(" >>病人按以下顺序就诊:");
				while (p != NULL)
				{
					printf("%d ", p->data);
					p = p->next;
				}
				printf("\n");
			}
			Destroyqueue(qu); //释放链队
			flag = false; //退出
			break;
		case 5: //下班
			if (qu->front != NULL) printf("请排队的病人明天就医!\n"); //队不空
			flag = false; //退出
			Destroyqueue(qu); //释放链队
			break;
		}
	}
}

int main()
{
	Seedoctor();
	return 0;
}

程序分析

  • 这里设计了一个不带头结点的单链表作为队列。
  • 运行结果:

2. 按升序对一个字符栈进行排序,即最小元素位于栈顶,最多只能使用一个额外的栈存放临时数据,并输出栈排序过程。

  • 算法思想:处理s栈的某个栈顶元素e,出栈元素e,将其存放在temp中。若临时栈temp为空,直接将e进入temp;若temp栈不空,将它的元素退栈(放入s栈中)直到temp栈顶元素小于e,再将tmp进人到temp栈中。
void StackSort(SqStack*& s) //对栈s中元素排序
{
	SqStack *temp;
	InitStack(temp);
	ElemType e, e1;
	while (!StackEmpty(s)) //s不空循环
	{
		Pop(s, e); //出栈元素e
		printf("   s:出栈%c=> ", e);
		while (!StackEmpty(temp))
		{
			GetTop(temp, e1);
			printf("temp:取栈顶元素%c ", e1);
			if (e1 > e)
			{
				printf("因%c>%c ", e1, e);
				printf("temp:退栈%c ", e1);
				Pop(temp, e1);
				printf("s:进栈%c ", e1);
				Push(s, e1);
			}
			else
			{
				printf("因%c<%c,退出循环 ", e1, e);
				break;
			}
		}
		Push(temp, e);
		printf("temp:进栈%c\n", e);
	}
	while (!StackEmpty(temp))
	{
		Pop(temp, e);
		Push(s, e);
	}
	DestroyStack(temp);
}

int main()
{
	ElemType e;
	SqStack *s;
	InitStack(s);
	printf("(1)依次进栈元素1,3,4,2\n");
	Push(s, '1');
	Push(s, '3');
	Push(s, '4');
	Push(s, '2');
	printf("(2)栈s排序过程:\n");
	StackSort(s);
	printf("(3)栈s排序完毕\n");
	printf("(4)s的出栈序列:");
	while (!StackEmpty(s))
	{
		Pop(s, e);
		printf("%c ",e);
	}
	printf("\n");
	DestroyStack(s);
	return 0;
}

程序分析

  • 运行结果:

3. 求解n皇后问题:即在n×n的方格棋盘上放置n个皇后,要求每个皇后不同行、不同列、不同左右对角线。

#include< stdio.h>
#include< stdlib.h>
#define Maxsize 100
typedef struct
{
	int col[Maxsize];//col[i]存放第i个皇后的列号
	int top; //栈顶指针
}StackType; //声明顺序栈类型

void dispasolution(StackType St) //输出一个解
{
	static int count = 0; //静态变量用于统计解个数
	printf("第%d个解:", ++count);
	for (int i = 1; i <= St.top; i++)
		printf("(%d,%d)", i, St.col[i]);
	printf("\n");
}

bool place(StackType St, int k, int j) //测试(k,j)是否与第1~k-1个皇后有冲突
{
	int i = 1;
	if (k == 1) return true; //放第一个皇后时没有冲突
	while (i <= k - 1) //测试与前面已放置的皇后是否有冲突
	{
		if ((St.col[i] == j) || (abs(j - St.col[i]) == abs(i - k))) return false; //有冲突时返回假
		i++;
	}
	return true; //没有冲突时返回真
}
			
void queen(int n) //求解n皇后问题
{
	int k;
	bool find;
	StackType St; //定义st
	St.top = 0; //初始化顶指针,为了让皇后从第1行开始,不用下标0
	St.top++;
	St.col[St.top] = 0; //col[1]=0,表示从第1个皇后开始,初始列号为0
	while (St.top != 0) //栈不空时循环
	{
		k = St.top; //试探栈顶的第k个皇后
		find = false; //尚未找到第k个皇后的位置,find设置为假
		for (int j = St.col[k] + 1; j <= n; j++) //为第k个皇后找一个合适的列号
			if (place(St, k, j)) //在第k行找到一个放皇后的位置(k,j)
			{
				St.col[St.top] = j; //修改第k个皇后的位置(新列号)
				find = true; //找到第k个皇后的位置, find设置为真
				break; //找到后退出for循环
			}
		if (find) //在第k行找到一个放皇后的位置(k,j)
		{
			if (k == n) dispasolution(St); //若所有皇后均放好输出一个解
			else
			{
				St.top++; //新进栈的皇后从第0列开始试
				St.col[St.top] = 0;
			}
		}
		else //若第k个皇后没有合适位置, 回溯
			St.top--; //即将第k个皇后退栈
	}
}

int main()
{
	int n; //n存放实际皇后个数
	printf("皇后问题(n<20) n= ");
	scanf("%d", &n);
	if (n > 20) printf("n值太大\n");
	else
	{
		printf("%d皇后问题求解如下:\n", n);
		queen(n);
	}
	return 0;
}

程序分析

  • 运行结果:

4. 设停车场内只有一个可停放n辆汽车的狭长通道,且只有一个大门可供汽车进出。汽车在停车场内按车辆到达时间的先后顺序,依次由南向北排列(大门在最北端,最先到达的第一辆车停放在车场的最南端),若车场内已停满n辆车,则后来的汽车只能在门外的便道(即候车场上)等候,一旦有车开走,则排在便道上的第一辆车即可开入;当停车场内某辆车要离开时,在它之后进入的车辆必须先退出车场为它让路,待该辆车开出大门外,其他车辆再按候车场原次序进入车场,每辆停放在车场的车在它离开停车场时必须按它停留的时间长短交纳费用。

  • 算法思想:用栈模拟停车场,用队列模拟车场外的便道,按照从键盘获取的数据序列进行模拟管理。每一组输入数据包括3个数据项:汽车到达(1)或者离开(2)、汽车牌照号码以及到达或离开的时刻。对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车辆离开,则输出汽车在停车场内停留的时间和应交纳的费用(在便道上停留的时间不收费)。这里栈采用顺序存储结构,队列采用环形队列。另外,还需设一个临时栈,用于临时停放为要给离开的汽车让路而从停车场退出来的汽车,也用顺序结构实现。
#include<stdio.h>
#include<malloc.h>
#define N 3 //停车场内最多的停车数
#define M 4 //候车场内最多的停车数
#define Price 2 //每单位停车费用
typedef struct
{
	int CarNo[N]; //车牌号
	int CarTime[N]; //进场时间
	int top; //栈指针
}SqStack; //声明顺序栈类型

typedef struct
{
	int CarNo[M]; //车牌号
	int front, rear; //队首和队尾指针
}SqQueue; //声明环形队列类型

//以下为栈的运算算法
void InitStack(SqStack* &s) //初始化栈
{
	s = (SqStack*)malloc(sizeof(SqStack));
	s->top = -1;
}

bool StackEmpty(SqStack* s) //判断栈空
{
	return s->top == - 1;
}

bool StackFull(SqStack* s) //判断栈满
{
	return s->top == N - 1;
}

bool Push(SqStack*& s, int e1, int e2) //进栈
{
	if (s->top == N - 1) return false; //栈满
	s->top++;
	s->CarNo[s->top] = e1;
	s->CarTime[s->top] = e2;
	return true;
}

bool Pop(SqStack*& s, int& e1, int& e2) //出栈
{
	if (s->top == -1) return false; //栈空
	e1 = s->CarNo[s->top];
	e2 = s->CarTime[s->top];
	s->top--;
	return true;
}

void DispStack(SqStack* s) //显示栈中元素
{
	for (int i = s->top; i >= 0; i--) printf("%d ", s->CarNo[i]);
	printf("\n");
}

//以下为队列的运算算法
void InitQueue(SqQueue*& q) //初始化队
{
	q = (SqQueue*)malloc(sizeof(SqQueue));
	q->front = q->rear = 0;
}
							
bool QueueEmpty(SqQueue *q) //判断队空
{
	return(q->front == q->rear);
}
								
bool QueueFull(SqQueue* q) //判断队满
{
	return (q->rear + 1) % M == q->front;
}

bool enQueue(SqQueue*& q, int e) //进队
{
	if ((q->rear + 1) % M == q->front) return false; //队满
	q->rear = (q->rear + 1) % M;
	q->CarNo[q->rear] = e;
	return true;
}

bool deQueue(SqQueue*& q, int& e) //出队
{
	if (q->front == q->rear) return false; //队空的情况
	q->front = (q->front + 1) % M;
	e = q->CarNo[q->front];
	return true;
}

void DispQueue(SqQueue* q) //显示队中元素
{
	int i = (q->front + 1) % M;
	printf("%d ", q->CarNo[i]);
	while ((q->rear - i + M) % M > 0)
	{
		i = (i + 1) % M;
		printf("%d ", q->CarNo[i]);
	}
	printf("\n");
}
		
int main()
{
	int comm, i, j;
	int no, e1, time, e2;
	SqStack* St, * St1;
	SqQueue* Qu;
	InitStack(St);
	InitStack(St1);
	InitQueue(Qu);
	do
	{
		printf(">输人指令(1:到达 2:离开 3:停车场 4:候车场 0:退出):");
		scanf("%d", &comm);
		switch (comm)
		{
		case 1: //汽车到达
			printf(" 车号 到达时间:");
			scanf("%d%d", &no, &time);
			if (!StackFull(St)) //停车场不满
			{
				Push(St, no, time);
				printf(" 停车场位置:%d\n", St->top + 1);
			}
			else //停车场满
			{
				if (!QueueFull(Qu)) //侯车场不满
				{
					enQueue(Qu, no);
					printf(" 候车场位置:%d\n", Qu->rear);
				}
				else printf(" 候车场已满,不能停车\n");
			}
			break;
		case 2:
			printf(" 车号 离开时间:");
			scanf("%d %d", &no, &time);
			for (i = 0; i <= St->top && St->CarNo[i] != no; i++);
			if (i > St->top) printf(" 未找到该编号的汽车\n");
			else
			{
				for (j = i; j <= St->top; j++)
				{
					Pop(St, e1, e2);
					Push(St1, e1, e2); //倒车到临时栈S1中
				}
				Pop(St, e1, e2);//该汽车离开
				printf(" %d汽车停车费用:%d\n", no, (time - e2) * Price);
				while (!StackEmpty(St1)) //将临时栈St1重新回到St中
				{
					Pop(St1, e1, e2);
					Push(St, e1, e2);
				}
				if (!QueueEmpty(Qu)) //队不空时, 将队头进栈St
				{
					deQueue(Qu, e1);
					Push(St, e1, time); //以当前时间开始计费
				}
			}
			break;
		case 3: //显示停车场情况
			if (!StackEmpty(St))
			{
				printf(" 停车场中的车辆:"); //输出停车场中的车辆
				DispStack(St);
			}
			else printf(" 停车场中无车辆\n");
			break;
		case 4: //显示候车场情况
			if (!QueueEmpty(Qu))
			{
				printf(" 候车场中的车辆:"); //输出候车场中的车辆
				DispQueue(Qu);
			}
			else printf(" 候车场中无车辆\n");
			break;
		case 0: //结束
			if (!StackEmpty(St))
			{
				printf(" 停车场中的车辆:"); //输出停车场中的车辆
				DispStack(St);
			}
			if (!QueueEmpty(Qu))
			{
				printf(" 候车场中的车辆:"); //输出候车场中的车辆
				DispQueue(Qu);
			}
			break;
		default: //其他情况
			printf(" 输入的命令错误\n");
			break;
		}
	} while (comm != 0);
		return 0;
}

程序分析

  • 运行结果:

5. 某汽车轮渡口,过江渡船每次能载10辆车过江。过江车辆分为客车类和货车类,上渡船有如下规定:同类车先到先上船;客年先于货车上船,且每上4辆客车,才允许放上1辆货车;若等待客车不足4辆,则以货车代替;若无车等待,允许客车都上船。试设计一个算法模拟渡口管理。

  • 算法思想:假设数组q的最大下标为10,恰好是每次载渡的最大量。假设客车的队列为q1,货车的队列为g2。若q1充足,则每取4个q1元素后再取一个q2元素,直到q的长度为10。若q1不充足,则直接用q2补齐。
Queue q; //过江渡船载渡队列
Queue q1; //客车队列
Queue q2; //货车队列

void manager()
{
	int i = 0, j = 0; //j表示渡船上的总车辆数
	while (j < 10) //不足10辆时
	{
		if (!QueueEmpty(q1) && i < 4) //客车队列不空,则未上足4辆
		{
			DeQueue(q1, x); //从客车队列出队
			EnQueue(q, x); //客车上渡船
			i++; //客车数加1
			j++; //渡船上的总车辆数加
		}
		else if (i == 4 && !QueueEmpty(q2)
		{

			DeQueue(q2, x); //客车已上足4辆
			EnQueue(q, x); //从货车队列出队
			j++; //渡船上的总车辆数加1
			i = 0; //每上辆货车,i重新计数	
		}
		else //其他情况(客车队列空或货车队列空)
		{
			while (j < 10 && i < 4 && !QueueEmpty(q2) //客车队列空
			{
				DeQueue(q2, x); //从货车队列出队
				EnQueue(a, x); //货车上渡船
				i++; //i计数, 当i > 4时, 退出本循环
				j++; //渡船上的总车辆数加1
			}
			i = 0;
		}
		if (QueueEmpty(q1) && QueueEmpty(q2))
			j = 11; //若货车和客车加起来不足10辆
	}
}