以客房管理系统为例实现链表的基本操作(C语言)
个人体验
敲完这段数据结构作业的代码后,我加深了对指针赋值操作的理解;在减少时间复杂度的目的下放弃了冒泡排序,第一次实现了插入排序;学会了新方法来寻找链表中点,以减少时间复杂度。
需求
(1)实现创建客房信息链表函数void Build(HLink &H),输入(客房名称、标准价格、床位数),同时修改入住价格、入住状态为默认值,即入住价格=标准价格*80%,入住状态为”空闲”(提示:用strcpy()字符串拷贝函数)。为了提高程序调试效率,要求:必须用文件操作来输入客房信息(客房名称、标准价格、床位数);
(2)实现输出客房信息函数void Exp(HLink H),输出所有客房的客房名称、标准价格、入住价格、床位数、入住状态;
(3)函数int Find(HLink &H, char *roomN),查找房间名称为roomN的客房。如果找到,则返回该客房在链表中的位置序号(>=1),否则返回0。提示:用strcmp()字符串比较函数;
(4)实现函数void updateH(HLink &H, int beds, char *state),将床位数为beds的客房入住状态改为state。提示:用strcpy()字符串拷贝函数;
(5)函数void Add(HLink &H),将该链表中未入住的客房入住价格均加价20%;
(6)求出入住价格最高的客房函数HLink FirstH(HLink &H),该函数内return语句返回入住价格最高的客房结点指针,返回前将该结点在链表中删除;
(7)函数void MoveK1(HLink &H, int k),将单链表中倒数第k个结点移到第一个结点位置,注意:严禁采用先计算链表长度n再减k(即n-k)的方法;
(8)函数void ReverseN2(HLink &H),将单链表的正中间位置结点之后的全部结点倒置的功能,注意:严禁采用先计算链表长度n再除以2(即n/2)的方法;
(9)函数void SortPriceL(HLink &H),按照客房(入住价格,客房名称)升序排序;
(10)函数void upBed(HLink &H,int beds),创建一个【床位数为beds的新结点】(还需输入:客房名称、标准价格等信息),使链表的形态为:【头结点】->【床位数>beds的结点】->【床位数为beds的新结点】->【床位数<=beds的结点】,要求【超过beds的结点】和【不超过beds的结点】这两段链表中的结点保持原来的前后相对顺序;
(11)主函数main()调用以上函数,(3)若返回值>=1则输出该客房在链表中的位置序号,否则输出该客房不存在;输出(4)~(10)处理后的链表内容,其中(6)还要输出入住价格最高的客房信息。
代码演示
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//定义客房链表结点结构
typedef struct HNode
{
char roomN[7]; //客房名称
float Price; //标准价格
float PriceL; //入住价格(默认值=标准价格*80%)
int Beds; //床位数Beds
char State[5]; //入住状态(值域:"空闲"、"入住"、"预订",默认值为"空闲")
struct HNode *next; //指针域
}Hotel, *HLink;
int main()
{ //函数声明
void Build(HLink &H); //*创建客房信息链表
void Exp(HLink H); //*输出客房信息
int Find(HLink &H, char *roomN); //*查找房间名称为roomN的客房
void updateH(HLink &H, int beds, char *state); //*将床位数为beds的客房入住状态改为state
void Add(HLink &H); //*将链表中未入住的客房入住价格均加价20%
HLink FirstH(HLink &H); //*返回入住价格最高的客房结点指针,返回前将该结点在链表中删除
void MoveK1(HLink &H, int k); //*将单链表中倒数第k个结点移到第一个结点位置
void ReverseN2(HLink &H); //*将单链表的正中间位置结点之后的全部结点倒置
void SortPriceL(HLink &H); //*按照客房(入住价格,客房名称)升序排序;前者优先级高于后者
void upBed(HLink &H, int beds); //*创建一个【床位数为beds的新结点】;【头结点】->【床位数>beds的结点】->【床位数为beds的新结点】->【床位数<=beds的结点】
//函数声明
HLink H;
Build(H);
{ //(3)
int result;
char roomN[7] = "8201"; //查找房间名为8201的位置序号
printf("(3)\n");
result = Find(H, roomN);
if (result == 0) {
printf("该客房不存在\n");
}
else if (result >= 1) {
printf("房间在链表中的位置序号:%d\n", result);
}
else
printf("程序错误!\n");
}
printf("\n客房信息(客房名称、标准价格、入住价格、床位数、入住状态)输出如下:\n");
printf("--------------------------------------------------------------------\n");
printf("客房名称\t标准价格\t入住价格\t床位数\t入住状态\n");
printf("--------------------------------------------------------------------\n");
{ //(4)
char State[5]="入住";
int beds = 2;
printf("(4)\n");
printf("将床位数为 2 的客房入住状态改为“入住”\n");
updateH(H,beds,State);//将床位数为 2 的客房入住状态改为“入住”
Exp(H);
}
{ //(5)
printf("(5)\n");
printf("未入住的客房入住价格均加价20%\n");
Add(H); //未入住的客房入住价格均加价20%
Exp(H);
}
{ //(6)
HLink max = (HLink)malloc(sizeof(HNode));
max->next= FirstH(H);//获取入住价格最高的客房对应的指针
printf("(6)\n");
printf("入住价格最高的客房:\n");
Exp(max);
printf("删除最高价客房后的链表:\n");
Exp(H);
}
{ //(7)
int last = 3;
printf("(7)\n");
printf("将倒数第3个结点移到第一个结点位置\n");
MoveK1(H,last); //倒数第3个结点移到第一个结点位置
Exp(H);
}
{ //(8)
printf("(8)\n");
ReverseN2(H); //将单链表的正中间位置结点之后的全部结点倒置
Exp(H);
}
{ //(9)
printf("(9)\n");
printf("按照客房(入住价格,客房名称)升序排序;前者优先级高于后者\n");
SortPriceL(H); //按照客房(入住价格,客房名称)升序排序;前者优先级高于后者
Exp(H);
}
{ //(10)
int beds = 7;
printf("(10)\n");
printf("添加一个床位数为7的客房,客房的其他信息通过文件addata.txt输入或自动补全\n");
upBed(H,beds); //添加一个床位数为7的客房,客房的其他信息通过文件addata.txt输入或自动补全
Exp(H);
}
system("pause");
return 0;
}
void Build(HLink &H)
{ //构造一个链表并读取文件的数据
Hotel* rear;
Hotel* p;
FILE *infile;
infile = fopen("indata.txt", "r"); //打开文本文件"indata.txt"
H = (HLink)malloc(sizeof(HNode));
rear = H;
//依次从文本文件中读取结点数据(客房名称、标准价格、床位数)
while (!feof(infile)) //判断是否读取到文件结尾
{
p = (HLink)malloc(sizeof(HNode));
fscanf(infile, "%s%f%d\n", p->roomN, &p->Price, &p->Beds);//从文件中逐行读取客房名称、标准价格、床位数
p->PriceL = p->Price*0.8;
strcpy(p->State, "空闲");
rear->next = p;
rear = p;
}
rear->next = NULL;
fclose(infile);
}
void Exp(HLink L) //遍历链表L,输出从首元结点到尾结点储存的所有客房信息
{
HLink p;
p = L->next;
while (p) {
printf("%-16s", p->roomN);
printf("%-16f", p->Price);
printf("%-16f", p->PriceL);
printf("%-8d", p->Beds);
printf("%-s\n", p->State);
p = p->next;
}
}
int Find(HLink &H, char*roomN)
{
int n = 0;
HLink p;
p = H->next;
while (p)
{
n++;
if (strcmp(p->roomN, roomN)) //若p->room和 roomN一样,则strcmp函数返回0,否则返回非0
{
p = p->next;
if (!p)
{
n = 0;
break;
}
}
else //若p->room和 roomN一样,则挑出循环,返回n后结束函数运算
break;
}
return n;
}
void updateH(HLink &H, int beds, char *state)
{
HLink p;
p = H->next;
while (p) //遍历链表H
{
if (p->Beds == beds) //若找到既定的床位数,则把p->state改为既定的字符串state
{
strcpy(p->State, state);
}
p = p->next;
}
}
void Add(HLink &H)
{
HLink p;
p = H->next;
while (p) //遍历链表H
{
if (strcmp(p->State, "入住")) //若找到p->state值为“入住”时,p->PriceL的值增加20%
{
p->PriceL = p->PriceL*1.2;
}
p = p->next;
}
}
HLink FirstH(HLink &H)
{
HLink max, p, pre, maxpre; //pre是p的前驱,maxpre是max的前驱
max = H->next;
maxpre = max;
p = H->next;
pre = p;
while (p) //用指针变量p遍历链表H
{
if (p->PriceL > max->PriceL) //用指针变量max存储最大结点位置
{
max = p;
maxpre = pre;
}
pre = p;
p = p->next;
}
maxpre->next = max->next;
max->next = NULL; //将最大值对应的结点从链表H中独立出来
return max;
}
void MoveK1(HLink &H, int k)
{//默认题中所说的第一个结点位置是首元结点位置
HLink p = H; //初始定义p是指向链表H中头结点(而不是首元结点)的指针
HLink pend = p; //定义一个终将指向单链表的末端节点的指针
HLink first, pre = p;
while (p && (k - 1)) //找到第k个节点,并把该节点的指针赋值给pend
{
if (!(pend->next))
{
printf("k大于单链表长度,产生溢出!\n");
exit(0);
}
pend = pend->next;
k--;
}
while (pend->next)//将p和pend两个指针沿着链表以相同的速度向后遍历,直到pend指向链表的末端结点
{
pre = p;
p = p->next;
pend = pend->next;
} //此时p指向单链表中倒数第k个结点
first = H->next;
H->next = p;
pre->next = p->next;
p->next = first->next;//将p移到第一个结点位置
}
void ReverseN2(HLink &H)
{
printf("将单链表的正中间位置结点之后的全部结点倒置: \n");
HLink slow, fast, middle;
fast = H;
for (slow = H; fast&&fast->next;) { //fast指针移动速度是slow指针的两倍,因此当fast移动到链表末时slow恰好移动到正中间
fast = fast->next->next; //当fast=NULL时,除头结点外链表结点数为单数,slow指针恰好在正中间位置
slow = slow->next; //当fast->next=NULL时,结点数为偶数,slow指针恰好在前半部分的最后一个结点
}
middle = slow;
HLink half = (HLink)malloc(sizeof(HNode));
half->next = NULL;
for (; middle->next; middle = middle->next)//前插法构造后半部分倒序链表,表头结点是half
{
HLink p = (HLink)malloc(sizeof(HNode));
p->Beds = middle->next->Beds;
p->Price = middle->next->Price;
p->PriceL = middle->next->PriceL;
strcpy(p->roomN, middle->next->roomN);
strcpy(p->State, middle->next->State);
p->next = half->next;
half->next = p;
}
slow->next = half->next;//将原结点的前半部分和新生成的half拼接在一起
}
void SortPriceL(HLink &H) //使用插入排序
{
HLink sorted, choose, aft;
sorted = H;
choose = sorted->next->next;
sorted->next->next = NULL;
while (choose) {//若“sorted指针已经遍历到尾结点或者sorted后一个指针存储的信息大于等于choose存储的信息”那么choose结点插入到sorted之后
if (!sorted->next ||//“sorted存储的信息大于choose”指的是入住价格大或者在入住价格一样时房间号大
((choose->PriceL < sorted->next->PriceL) ||
((choose->PriceL == sorted->next->PriceL) && (strcmp(choose->roomN, sorted->next->roomN) < 0)))) {
aft = choose->next;
choose->next = sorted->next;
sorted->next = choose;
choose = aft;
sorted = H;
}
else {
sorted = sorted->next;
}
}
}
void upBed(HLink &H, int beds) //客房名称、标准价格通过文件addata.txt输入
{
HLink up = (HLink)malloc(sizeof(HNode));
HLink L = (HLink)malloc(sizeof(HNode));
HLink p, rear, N;
{ //该程序块的功能是从addata.txt文件中读取客房名称、标准价格,再结合函数输入的床位数,把其余信息按同样的规则补齐,存储在L中
Hotel* p;
FILE *infile;
infile = fopen("addata.txt", "r"); //打开文本文件"addata.txt"
rear = L; //文件中的数据存储在链表L中
//依次从文本文件中读取结点数据(客房名称、标准价格)
while (!feof(infile)) //判断是否读取到文件结尾
{
p = (HLink)malloc(sizeof(HNode));
fscanf(infile, "%s%f\n", p->roomN, &p->Price);//从文件中逐行读取客房名称、标准价格
p->PriceL = p->Price*0.8;
strcpy(p->State, "空闲");
p->Beds = beds;
rear->next = p;
rear = p;
}
rear->next = NULL;
fclose(infile);
}
N = up;
for (p = H->next; p; p = p->next) {//对于已有的链表H,床位数大于beds的结点存储在链表up中,小于或等于beds的结点按原来的顺序接在链表L之后
if (p->Beds > beds) {
N->next = p;
N = N->next;
}
else if (p->Beds <= beds) {
rear->next = p;
rear = rear->next;
}
}
rear->next = NULL;
N->next = L->next; //链表L从首元结点到尾结点接在链表up之后
H = up; //链表up的头指针传给变量H
}
用于调试的两个文本文档
indata.txt
8101 100 1
8102 100 4
8103 150 2
8201 200 1
8202 200 1
8203 200 1
8204 200 1
8205 200 5
8206 2 1
8207 200 1
8208 200 3
8209 200 1
addata.txt
8301 800