【C】【Queue】双向链表队列的模拟营业额计算

一、模拟小摊的营业情况

(模拟方面并不专业,只是一个思路)

//双向链队列的实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct {
	int wait_time;//等待时间
	int n;//客人序号
}Item;

typedef struct node {
	Item item;
	struct node* next;//指向下一个指针
	struct node* piror;//指向前一个指针
}Node;

typedef struct {
	Node* rear;//队尾指针
	Node* front;//队首指针
	int node_num;//节点数量
}Queue;

bool InitQueue(Queue* pq);//初始化队列
bool QueueIsEmpty(Queue* pq);//判断队列是否为空
bool QueueIsFull(Queue* pq);//判断队列是否已满
bool InQueue(Queue* pq);//入队
bool OutQueue(Queue* pq);//队首出队
bool FreeQueue(Queue* pq);//释放队列
bool CanNotWait(Queue* pq, Node** pscan);//删除一个节点
void ShowQueue(Queue* pq);//展现队列
void CalculateBudget(Queue* pq);//计算预算

bool InitQueue(Queue* pq)
{
	pq->rear = (Node*)malloc(sizeof(Node));
	pq->front = (Node*)malloc(sizeof(Node));
	pq->node_num = 0;
	pq->rear = pq->front;//为队空创造条件

	if (pq->rear == NULL)
		return false;

	if (pq->front == NULL)
		return false;

	return true;
}

bool QueueIsEmpty(Queue* pq)
{
	//队尾就是队头时,说明队空
	if (pq->rear == pq->front)
		return true;
	else return false;
}

bool QueueIsFull(Queue* pq)
{
	Node* pnew;
	pnew = (Node*)malloc(sizeof(Node));
	if (pnew == NULL)
	{
		free(pnew);
		return true;
	}
	else
	{
		free(pnew);
		return false;
	}
}

bool InQueue(Queue* pq)
{
	static n = 1;
	Node* pnew;
	pnew = (Node*)malloc(sizeof(Node));
	if (pnew == NULL)
		return false;
	pnew->item.wait_time = 0;//客人等待时间初始化为0
	pnew->item.n = n++;//给客人标上序号

	if (QueueIsFull(pq) == true)
	{
		printf("队列已满,无法入队!\n");
		return false;
	}
	else if (QueueIsEmpty(pq) == true)
	{
		pq->front->piror = NULL;//队首前面不会排节点
		pq->front->next = pnew;//队首之后是新节点
		pnew->piror = pq->front;//把新节点排在队首后面,必须在前两条之后实现
		pnew->next = NULL;//新节点后面没有节点
		pq->rear = pnew;//新节点作为队尾,必须最后实现
	}
	else
	{
		pq->rear->next = pnew;
		pnew->piror = pq->rear;
		pnew->next = NULL;
		pq->rear = pnew;
	}

	pq->node_num++;//节点数量增加
	return true;
}

bool OutQueue(Queue* pq)
{
	Node* ptemp;

	if (QueueIsEmpty(pq) == true)
	{
		printf("队列为空!出队失败!\n");
		return false;
	}
	else
	{
		ptemp = pq->front;
		pq->front = pq->front->next;
		free(ptemp);
		pq->node_num--;//节点数量减少
		return true;
	}
}

bool FreeQueue(Queue* pq)
{
	Node* psave;

	while (QueueIsEmpty(pq) == false)
	{
		psave = pq->front;
		pq->front = pq->front->next;
		free(psave);
	}
	return true;
}

void ShowQueue(Queue* pq)
{
	Node* pscan;

	if (QueueIsEmpty(pq) == true)
		printf("队列为空!\n");
	else
	{
		//从队头向后遍历
		pscan = pq->front->next;//队头本身是没有内容的(Item 没有初始化)
		while (pscan != NULL)//rear->next = NULL,rear有内容
		{
			printf("客人%d:等待时间%d\n", pscan->item.n, pscan->item.wait_time);
			pscan = pscan->next;
		}

		putchar('\n');

		//从队尾向前遍历
		//pscan = pq->rear;
		//while (pscan != pq->front)//front没内容,且不为空指针
		//{
		//	printf("%d ", pscan->item.wait_time);
		//	pscan = pscan->piror;
		//}
	}
}

bool CanNotWait(Queue* pq, Node** pscan)
{
	Node* ptemp_piror;
	Node* ptemp_next;
	bool flag1 = false;
	bool flag2 = false;

	ptemp_piror = (Node*)malloc(sizeof(Node));
	ptemp_next = (Node*)malloc(sizeof(Node));
	if (ptemp_piror == NULL)
		return false;
	if (ptemp_next == NULL)
		return false;

	if ((*pscan)->piror != pq->front)
	{
		ptemp_piror = (*pscan)->piror;
		flag1 = true;
	}
	if ((*pscan)->next != NULL)
	{
		ptemp_next = (*pscan)->next;
		flag2 = true;
	}

	//pscan当前指向的节点,前面不是队头,本身不是队尾才实现
	//通过flag标记判断
	if (flag1 == true && flag2 == true)
	{
		ptemp_piror->next = ptemp_next;
		ptemp_next->piror = ptemp_piror;
		(*pscan) = ptemp_next;//pscan地址向后更新
		pq->node_num--;
		return true;
	}

	//如果psacn当前指向队尾
	if ((*pscan)->next == NULL)
	{
		pq->rear = (*pscan)->piror;//pscan前面的节点变成队尾
		pq->rear->next = NULL;//队尾节点后面一定是NULL
		(*pscan) = pq->rear->next;//因为已经遍历完了,让pscan指向NULL
		pq->node_num--;//节点数量减1
		return true;
	}

	return false;
}


//营业额收入预估程序
//摊位每次接待一名顾客,队伍最多排5人,平均每3分钟来1位新顾客
//摊位每次接待需要10分钟,顾客平均等待15分钟就会离队
//每天的摊位费是500元,每次接待的纯利润是5元
//客流量达到多少才能不亏本,每天营业多久才能不亏本,有多少顾客因为队满未入队,有多少顾客因等待过久而离队
void CalculateBudget(Queue* pq)
{
	int open_time = 0;//开张时间
	int new_guest_time = 3;//每3分钟来一个新客
	int serve_time = 5;//招待一个顾客需要8分钟
	int all_guest = 0;//来过店铺的顾客量
	int max_queuq_value = 5;//最大队伍长度
	double profit = 5;//平均接待每个顾客的盈利
	double gross_profit = 0;//总盈利额
	double cost_price = 500;//成本额
	int max_wait_time = 15;//极限等待时间
	int queue_full = 0;//因队满而离开
	int can_not_wait = 0;//因等待过久而离开
	Node* pscan;//遍历用节点指针

	int flag = 1;//用于记录招待的时间

	while (gross_profit < cost_price)
	{

		if (QueueIsEmpty(pq) == false)
		{
			if (flag == 1)
			{
				printf("开始招待顾客!\n");
				flag++;
				pq->front->next->item.wait_time = 0;//正在被招待的顾客不会因为等不及而离队
			}
			else if (flag < serve_time)//还没招待完
			{
				printf("正在招待顾客!\n");
				flag++;
				pq->front->next->item.wait_time = 0;
			}
			else
			{
				OutQueue(pq);
				gross_profit += profit;
				printf("招待完一名顾客!\n");
				flag = 1;
			}
		}

		if (QueueIsEmpty(pq) == false)
		{
			pscan = pq->front->next;
			while (pscan != NULL)
			{
				pscan->item.wait_time++;//用遍历让每一位在队列中的顾客的等待时间+1
				if (pscan->item.wait_time >= max_wait_time)//超过等待时间离队
				{
					if (CanNotWait(pq, &pscan) == true)//pscan经过此函数会变化
					{
						can_not_wait++;//因等不及而离队的顾客+1
						printf("顾客等不及,出队!\n");
						continue;//跳回循环
					}
				}
				pscan = pscan->next;//没有离队情况下,自动向后遍历
			}
		}

		if (open_time % new_guest_time == 0)//达到要求来新顾客
		{
			printf("来了一位新顾客!\n");
			if (pq->node_num == max_queuq_value)
			{
				queue_full++;
				printf("因为队满而离开了!\n");
			}
			else
			{
				InQueue(pq);
				printf("成功进队!\n");
			}

			all_guest++;
		}
		else
			printf("没有新顾客光临!\n");

		open_time++;
		printf("已经赚了%lf元\n", gross_profit);
		ShowQueue(pq);
	}

	printf("open_time = %d\n", open_time);
	printf("all_guest = %d\n", all_guest);
	printf("queue_full = %d\n", queue_full);
	printf("can_not_wait = %d\n", can_not_wait);
}

int main()
{
	Queue Q;

	if (InitQueue(&Q) == true)
		printf("程序运行……\n");
	else
		exit(1);

	CalculateBudget(&Q);
	FreeQueue(&Q);

	return 0;
}

二、CanNot Wait()函数参数列表中使用Node** pscan的原因

1. 如果直接将指针作为实参传递

#include<stdio.h>

void fun(int* p)
{
    int b = 2;
    p = &b;
}

int main(void)
{
    int a = 1;
    int* p;

    p = &a;
    printf("p = %p\n", p);
    printf("*p = %d\n", *p);
    
    fun(p);
    printf("p = %p\n", p);
    printf("*p = %d\n", *p);

    return 0;
}

结果如下

p = 000000000061FE14
*p = 1
p = 000000000061FE14
*p = 1

发现指针p不论是指向的地址还是指向的均未改变

这是因为,和所有变量一样,指针变量也有自己的地址
当指针指向某一变量时,指针的值就是这个变量的地址

C Primer Plus P255 - 10.5 指针操作 - 取址

说明将指针p这个变量直接作为实参传递时,就会像普通的变量传递那样

实际参数是具体的值,该值要被赋给作为形式参数的变量。因为被调函数使用的是从主调函数中拷贝而来,所以无论被调函数对拷贝函数进行什么操作,都不会影响主调函数中的原始数据

C Primer Plus P214 - 9.1.6 调用带实际参数的函数

因此我们可以采用,引用类型指向指针的指针来达到想要的效果

2. 如果将指针的地址作为实参传递(即使用指向指针的指针)

#include<stdio.h>
#include<string.h>

void fun(int** p)
{
    int b = 2;
    *p = &b;
}

int main(void)
{
    int a = 1;
    int* p;

    p = &a;
    printf("p = %p\n", p);
    printf("*p = %d\n", *p);
    fun(&p);
    printf("p = %p\n", p);
    printf("*p = %d\n", *p);

    return 0;
}

结果如下

p = 000000000061FE1C
*p = 1
p = 000000000061FDDC
*p = 0

发现指针p地址改变了,但地址中存放的不是我们想要的2
(其实地址改变了,也不是我们想要的地址,只是一个随机分配的地址)

这是因为变量b作用域只在fun()函数当中,离开后便不可用

C Primer Plus P321 - 12.1.1 作用域

变量b定义为static类型便可正确传递变量

static int b = 2;
p = 000000000061FE1C
*p = 1
p = 0000000000403010
*p = 2

三、程序设计的导图

请添加图片描述

四、【C】【Queue】双向链表的队列实现

【C】【Queue】双向链表的队列实现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值