mirror of
https://github.com/kangjianwei/Data-Structure.git
synced 2026-09-24 00:20:18 +08:00
💡 栈和队列源码重构
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(SqStack SqStack.h SqStack.c SqStack-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(SqStack Scanf_lib)
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
// 测试函数,打印元素
|
||||
void PrintElem(SElemType e);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SqStack S;
|
||||
int i;
|
||||
SElemType e;
|
||||
|
||||
printf("████████ 函数 InitStack \n");
|
||||
{
|
||||
printf("█ 初始化顺序栈 S ...\n");
|
||||
InitStack(&S);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackEmpty \n");
|
||||
{
|
||||
StackEmpty(S) ? printf("█ S 为空!!\n") : printf("█ S 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 Push \n");
|
||||
{
|
||||
for(i = 1; i <= 6; i++) {
|
||||
Push(&S, 2 * i);
|
||||
printf("█ 将 \"%2d\" 压入栈 S ...\n", 2 * i);
|
||||
}
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackTraverse \n");
|
||||
{
|
||||
printf("█ S 中的元素为:S = ");
|
||||
StackTraverse(S, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackLength \n");
|
||||
{
|
||||
i = StackLength(S);
|
||||
printf("█ S 的长度为 %d \n", i);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 Pop \n");
|
||||
{
|
||||
Pop(&S, &e);
|
||||
printf("█ 栈顶元素 \"%d\" 出栈...\n", e);
|
||||
printf("█ S 中的元素为:S = ");
|
||||
StackTraverse(S, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 GetTop \n");
|
||||
{
|
||||
GetTop(S, &e);
|
||||
printf("█ 栈顶元素的值为 \"%d\" \n", e);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 ClearStack \n");
|
||||
{
|
||||
printf("█ 清空 S 前:");
|
||||
StackEmpty(S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
|
||||
|
||||
ClearStack(&S);
|
||||
|
||||
printf("█ 清空 S 后:");
|
||||
StackEmpty(S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DestroyStack \n");
|
||||
{
|
||||
printf("█ 销毁 S 前:");
|
||||
S.base != NULL && S.top != NULL ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
|
||||
|
||||
DestroyStack(&S);
|
||||
|
||||
printf("█ 销毁 S 后:");
|
||||
S.base != NULL && S.top != NULL ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 测试函数,打印元素
|
||||
void PrintElem(SElemType e) {
|
||||
printf("%d ", e);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*=========================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
==========================*/
|
||||
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).base = (SElemType*) malloc(STACK_INIT_SIZE * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
(*S).stacksize = STACK_INIT_SIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放顺序栈所占内存。
|
||||
*/
|
||||
Status DestroyStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
free((*S).base);
|
||||
|
||||
(*S).base = NULL;
|
||||
(*S).top = NULL;
|
||||
(*S).stacksize = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理顺序栈中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status ClearStack(SqStack* S) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S) {
|
||||
if(S.top == S.base) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回顺序栈包含的有效元素的数量。
|
||||
*/
|
||||
int StackLength(SqStack S) {
|
||||
if(S.base == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) (S.top - S.base);
|
||||
}
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 返回栈顶元素,并用e接收。
|
||||
*/
|
||||
Status GetTop(SqStack S, SElemType* e) {
|
||||
if(S.base == NULL || S.top == S.base) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 不会改变栈中元素
|
||||
*e = *(S.top - 1);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 栈满时,追加存储空间
|
||||
if((*S).top - (*S).base >= (*S).stacksize) {
|
||||
(*S).base = (SElemType*) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW); // 存储分配失败
|
||||
}
|
||||
|
||||
(*S).top = (*S).base + (*S).stacksize;
|
||||
(*S).stacksize += STACKINCREMENT;
|
||||
}
|
||||
|
||||
// 进栈先赋值,栈顶指针再自增
|
||||
*(S->top++) = e;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*S).top == (*S).base) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出栈栈顶指针先递减,再赋值
|
||||
*e = *(--(*S).top);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问顺序栈S
|
||||
*/
|
||||
Status StackTraverse(SqStack S, void(Visit)(SElemType)) {
|
||||
SElemType* p = S.base;
|
||||
|
||||
if(S.base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
while(p < S.top) {
|
||||
Visit(*p++);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*=========================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
==========================*/
|
||||
|
||||
#ifndef SQSTACK_H
|
||||
#define SQSTACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define STACK_INIT_SIZE 100 // 顺序栈存储空间的初始分配量
|
||||
#define STACKINCREMENT 10 // 顺序栈存储空间的分配增量
|
||||
|
||||
/* 顺序栈元素类型定义 */
|
||||
typedef int SElemType;
|
||||
|
||||
// 顺序栈元素结构
|
||||
typedef struct {
|
||||
SElemType* base; // 栈底指针
|
||||
SElemType* top; // 栈顶指针
|
||||
int stacksize; // 当前已分配的存储空间,以元素为单位
|
||||
} SqStack;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放顺序栈所占内存。
|
||||
*/
|
||||
Status DestroyStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理顺序栈中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status ClearStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回顺序栈包含的有效元素的数量。
|
||||
*/
|
||||
int StackLength(SqStack S);
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 返回栈顶元素,并用e接收。
|
||||
*/
|
||||
Status GetTop(SqStack S, SElemType* e);
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e);
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e);
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问顺序栈S
|
||||
*/
|
||||
Status StackTraverse(SqStack S, void(Visit)(SElemType));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(Conversion SqStack.h SqStack.c Conversion.h Conversion.c Conversion-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(Conversion Scanf_lib)
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "Conversion.h" //**▲03 栈和队列**//
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int i = 342391;
|
||||
|
||||
printf("将十进制数转换为八进制数...\n");
|
||||
|
||||
conversion(i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*==============
|
||||
* 进制转换
|
||||
*
|
||||
* 包含算法: 3.1
|
||||
===============*/
|
||||
|
||||
#include "Conversion.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.1 ████████
|
||||
*
|
||||
* 进制转换:将指定的非负十进制整数,转换为八进制后输出。
|
||||
*
|
||||
*【注】
|
||||
* 教材使用的是控制台输入,这里为了便于测试,直接改为从形参接收参数
|
||||
*/
|
||||
void conversion(int i) {
|
||||
SqStack S;
|
||||
SElemType e;
|
||||
|
||||
InitStack(&S);
|
||||
|
||||
// 八进制数前面加0
|
||||
printf("十进制数 %d 转换为八进制数后为:0", i);
|
||||
|
||||
while(i!=0) {
|
||||
Push(&S, i % 8); // 进栈时从低位到高位
|
||||
i = i / 8;
|
||||
}
|
||||
|
||||
while(StackEmpty(S)==FALSE) {
|
||||
Pop(&S, &e); // 出栈时从高位到低位
|
||||
printf("%d", e);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*==============
|
||||
* 进制转换
|
||||
*
|
||||
* 包含算法: 3.1
|
||||
===============*/
|
||||
|
||||
#ifndef CONVERSION_H
|
||||
#define CONVERSION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.1 ████████
|
||||
*
|
||||
* 进制转换:将指定的非负十进制整数,转换为八进制后输出。
|
||||
*/
|
||||
void conversion(int i);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).base = (SElemType*) malloc(STACK_INIT_SIZE * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
(*S).stacksize = STACK_INIT_SIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S) {
|
||||
if(S.top == S.base) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 栈满时,追加存储空间
|
||||
if((*S).top - (*S).base >= (*S).stacksize) {
|
||||
(*S).base = (SElemType*) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW); // 存储分配失败
|
||||
}
|
||||
|
||||
(*S).top = (*S).base + (*S).stacksize;
|
||||
(*S).stacksize += STACKINCREMENT;
|
||||
}
|
||||
|
||||
// 进栈先赋值,栈顶指针再自增
|
||||
*(S->top++) = e;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*S).top == (*S).base) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出栈栈顶指针先递减,再赋值
|
||||
*e = *(--(*S).top);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#ifndef SQSTACK_H
|
||||
#define SQSTACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define STACK_INIT_SIZE 100 // 顺序栈存储空间的初始分配量
|
||||
#define STACKINCREMENT 10 // 顺序栈存储空间的分配增量
|
||||
|
||||
/* 顺序栈元素类型定义 */
|
||||
typedef int SElemType;
|
||||
|
||||
// 顺序栈元素结构
|
||||
typedef struct {
|
||||
SElemType* base; // 栈底指针
|
||||
SElemType* top; // 栈顶指针
|
||||
int stacksize; // 当前已分配的存储空间,以元素为单位
|
||||
} SqStack;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S);
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e);
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(LineEdit SqStack.h SqStack.c LineEdit.h LineEdit.c LineEdit-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(LineEdit Scanf_lib)
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
#include "LineEdit.h" //**▲03 栈和队列**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
char* buf = "whli##ilr#e(s#*s)\noutcha@ putchar(*s=#++);"; //需要录入的内容
|
||||
|
||||
printf("作为示范,用户输入的文本内容为:\n");
|
||||
printf("%s\n\n", buf);
|
||||
|
||||
printf("进入行编辑程序...\n\n");
|
||||
|
||||
printf("特殊符号:'#' 代表删除上一元素 '@' 代表删除当前输入行\n");
|
||||
printf(" '\\n'代表此行输入无误 '\\0'代表输入结束\n");
|
||||
printf("最终存储的内容为:\n");
|
||||
LineEdit(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*==============
|
||||
* 行编辑程序
|
||||
*
|
||||
* 包含算法: 3.2
|
||||
===============*/
|
||||
|
||||
#include "LineEdit.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.2 ████████
|
||||
*
|
||||
* 行编辑程序,模拟编辑文本时的退格与清空行的操作。
|
||||
*
|
||||
*【注】
|
||||
* 教材使用的是控制台输入,这里为了便于测试,直接改为从形参接收参数
|
||||
*/
|
||||
void LineEdit(const char buffer[]) {
|
||||
SqStack S; //接收输入的字符
|
||||
SElemType e;
|
||||
int i;
|
||||
char ch;
|
||||
|
||||
// 初始化栈
|
||||
InitStack(&S);
|
||||
|
||||
i = 0;
|
||||
ch = buffer[i++];
|
||||
|
||||
// 如果未达文本末尾
|
||||
while(ch != EOF) {
|
||||
// 如果未达文本末尾,且本行未结束(未遇到换行)
|
||||
while(ch != EOF && ch != '\n') {
|
||||
switch(ch) {
|
||||
case '#':
|
||||
Pop(&S, &e); // 遇到'#'表示删除一个字符
|
||||
break;
|
||||
case '@':
|
||||
ClearStack(&S); // 遇到'@'表示清空当前行
|
||||
break;
|
||||
default :
|
||||
Push(&S, ch); // 有效字符入栈
|
||||
}
|
||||
|
||||
// 识别下一个字符
|
||||
ch = buffer[i++];
|
||||
}
|
||||
|
||||
// 清空之前输出当前栈的内容,此处教材上没有
|
||||
StackTraverse(S, Print);
|
||||
|
||||
// 清空改行的缓冲区
|
||||
ClearStack(&S);
|
||||
|
||||
// 如果未到文本末尾,说明遇到了'\n',即该行结束了
|
||||
if(ch != EOF) {
|
||||
// 进入下一行
|
||||
ch = buffer[i++];
|
||||
}
|
||||
}
|
||||
|
||||
// 已经到了文本末尾,输出目前栈中的元素,此处教材上没有
|
||||
StackTraverse(S, Print);
|
||||
|
||||
// 销毁栈
|
||||
DestroyStack(&S);
|
||||
}
|
||||
|
||||
// 测试函数,打印元素
|
||||
void Print(SElemType e) {
|
||||
printf("%c", e);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*==============
|
||||
* 行编辑程序
|
||||
*
|
||||
* 包含算法: 3.2
|
||||
===============*/
|
||||
|
||||
#ifndef LINEEDIT_H
|
||||
#define LINEEDIT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
#include "LineEdit.h"
|
||||
|
||||
// 模拟文件中的文本结束标记,需要覆盖已有的定义
|
||||
#ifdef EOF
|
||||
#undef EOF
|
||||
#define EOF '\0'
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ████████ 算法3.2 ████████
|
||||
*
|
||||
* 行编辑程序,模拟编辑文本时的退格与清空行的操作。
|
||||
*
|
||||
*【注】
|
||||
* 教材使用的是控制台输入,这里为了便于测试,直接改为从形参接收参数
|
||||
*/
|
||||
void LineEdit(const char buffer[]);
|
||||
|
||||
// 测试函数,打印元素
|
||||
void Print(SElemType e);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).base = (SElemType*) malloc(STACK_INIT_SIZE * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
(*S).stacksize = STACK_INIT_SIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放顺序栈所占内存。
|
||||
*/
|
||||
Status DestroyStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
free((*S).base);
|
||||
|
||||
(*S).base = NULL;
|
||||
(*S).top = NULL;
|
||||
(*S).stacksize = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理顺序栈中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status ClearStack(SqStack* S) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 栈满时,追加存储空间
|
||||
if((*S).top - (*S).base >= (*S).stacksize) {
|
||||
(*S).base = (SElemType*) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW); // 存储分配失败
|
||||
}
|
||||
|
||||
(*S).top = (*S).base + (*S).stacksize;
|
||||
(*S).stacksize += STACKINCREMENT;
|
||||
}
|
||||
|
||||
// 进栈先赋值,栈顶指针再自增
|
||||
*(S->top++) = e;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*S).top == (*S).base) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出栈栈顶指针先递减,再赋值
|
||||
*e = *(--(*S).top);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问顺序栈S
|
||||
*/
|
||||
Status StackTraverse(SqStack S, void(Visit)(SElemType)) {
|
||||
SElemType* p = S.base;
|
||||
|
||||
if(S.base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
while(p < S.top) {
|
||||
Visit(*p++);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#ifndef SQSTACK_H
|
||||
#define SQSTACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define STACK_INIT_SIZE 100 // 顺序栈存储空间的初始分配量
|
||||
#define STACKINCREMENT 10 // 顺序栈存储空间的分配增量
|
||||
|
||||
/* 顺序栈元素类型定义 */
|
||||
typedef int SElemType;
|
||||
|
||||
// 顺序栈元素结构
|
||||
typedef struct {
|
||||
SElemType* base; // 栈底指针
|
||||
SElemType* top; // 栈顶指针
|
||||
int stacksize; // 当前已分配的存储空间,以元素为单位
|
||||
} SqStack;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放顺序栈所占内存。
|
||||
*/
|
||||
Status DestroyStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理顺序栈中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status ClearStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e);
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e);
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问顺序栈S
|
||||
*/
|
||||
Status StackTraverse(SqStack S, void(Visit)(SElemType));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(Maze SqStack.h SqStack.c Maze.h Maze.c Maze-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(Maze Scanf_lib)
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "Maze.h" //**▲03 栈和队列**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
MazeType maze;
|
||||
PosType start, end;
|
||||
char n, Re = 'Y';
|
||||
|
||||
while(Re == 'Y' || Re == 'y') {
|
||||
InitMaze(maze, &start, &end); // 初始化迷宫,包括出入口
|
||||
|
||||
MazePath(maze, start, end); // 迷宫寻路
|
||||
|
||||
printf("重置?(Y/N):");
|
||||
scanf("%c%c", &Re, &n);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
/*==============
|
||||
* 迷宫寻路程序
|
||||
*
|
||||
* 包含算法: 3.3
|
||||
===============*/
|
||||
|
||||
#ifndef MAZE_C
|
||||
#define MAZE_C
|
||||
|
||||
#include "Maze.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.3 ████████
|
||||
*
|
||||
* 迷宫寻路
|
||||
*
|
||||
* 使用穷举法,找到一条可行通路即返回
|
||||
*/
|
||||
Status MazePath(MazeType maze, PosType start, PosType end) {
|
||||
SqStack S; // 存储探索过的通道块
|
||||
SElemType e; // e存储当前通道块信息
|
||||
PosType curPos; // 当前位置
|
||||
int curStep; // 当前通道块序号
|
||||
|
||||
// 初始化轨迹栈
|
||||
InitStack(&S);
|
||||
|
||||
curPos = start; // 设定当前位置为"出口位置"
|
||||
curStep = 1; // 探索第一步
|
||||
|
||||
do {
|
||||
// 如果当前位置可通过(要求改位置是从未曾探索的通道块)
|
||||
if(Pass(maze, curPos)) {
|
||||
// 留下初始足迹,即留下向东访问的标记
|
||||
FootPrint(maze, curPos);
|
||||
|
||||
// 构造一个通道块信息并返回
|
||||
e = Construct(curStep, curPos, East);
|
||||
|
||||
// 加入路径
|
||||
Push(&S, e);
|
||||
|
||||
// 如果到达终点
|
||||
if(Equals(curPos, end) == TRUE) {
|
||||
printf("\n寻路成功!!\n\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// 获取下一个应当探索的位置,即当前位置的东邻
|
||||
curPos = NextPos(curPos, East);
|
||||
|
||||
// 探索下一步
|
||||
curStep++;
|
||||
|
||||
// 如果当前位置已经探索过了,则考虑修改探索方向
|
||||
} else {
|
||||
// 如果栈不为空(存在探索的必要)
|
||||
if(!StackEmpty(S)) {
|
||||
// 回退到上一个位置
|
||||
Pop(&S, &e);
|
||||
|
||||
// 如果待探索位置的4个方向都探索过,则需要做标记
|
||||
while(e.di == North && !StackEmpty(S)) {
|
||||
// 留下"死胡同"标记,即从该位置出发的路径都没有通路
|
||||
MarkPrint(maze, e.seat, Impasse);
|
||||
|
||||
// 继续回退
|
||||
Pop(&S, &e);
|
||||
}
|
||||
|
||||
// 如果待探索位置还有剩余可探索的方向
|
||||
if(e.di < North) {
|
||||
// 改变探索方向,按东南西北的方向轮询
|
||||
++e.di;
|
||||
|
||||
// 在迷宫中留下访问标记,用来观察迷宫状态(教材中没有该步骤)
|
||||
MarkPrint(maze, e.seat, e.di);
|
||||
|
||||
// 重新将该位置加入到路径中
|
||||
Push(&S, e);
|
||||
|
||||
// 获取下一个应当探索的位置
|
||||
curPos = NextPos(e.seat, e.di);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 栈不为空,意味着还有探索的必要
|
||||
} while(!StackEmpty(S));
|
||||
|
||||
printf("\n寻路失败!!\n\n");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化一个规模为N×N迷宫
|
||||
* start和end分别为迷宫的入口坐标和出口坐标
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作,但该操作是必须存在的
|
||||
*/
|
||||
void InitMaze(MazeType maze, PosType* start, PosType* end) {
|
||||
int i, j, tmp;
|
||||
|
||||
srand((unsigned) time(NULL)); // 用系统时间做随机数种子
|
||||
|
||||
for(i = 0; i < M; i++) {
|
||||
for(j = 0; j < N; j++) {
|
||||
|
||||
// 在迷宫最外层生成外墙
|
||||
if(i == 0 || j == 0 || i == M - 1 || j == N - 1) {
|
||||
maze[i][j] = Wall;
|
||||
|
||||
// 迷宫内部的物件
|
||||
} else {
|
||||
tmp = rand() % X; // 生成随机数[0, X-1]填充迷宫
|
||||
|
||||
if(tmp == 0) {
|
||||
// 1/X的概率生成障碍
|
||||
maze[i][j] = Obstacle;
|
||||
} else {
|
||||
// 其它地方作为可遍历的通路
|
||||
maze[i][j] = Way;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 迷宫入口坐标
|
||||
(*start).x = 1;
|
||||
(*start).y = 0;
|
||||
|
||||
// 迷宫出口坐标
|
||||
(*end).x = M - 2;
|
||||
(*end).y = N - 1;
|
||||
|
||||
// 开放入口和出口
|
||||
maze[1][0] = maze[M - 2][N - 1] = Way;
|
||||
|
||||
// 为了提高寻路成功率,将入口处和出口处临近的结点设为通路(非必须操作)
|
||||
maze[1][1] = maze[M - 2][N - 2] = Way;
|
||||
|
||||
// 显示迷宫的初始状态
|
||||
PaintMaze(maze);
|
||||
}
|
||||
|
||||
/*
|
||||
* 判断当前位置是否可通过:要求该位置是从未曾探索的通道块
|
||||
*
|
||||
*【注】
|
||||
* 可理解为判断当前位置是否为首次探索
|
||||
*/
|
||||
Status Pass(MazeType maze, PosType seat) {
|
||||
int x = seat.x;
|
||||
int y = seat.y;
|
||||
|
||||
// 首先检查是否越界,如果是越界了,当前位置肯定无法通过
|
||||
if(x < 0 || y < 0 || x > M - 1 || y > N - 1) {
|
||||
return FALSE; //越界
|
||||
}
|
||||
|
||||
// 要求该位置必须是从未曾探索的通道块
|
||||
if(maze[x][y] != Way) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取下一个应当探索的位置
|
||||
* di指示当前所处位置的探索方向,包括East, South, West, North
|
||||
*/
|
||||
PosType NextPos(PosType seat, int di) {
|
||||
PosType tmp = seat;
|
||||
|
||||
switch(di) {
|
||||
case East:
|
||||
tmp.y++; //向东
|
||||
break;
|
||||
case South:
|
||||
tmp.x++; //向南
|
||||
break;
|
||||
case West:
|
||||
tmp.y--; //向西
|
||||
break;
|
||||
case North:
|
||||
tmp.x--; //向北
|
||||
break;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* 留下初始访问足迹
|
||||
*
|
||||
* 初始访问足迹即向东访问
|
||||
*/
|
||||
void FootPrint(MazeType maze, PosType seat) {
|
||||
//初始设置向东探索
|
||||
MarkPrint(maze, seat, East);
|
||||
}
|
||||
|
||||
/*
|
||||
* 在迷宫的seat处留下mark标记
|
||||
*
|
||||
*【注】
|
||||
* 该函数与教材上的函数有区别
|
||||
* 教材上只是用此函数留下"不可探索"的标记
|
||||
* 而此处的函数改进为可以留下任意标记,包括探索方向的标记
|
||||
*/
|
||||
void MarkPrint(MazeType maze, PosType seat, int mark) {
|
||||
int x = seat.x;
|
||||
int y = seat.y;
|
||||
|
||||
maze[x][y] = mark; //留下不能通过的标记
|
||||
|
||||
// 绘制迷宫
|
||||
PaintMaze(maze);
|
||||
}
|
||||
|
||||
/*
|
||||
* 构造一个通道块信息并返回
|
||||
*
|
||||
*【注】
|
||||
* 教材中有此操作,但无此函数
|
||||
*/
|
||||
SElemType Construct(int ord, PosType seat, int di) {
|
||||
SElemType e;
|
||||
|
||||
e.ord = ord;
|
||||
e.seat = seat;
|
||||
e.di = di;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判断两个坐标是否相等
|
||||
*
|
||||
*【注】
|
||||
* 教材中有此操作,但无此函数
|
||||
* 因为这里需要比较两个结构体,所以不能直接用"=="符号
|
||||
*/
|
||||
Status Equals(PosType seat1, PosType seat2) {
|
||||
if(seat1.x == seat2.x && seat1.y == seat2.y) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 绘制迷宫
|
||||
* 以图形的方式呈现迷宫当前的状态
|
||||
*
|
||||
*【注】
|
||||
* 1.教材中无此操作,此处增加该操作的目的是观察寻路过程的每一步
|
||||
* 2.该实现适用于CLion的控制台
|
||||
*/
|
||||
void PaintMaze(MazeType maze) {
|
||||
int i, j;
|
||||
|
||||
Wait(SleepTime); // 暂停一下
|
||||
|
||||
for(i = 0; i < M; i++) {
|
||||
for(j = 0; j < N; j++) {
|
||||
if(maze[i][j] == Wall) { // 外墙
|
||||
printf("*");
|
||||
} else if(maze[i][j] == Obstacle) { // 迷宫内部的障碍
|
||||
printf("●");
|
||||
} else if(maze[i][j] == East) { // 正在朝东探索
|
||||
printf("▷");
|
||||
} else if(maze[i][j] == South) { // 正在朝南探索
|
||||
printf("▽");
|
||||
} else if(maze[i][j] == West) { // 正在朝西探索
|
||||
printf("◁");
|
||||
} else if(maze[i][j] == North) { // 正在朝北探索
|
||||
printf("△");
|
||||
} else if(maze[i][j] == Impasse) { // 死胡同,即四个方向都探索过,但无法通过的位置
|
||||
printf("x");
|
||||
} else { // 还未探索过的路径结点
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
if(j != 0 && j % (N - 1) == 0) { // 每隔N个结点换行
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*==============
|
||||
* 迷宫寻路程序
|
||||
*
|
||||
* 包含算法: 3.3
|
||||
===============*/
|
||||
|
||||
#ifndef MAZE_H
|
||||
#define MAZE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供system、rand、srand原型
|
||||
#include <time.h> // 提供time原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define M 15 // 迷宫的行数
|
||||
#define N 30 // 迷宫的列数
|
||||
|
||||
#define X 4 // X指示迷宫障碍出现的概率。例如,X=4,意味着遍历迷宫时遇到障碍的概率是1/4=25%
|
||||
|
||||
#define SleepTime 3 //SleepTime代表打印地图时的时间间隔
|
||||
|
||||
/* 迷宫类型定义 */
|
||||
typedef enum {
|
||||
Wall, // 外墙
|
||||
Obstacle, // 迷宫内部的障碍
|
||||
Way, // 通路
|
||||
Impasse, // “死胡同”
|
||||
East, South, West, North // 当前探索方向:东南西北
|
||||
} MazeNode;
|
||||
|
||||
typedef int MazeType[M][N]; // 迷宫类型
|
||||
|
||||
|
||||
/*
|
||||
* ████████ 算法3.3 ████████
|
||||
*
|
||||
* 迷宫寻路
|
||||
*
|
||||
* 使用穷举法,找到一条可行通路即返回
|
||||
*/
|
||||
Status MazePath(MazeType maze, PosType start, PosType end);
|
||||
|
||||
/*
|
||||
* 初始化一个规模为N×N迷宫
|
||||
* start和end分别为迷宫的入口坐标和出口坐标
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作,但该操作是必须存在的
|
||||
*/
|
||||
void InitMaze(MazeType maze, PosType* start, PosType* end);
|
||||
|
||||
/*
|
||||
* 判断当前位置是否可通过:要求该位置是从未曾探索的通道块
|
||||
*
|
||||
*【注】
|
||||
* 可理解为判断当前位置是否为首次探索
|
||||
*/
|
||||
Status Pass(MazeType maze, PosType seat);
|
||||
|
||||
/*
|
||||
* 获取下一个应当探索的位置
|
||||
* di指示当前所处位置的探索方向,包括East, South, West, North
|
||||
*/
|
||||
PosType NextPos(PosType seat, int di);
|
||||
|
||||
/*
|
||||
* 留下初始访问足迹
|
||||
*
|
||||
* 初始访问足迹即向东访问
|
||||
*/
|
||||
void FootPrint(MazeType maze, PosType seat);
|
||||
|
||||
/*
|
||||
* 在迷宫的seat处留下mark标记
|
||||
*
|
||||
*【注】
|
||||
* 该函数与教材上的函数有区别
|
||||
* 教材上只是用此函数留下"不可探索"的标记
|
||||
* 而此处的函数改进为可以留下任意标记,包括探索方向的标记
|
||||
*/
|
||||
void MarkPrint(MazeType maze, PosType seat, int mark);
|
||||
|
||||
/*
|
||||
* 构造一个通道块信息并返回
|
||||
*
|
||||
*【注】
|
||||
* 教材中有此操作,但无此函数
|
||||
*/
|
||||
SElemType Construct(int ord, PosType seat, int di);
|
||||
|
||||
/*
|
||||
* 判断两个坐标是否相等
|
||||
*
|
||||
*【注】
|
||||
* 教材中有此操作,但无此函数
|
||||
* 因为这里需要比较两个结构体,所以不能直接用"=="符号
|
||||
*/
|
||||
Status Equals(PosType a, PosType b);
|
||||
|
||||
/*
|
||||
* 绘制迷宫
|
||||
* 以图形的方式呈现迷宫当前的状态
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作
|
||||
* 此处增加该操作的目的是观察寻路过程的每一步
|
||||
*/
|
||||
void PaintMaze(MazeType maze);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).base = (SElemType*) malloc(STACK_INIT_SIZE * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
(*S).stacksize = STACK_INIT_SIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S) {
|
||||
if(S.top == S.base) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 栈满时,追加存储空间
|
||||
if((*S).top - (*S).base >= (*S).stacksize) {
|
||||
(*S).base = (SElemType*) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW); // 存储分配失败
|
||||
}
|
||||
|
||||
(*S).top = (*S).base + (*S).stacksize;
|
||||
(*S).stacksize += STACKINCREMENT;
|
||||
}
|
||||
|
||||
// 进栈先赋值,栈顶指针再自增
|
||||
*(S->top++) = e;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*S).top == (*S).base) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出栈栈顶指针先递减,再赋值
|
||||
*e = *(--(*S).top);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#ifndef SQSTACK_H
|
||||
#define SQSTACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define STACK_INIT_SIZE 100 // 顺序栈存储空间的初始分配量
|
||||
#define STACKINCREMENT 10 // 顺序栈存储空间的分配增量
|
||||
|
||||
// 迷宫通道块的坐标
|
||||
typedef struct {
|
||||
int x; // 通道块的横、纵坐标定义
|
||||
int y;
|
||||
} PosType;
|
||||
|
||||
/* 通道块信息,用于迷宫算法 */
|
||||
typedef struct {
|
||||
int ord; // 通道块的“序号”
|
||||
PosType seat; // 通道块的“坐标位置”
|
||||
int di; // 下一个应当访问的“方向”
|
||||
} SElemType;
|
||||
|
||||
// 顺序栈元素结构
|
||||
typedef struct {
|
||||
SElemType* base; // 栈底指针
|
||||
SElemType* top; // 栈顶指针
|
||||
int stacksize; // 当前已分配的存储空间,以元素为单位
|
||||
} SqStack;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S);
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e);
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(Expression SqStack.h SqStack.c Expression.h Expression.c Expression-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(Expression Scanf_lib)
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "Expression.h" //**▲03 栈和队列**//
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char opnd;
|
||||
char* exp = "(1+3)*2/4#";
|
||||
|
||||
opnd = EvaluateExpression(exp);
|
||||
|
||||
printf("作为示例, %s 的计算结果为:%d\n", exp, opnd - '0');
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*==============
|
||||
* 表达式计算
|
||||
*
|
||||
* 包含算法: 3.4
|
||||
===============*/
|
||||
|
||||
#include "Expression.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.4 ████████
|
||||
*
|
||||
* 从exp读入表达式,并计算表达式的运算结果
|
||||
*
|
||||
*【注】
|
||||
* 1.教材使用的是控制台输入,这里为了便于测试,直接改为从形参接收参数
|
||||
* 2.该计算功能有限,理论上仅支持对个位数运算,且要求每一步的运算结果也是个位数。
|
||||
* 教材提供此算法的目的是验证栈的使用,如果想扩展对运算符的支持,并扩大对操作数的支持,
|
||||
* 则可以顺着此思路进行改版
|
||||
*/
|
||||
OperandType EvaluateExpression(const char exp[]) {
|
||||
SElemType c; // 输入序列
|
||||
|
||||
SqStack OPTR; // 运算符栈
|
||||
SqStack OPND; // 操作数栈
|
||||
|
||||
OperatorType theta, x; // 运算符
|
||||
OperandType a, b; // 操作数
|
||||
|
||||
int i = 0;
|
||||
|
||||
// 初始化运算符栈,并将一个界限符'#'入栈
|
||||
InitStack(&OPTR);
|
||||
Push(&OPTR, '#');
|
||||
|
||||
// 初始化操作数栈,并开始读取输入
|
||||
InitStack(&OPND);
|
||||
c = exp[i++];
|
||||
|
||||
// 当输入中遇到界限符'#',且运算符栈的栈顶元素也是界限符'#'时,则表示读取结束,且运算结束
|
||||
while(c != '#' || GetTop(OPTR) != '#') {
|
||||
// 如果ch不是运算符,则视其为操作数,将其入栈
|
||||
if(!In(c, OP)) {
|
||||
Push(&OPND, c); // 将操作数入栈
|
||||
c = exp[i++]; // 获取下一个输入字符
|
||||
} else {
|
||||
switch(Precede(GetTop(OPTR), c)) {
|
||||
// 栈中运算符优先级低,继续进栈
|
||||
case '<':
|
||||
Push(&OPTR, c);
|
||||
c = exp[i++];
|
||||
break;
|
||||
|
||||
// 优先级相等时,说明这里遇到括号,需要脱括号
|
||||
case '=':
|
||||
Pop(&OPTR, &x);
|
||||
c = exp[i++];
|
||||
break;
|
||||
|
||||
/*
|
||||
* 栈中运算符优先级高时,先计算,再将计算结果压入栈
|
||||
*
|
||||
* 注:这儿没有读字符,c保留的还是刚才读到的字符
|
||||
*/
|
||||
case '>':
|
||||
Pop(&OPTR, &theta); // 弹出运算符
|
||||
Pop(&OPND, &b); // 弹出右边的操作数
|
||||
Pop(&OPND, &a); // 弹出左边的操作数
|
||||
Push(&OPND, Operate(a, theta, b));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GetTop(OPND);
|
||||
}
|
||||
|
||||
// 判断指定的运算符是否合规
|
||||
Status In(SElemType c, const char OP[]) {
|
||||
|
||||
SElemType* e = strchr(OP, c);
|
||||
|
||||
// 如果运算符c不在合规范围内,说明指定的运算符不合规
|
||||
if(e == NULL) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 判断运算符栈中操作符o1与表达式中的操作符o2的优先级。
|
||||
*
|
||||
* 返回'>'、'<'、'=',以指示o1和o2的优先级
|
||||
*/
|
||||
OperatorType Precede(OperatorType o1, OperatorType o2) {
|
||||
int x, y;
|
||||
|
||||
// 获取指定的运算符在运算符表中的位置
|
||||
char* p1 = strchr(OP, o1);
|
||||
char* p2 = strchr(OP, o2);
|
||||
|
||||
// 计算出一个运算符优先级表坐标
|
||||
x = p1 - OP;
|
||||
y = p2 - OP;
|
||||
|
||||
return PrecedeTable[x][y];
|
||||
}
|
||||
|
||||
/*
|
||||
* 对操作数进行运算
|
||||
*
|
||||
* a、b是操作数,theta是运算符。
|
||||
* 对于操作数和运算结果,仅保证对个位数的支持
|
||||
*/
|
||||
OperandType Operate(OperandType a, OperatorType theta, OperandType b) {
|
||||
int x, y, z = CHAR_MAX - 48;
|
||||
|
||||
// 先从字符型转为整型
|
||||
x = a - '0';
|
||||
y = b - '0';
|
||||
|
||||
switch(theta) {
|
||||
case '+':
|
||||
z = x + y;
|
||||
break;
|
||||
case '-':
|
||||
z = x - y;
|
||||
break;
|
||||
case '*':
|
||||
z = x * y;
|
||||
break;
|
||||
case '/':
|
||||
z = x / y;
|
||||
break;
|
||||
}
|
||||
|
||||
// 计算完成后,将整型转换为字符型返回
|
||||
return z + 48;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*==============
|
||||
* 表达式计算
|
||||
*
|
||||
* 包含算法: 3.4
|
||||
===============*/
|
||||
|
||||
#ifndef EXPRESSION_H
|
||||
#define EXPRESSION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
typedef SElemType OperatorType; // 运算符类型
|
||||
typedef SElemType OperandType; // 操作数类型
|
||||
|
||||
// 运算符表,即表达式中允许出现的符号(包括界限符'#')
|
||||
static const char OP[] = {'+', '-', '*', '/', '(', ')', '#'};
|
||||
|
||||
/*
|
||||
* 运算符优先级表(包括界限符'#'),与上面的OP表是呼应的。
|
||||
* 可参见教材中的"算符间的优先关系"表
|
||||
*/
|
||||
static const char PrecedeTable[7][7] = {{'>', '>', '<', '<', '<', '>', '>'},
|
||||
{'>', '>', '<', '<', '<', '>', '>'},
|
||||
{'>', '>', '>', '>', '<', '>', '>'},
|
||||
{'>', '>', '>', '>', '<', '>', '>'},
|
||||
{'<', '<', '<', '<', '<', '=', ' '},
|
||||
{'>', '>', '>', '>', ' ', '>', '>'},
|
||||
{'<', '<', '<', '<', '<', ' ', '='}};
|
||||
|
||||
|
||||
/*
|
||||
* ████████ 算法3.4 ████████
|
||||
*
|
||||
* 从exp读入表达式,并计算表达式的运算结果
|
||||
*
|
||||
*【注】
|
||||
* 1.教材使用的是控制台输入,这里为了便于测试,直接改为从形参接收参数
|
||||
* 2.该计算功能有限,理论上仅支持对个位数运算,且要求每一步的运算结果也是个位数。
|
||||
* 教材提供此算法的目的是验证栈的使用,如果想扩展对运算符的支持,并扩大对操作数的支持,
|
||||
* 则可以顺着此思路进行改版
|
||||
*/
|
||||
OperandType EvaluateExpression(const char exp[]);
|
||||
|
||||
/*
|
||||
* 判断指定的运算符是否合规
|
||||
*
|
||||
* OP中存储了合规的运算符,包括界限符'#'
|
||||
*/
|
||||
Status In(SElemType c, const char OP[]);
|
||||
|
||||
/*
|
||||
* 判断运算符栈中操作符o1与表达式中的操作符o2的优先级。
|
||||
*
|
||||
* 返回'>'、'<'、'=',以指示o1和o2的优先级
|
||||
*/
|
||||
OperatorType Precede(OperatorType o1, OperatorType o2);
|
||||
|
||||
/*
|
||||
* 对操作数进行运算
|
||||
*
|
||||
* a、b是操作数,theta是运算符。
|
||||
* 对于操作数和运算结果,仅保持对个位数的支持
|
||||
*/
|
||||
OperandType Operate(OperandType a, OperatorType theta, OperandType b);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S) {
|
||||
if(S == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*S).base = (SElemType*) malloc(STACK_INIT_SIZE * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*S).top = (*S).base;
|
||||
(*S).stacksize = STACK_INIT_SIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S) {
|
||||
if(S.top == S.base) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取操作符栈的栈顶元素。
|
||||
*
|
||||
*【注】
|
||||
* 该操作的实现与传统的顺序栈的取值操作有些不同,但核心作用一致
|
||||
*/
|
||||
SElemType GetTop(SqStack S) {
|
||||
SElemType e;
|
||||
|
||||
if(S.base == NULL || S.top == S.base) {
|
||||
return '\0';
|
||||
}
|
||||
|
||||
// 不会改变栈中元素
|
||||
e = *(S.top - 1);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 栈满时,追加存储空间
|
||||
if((*S).top - (*S).base >= (*S).stacksize) {
|
||||
(*S).base = (SElemType*) realloc((*S).base, ((*S).stacksize + STACKINCREMENT) * sizeof(SElemType));
|
||||
if((*S).base == NULL) {
|
||||
exit(OVERFLOW); // 存储分配失败
|
||||
}
|
||||
|
||||
(*S).top = (*S).base + (*S).stacksize;
|
||||
(*S).stacksize += STACKINCREMENT;
|
||||
}
|
||||
|
||||
// 进栈先赋值,栈顶指针再自增
|
||||
*(S->top++) = e;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e) {
|
||||
if(S == NULL || (*S).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*S).top == (*S).base) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出栈栈顶指针先递减,再赋值
|
||||
*e = *(--(*S).top);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*=============================
|
||||
* 栈的顺序存储结构(顺序栈)
|
||||
=============================*/
|
||||
|
||||
#ifndef SQSTACK_H
|
||||
#define SQSTACK_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define STACK_INIT_SIZE 100 // 顺序栈存储空间的初始分配量
|
||||
#define STACKINCREMENT 10 // 顺序栈存储空间的分配增量
|
||||
|
||||
/* 表达式元素类型定义 */
|
||||
typedef char SElemType;
|
||||
|
||||
// 顺序栈元素结构
|
||||
typedef struct {
|
||||
SElemType* base; // 栈底指针
|
||||
SElemType* top; // 栈顶指针
|
||||
int stacksize; // 当前已分配的存储空间,以元素为单位
|
||||
} SqStack;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空栈。初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitStack(SqStack* S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断顺序栈中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 顺序栈为空
|
||||
* FALSE: 顺序栈不为空
|
||||
*/
|
||||
Status StackEmpty(SqStack S);
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取操作符栈的栈顶元素。
|
||||
*
|
||||
*【注】
|
||||
* 该操作的实现与传统的顺序栈的取值操作有些不同,但核心作用一致
|
||||
*/
|
||||
SElemType GetTop(SqStack S);
|
||||
|
||||
/*
|
||||
* 入栈
|
||||
*
|
||||
* 将元素e压入到栈顶。
|
||||
*/
|
||||
Status Push(SqStack* S, SElemType e);
|
||||
|
||||
/*
|
||||
* 出栈
|
||||
*
|
||||
* 将栈顶元素弹出,并用e接收。
|
||||
*/
|
||||
Status Pop(SqStack* S, SElemType* e);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(Hanoi Hanoi.h Hanoi.c Hanoi-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(Hanoi Scanf_lib)
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "Hanoi.h" //**03 栈和队列**//
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char x = 'x';
|
||||
char y = 'y';
|
||||
char z = 'z';
|
||||
|
||||
printf("作为示例,假设圆盘个数为 %d ,操作步骤如下...\n", N);
|
||||
|
||||
init(N);
|
||||
|
||||
hanoi(N, x, y, z);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*==============
|
||||
* 汉诺塔
|
||||
*
|
||||
* 包含算法: 3.5
|
||||
===============*/
|
||||
|
||||
#include "Hanoi.h" //**03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.5 ████████
|
||||
*
|
||||
* 汉诺塔求解:以y为辅助,将x上前n个圆盘移动到z
|
||||
*/
|
||||
void hanoi(int n, char x, char y, char z) {
|
||||
if(n == 1) { // 欲移动n个圆盘,需先移动其上的n-1个圆盘
|
||||
move(x, 1, z); // 将编号为1的圆盘从x移到z
|
||||
} else {
|
||||
hanoi(n - 1, x, z, y); // 将x上编号为1至n-1的圆盘移到y,z作辅助塔
|
||||
move(x, n, z); // 将编号为n的圆盘从x移到z
|
||||
hanoi(n - 1, y, x, z); // 将y上编号为1至n-1的圆盘移动到z,x作辅助塔
|
||||
}
|
||||
}
|
||||
|
||||
void move(char x, int n, char z) {
|
||||
// step为全局变量,在main函数之外定义
|
||||
gStep++;
|
||||
printf("第%2d步:将第 %d 个圆盘从 %c 移到 %c \n", gStep, n, x, z);
|
||||
|
||||
// 汉诺塔移动的图形表示
|
||||
PrintGraph(x, n, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* 汉诺塔图形信息初始化
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作。
|
||||
* 增加此操作的目的是为了便于观察汉诺塔圆盘的移动过程
|
||||
*/
|
||||
void init(int n) {
|
||||
int i;
|
||||
int* towerX, * towerY, * towerZ;
|
||||
|
||||
T.plates = (int**) malloc(3 * sizeof(int*));
|
||||
|
||||
towerX = (int*) malloc(n * sizeof(int));
|
||||
towerY = (int*) malloc(n * sizeof(int));
|
||||
towerZ = (int*) malloc(n * sizeof(int));
|
||||
|
||||
for(i = 0; i < n; ++i) {
|
||||
towerX[i] = n - i;
|
||||
towerY[i] = 0;
|
||||
towerZ[i] = 0;
|
||||
}
|
||||
|
||||
T.plates[0] = towerX;
|
||||
T.plates[1] = towerY;
|
||||
T.plates[2] = towerZ;
|
||||
|
||||
T.high[0] = n;
|
||||
T.high[1] = 0;
|
||||
T.high[2] = 0;
|
||||
|
||||
// 汉诺塔移动的图形表示
|
||||
PrintGraph('\0', 0, '\0');
|
||||
}
|
||||
|
||||
/*
|
||||
* 汉诺塔移动的图形表示
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作。
|
||||
* 增加此操作的目的是为了便于观察汉诺塔圆盘的移动过程
|
||||
*/
|
||||
void PrintGraph(char t1, int n, char t2) {
|
||||
int i, j;
|
||||
char** s;
|
||||
|
||||
// 将n号盘子从t1中移除
|
||||
if(t1 == 'x') {
|
||||
T.plates[0][T.high[0] - 1] = 0;
|
||||
T.high[0]--;
|
||||
} else if(t1 == 'y') {
|
||||
T.plates[1][T.high[1] - 1] = 0;
|
||||
T.high[1]--;
|
||||
} else if(t1 == 'z') {
|
||||
T.plates[2][T.high[2] - 1] = 0;
|
||||
T.high[2]--;
|
||||
} else {
|
||||
// t1上的圆盘不需要移动
|
||||
}
|
||||
|
||||
// 将n号盘子添加到t2中
|
||||
if(t2 == 'x') {
|
||||
T.plates[0][T.high[0]] = n;
|
||||
T.high[0]++;
|
||||
} else if(t2 == 'y') {
|
||||
T.plates[1][T.high[1]] = n;
|
||||
T.high[1]++;
|
||||
} else if(t2 == 'z') {
|
||||
T.plates[2][T.high[2]] = n;
|
||||
T.high[2]++;
|
||||
} else {
|
||||
// t2上的圆盘不需要移动
|
||||
}
|
||||
|
||||
s = (char**) malloc((N + 2) * sizeof(char*));
|
||||
for(i = 0; i < N + 2; i++) {
|
||||
s[i] = (char*) malloc(N * sizeof(char));
|
||||
|
||||
for(j = 0; j < i; j++) {
|
||||
if(i == N + 1) {
|
||||
s[i][j] = '-'; // 将最后一行初始化为托盘
|
||||
} else {
|
||||
s[i][j] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
if(i == N + 1) {
|
||||
s[i][j - 1] = '\0';
|
||||
} else {
|
||||
s[i][j] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
for(i = N - 1; i >= 0; i--) {
|
||||
printf("%-*s | %-*s | %-*s\n", N, s[T.plates[0][i]], N, s[T.plates[1][i]], N, s[T.plates[2][i]]);
|
||||
}
|
||||
printf("%-*s + %-*s + %-*s\n", N, s[N + 1], N, s[N + 1], N, s[N + 1]);
|
||||
printf("%-*s %-*s %-*s\n", N + 2, "x", N + 2, "y", N + 2, "z");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*==============
|
||||
* 汉诺塔
|
||||
*
|
||||
* 包含算法: 3.5
|
||||
===============*/
|
||||
|
||||
#ifndef HANOI_H
|
||||
#define HANOI_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "Status.h"
|
||||
|
||||
#define N 5 // 汉诺塔中盘子总数
|
||||
|
||||
// 汉诺塔图形信息
|
||||
typedef struct {
|
||||
int** plates; // 汉诺塔中的圆盘信息
|
||||
int high[3]; // 三座塔的高度(持有的盘子数量)
|
||||
} Tower;
|
||||
|
||||
// 汉诺塔
|
||||
Tower T;
|
||||
|
||||
// 统计移动步数
|
||||
int gStep;
|
||||
|
||||
|
||||
/*
|
||||
* ████████ 算法3.5 ████████
|
||||
*
|
||||
* 汉诺塔求解:以y塔为辅助,将x塔上前n个圆盘移动到z塔
|
||||
*/
|
||||
void hanoi(int n, char x, char y, char z);
|
||||
|
||||
/*
|
||||
* 移动汉诺塔圆盘:将第n个圆盘从x塔移到z塔。
|
||||
*/
|
||||
void move(char x, int n, char z);
|
||||
|
||||
/*
|
||||
* 汉诺塔图形信息初始化
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作。
|
||||
* 增加此操作的目的是为了便于观察汉诺塔圆盘的移动过程
|
||||
*/
|
||||
void init(int n);
|
||||
|
||||
/*
|
||||
* 汉诺塔移动的图形表示,参数含义参见move()函数
|
||||
*
|
||||
*【注】
|
||||
* 教材中无此操作。
|
||||
* 增加此操作的目的是为了便于观察汉诺塔圆盘的移动过程
|
||||
*/
|
||||
void PrintGraph(char x, int n, char z);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(LinkQueue LinkQueue.h LinkQueue.c LinkQueue-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(LinkQueue Scanf_lib)
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include "LinkQueue.h" //**03 栈和队列**//
|
||||
|
||||
// 测试函数,打印整型
|
||||
void PrintElem(QElemType e);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
LinkQueue Q;
|
||||
int i;
|
||||
QElemType e;
|
||||
|
||||
printf("████████ 函数 InitQueue \n");
|
||||
{
|
||||
printf("█ 初始化链队 Q ...\n");
|
||||
InitQueue(&Q);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueEmpty \n");
|
||||
{
|
||||
QueueEmpty(Q) ? printf("█ Q 为空!!\n") : printf("█ Q 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 EnQueue \n");
|
||||
{
|
||||
for(i = 1; i <= 6; i++) {
|
||||
EnQueue(&Q, 2 * i);
|
||||
printf("█ 元素 \"%2d\" 入队...\n", 2 * i);
|
||||
}
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueTraverse \n");
|
||||
{
|
||||
printf("█ Q 中的元素为:Q = ");
|
||||
QueueTraverse(Q, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueLength \n");
|
||||
{
|
||||
i = QueueLength(Q);
|
||||
printf("█ Q 的长度为 %d \n", i);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DeQueue \n");
|
||||
{
|
||||
DeQueue(&Q, &e);
|
||||
printf("█ 队头元素 \"%d\" 出队...\n", e);
|
||||
printf("█ Q 中的元素为:Q = ");
|
||||
QueueTraverse(Q, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 GetHead \n");
|
||||
{
|
||||
GetHead(Q, &e);
|
||||
printf("█ 队头元素的值为 \"%d\" \n", e);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 ClearQueue \n");
|
||||
{
|
||||
printf("█ 清空 Q 前:");
|
||||
QueueEmpty(Q) ? printf(" Q 为空!!\n") : printf(" Q 不为空!\n");
|
||||
|
||||
ClearQueue(&Q);
|
||||
|
||||
printf("█ 清空 Q 后:");
|
||||
QueueEmpty(Q) ? printf(" Q 为空!!\n") : printf(" Q 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DestroyQueue \n");
|
||||
{
|
||||
printf("█ 销毁 Q 前:");
|
||||
Q.front != NULL && Q.rear != NULL ? printf(" Q 存在!\n") : printf(" Q 不存在!!\n");
|
||||
|
||||
DestroyQueue(&Q);
|
||||
|
||||
printf("█ 销毁 Q 后:");
|
||||
Q.front != NULL && Q.rear != NULL ? printf(" Q 存在!\n") : printf(" Q 不存在!!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 测试函数,打印整型
|
||||
void PrintElem(QElemType e) {
|
||||
printf("%d ", e);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*=========================
|
||||
* 队列的链式存储结构(链队)
|
||||
==========================*/
|
||||
|
||||
#ifndef LINKQUEUE_C
|
||||
#define LINKQUEUE_C
|
||||
|
||||
#include "LinkQueue.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的链队。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列带有头结点
|
||||
*/
|
||||
Status InitQueue(LinkQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*Q).front = (*Q).rear = (QueuePtr) malloc(sizeof(QNode));
|
||||
if(!(*Q).front) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*Q).front->next = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放链队所占内存。
|
||||
*/
|
||||
Status DestroyQueue(LinkQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
while((*Q).front) {
|
||||
(*Q).rear = (*Q).front->next;
|
||||
free((*Q).front);
|
||||
(*Q).front = (*Q).rear;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 这里需要释放链队中非头结点处的空间。
|
||||
*/
|
||||
Status ClearQueue(LinkQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*Q).rear = (*Q).front->next;
|
||||
|
||||
while((*Q).rear) {
|
||||
(*Q).front->next = (*Q).rear->next;
|
||||
free((*Q).rear);
|
||||
(*Q).rear = (*Q).front->next;
|
||||
}
|
||||
|
||||
(*Q).rear = (*Q).front;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链队中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链队为空
|
||||
* FALSE: 链队不为空
|
||||
*/
|
||||
Status QueueEmpty(LinkQueue Q) {
|
||||
if(Q.front == Q.rear) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回链队包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(LinkQueue Q) {
|
||||
int count = 0;
|
||||
QueuePtr p = Q.front;
|
||||
|
||||
while(p != Q.rear) {
|
||||
count++;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(LinkQueue Q, QElemType* e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q.front == NULL || Q.front == Q.rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = Q.front->next;
|
||||
*e = p->data;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(LinkQueue* Q, QElemType e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q == NULL || (*Q).front == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = (QueuePtr) malloc(sizeof(QNode));
|
||||
if(!p) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
p->data = e;
|
||||
p->next = NULL;
|
||||
|
||||
(*Q).rear->next = p;
|
||||
(*Q).rear = p;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(LinkQueue* Q, QElemType* e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q == NULL || (*Q).front == NULL || (*Q).front == (*Q).rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = (*Q).front->next;
|
||||
*e = p->data;
|
||||
|
||||
(*Q).front->next = p->next;
|
||||
if((*Q).rear == p) {
|
||||
(*Q).rear = (*Q).front;
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问队列Q
|
||||
*/
|
||||
Status QueueTraverse(LinkQueue Q, void (Visit)(QElemType)) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q.front == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = Q.front->next;
|
||||
|
||||
while(p != NULL) {
|
||||
Visit(p->data);
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/*=========================
|
||||
* 队列的链式存储结构(链队)
|
||||
==========================*/
|
||||
|
||||
#ifndef LINKQUEUE_H
|
||||
#define LINKQUEUE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 链队元素类型定义 */
|
||||
typedef int QElemType;
|
||||
|
||||
// 队列元素结构
|
||||
typedef struct QNode {
|
||||
QElemType data;
|
||||
struct QNode* next;
|
||||
} QNode, * QueuePtr;
|
||||
|
||||
// 队列结构
|
||||
typedef struct {
|
||||
QueuePtr front; // 队头指针
|
||||
QueuePtr rear; // 队尾指针
|
||||
} LinkQueue; // 队列的链式存储表示
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的链队。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列带有头结点
|
||||
*/
|
||||
Status InitQueue(LinkQueue* Q);
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放链队所占内存。
|
||||
*/
|
||||
Status DestroyQueue(LinkQueue* Q);
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 这里需要释放链队中非头结点处的空间。
|
||||
*/
|
||||
Status ClearQueue(LinkQueue* Q);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链队中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链队为空
|
||||
* FALSE: 链队不为空
|
||||
*/
|
||||
Status QueueEmpty(LinkQueue Q);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回链队包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(LinkQueue Q);
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(LinkQueue Q, QElemType* e);
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(LinkQueue* Q, QElemType e);
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(LinkQueue* Q, QElemType* e);
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问队列Q
|
||||
*/
|
||||
Status QueueTraverse(LinkQueue Q, void(Visit)(QElemType));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(SqQueue SqQueue.h SqQueue.c SqQueue-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(SqQueue Scanf_lib)
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include "SqQueue.h" //**▲03 栈和队列**//
|
||||
|
||||
// 测试函数,打印整型
|
||||
void PrintElem(QElemType e);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SqQueue Q;
|
||||
int i;
|
||||
QElemType e;
|
||||
|
||||
printf("████████ 函数 InitQueue 测试...\n");
|
||||
{
|
||||
printf("█ 初始化循环顺序队列 Q ...\n");
|
||||
InitQueue(&Q);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueEmpty 测试...\n");
|
||||
{
|
||||
QueueEmpty(Q) ? printf("█ Q 为空!!\n") : printf("█ Q 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 EnQueue 测试...\n");
|
||||
{
|
||||
for(i = 1; i <= 6; i++) {
|
||||
EnQueue(&Q, 2 * i);
|
||||
printf("█ 元素 \"%2d\" 入队Q...\n", 2 * i);
|
||||
}
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueTraverse 测试...\n");
|
||||
{
|
||||
printf("█ Q 中的元素为:Q = ");
|
||||
QueueTraverse(Q, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 QueueLength 测试...\n");
|
||||
{
|
||||
i = QueueLength(Q);
|
||||
printf("█ Q 的长度为 %d \n", i);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DeQueue 测试...\n");
|
||||
{
|
||||
DeQueue(&Q, &e);
|
||||
printf("█ 队头元素 \"%d\" 出队...\n", e);
|
||||
printf("█ Q 中的元素为:Q = ");
|
||||
QueueTraverse(Q, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 GetHead 测试...\n");
|
||||
{
|
||||
GetHead(Q, &e);
|
||||
printf("█ 队头元素的值为 \"%d\" \n", e);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 ClearQueue 测试...\n");
|
||||
{
|
||||
printf("█ 清空 Q 前:");
|
||||
QueueEmpty(Q) ? printf(" Q 为空!!\n") : printf(" Q 不为空!\n");
|
||||
|
||||
ClearQueue(&Q);
|
||||
|
||||
printf("█ 清空 Q 后:");
|
||||
QueueEmpty(Q) ? printf(" Q 为空!!\n") : printf(" Q 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DestroyQueue 测试...\n");
|
||||
{
|
||||
printf("█ 销毁 Q 前:");
|
||||
Q.base != NULL ? printf(" Q 存在!\n") : printf(" Q 不存在!!\n");
|
||||
|
||||
DestroyQueue(&Q);
|
||||
|
||||
printf("█ 销毁 Q 后:");
|
||||
Q.base != NULL ? printf(" Q 存在!\n") : printf(" Q 不存在!!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 测试函数,打印整型
|
||||
void PrintElem(QElemType e) {
|
||||
printf("%d ", e);
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*=============================
|
||||
* 队列的顺序存储结构(顺序队列)
|
||||
==============================*/
|
||||
|
||||
#include "SqQueue.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的顺序队列。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列是循环队列
|
||||
*/
|
||||
Status InitQueue(SqQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*Q).base = (QElemType*) malloc(MAXQSIZE * sizeof(QElemType));
|
||||
if(!(*Q).base) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*Q).front = (*Q).rear = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放循环顺序队列所占内存。
|
||||
*/
|
||||
Status DestroyQueue(SqQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if((*Q).base) {
|
||||
free((*Q).base);
|
||||
}
|
||||
|
||||
(*Q).base = NULL;
|
||||
(*Q).front = (*Q).rear = 0;
|
||||
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理循环顺序队列中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status ClearQueue(SqQueue* Q) {
|
||||
if(Q == NULL || (*Q).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*Q).front = (*Q).rear = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断循环顺序队列中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 循环顺序队列为空
|
||||
* FALSE: 循环顺序队列不为空
|
||||
*/
|
||||
Status QueueEmpty(SqQueue Q) {
|
||||
// 队列空的标志
|
||||
if(Q.front == Q.rear) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回循环顺序队列包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(SqQueue Q) {
|
||||
if(Q.base == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 队列长度
|
||||
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(SqQueue Q, QElemType* e) {
|
||||
// 队列空的标志
|
||||
if(Q.base == NULL || Q.front == Q.rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*e = Q.base[Q.front];
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(SqQueue* Q, QElemType e) {
|
||||
if(Q == NULL || (*Q).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 队列满的标志(会浪费一个空间来区分队列空和队列满)
|
||||
if(((*Q).rear + 1) % MAXQSIZE == (*Q).front) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 入队
|
||||
(*Q).base[(*Q).rear] = e;
|
||||
|
||||
// 尾指针前进
|
||||
(*Q).rear = ((*Q).rear + 1) % MAXQSIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(SqQueue* Q, QElemType* e) {
|
||||
if(Q == NULL || (*Q).base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 队列空的标志
|
||||
if((*Q).front == (*Q).rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 出队
|
||||
*e = (*Q).base[(*Q).front];
|
||||
|
||||
// 头指针前进
|
||||
(*Q).front = ((*Q).front + 1) % MAXQSIZE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问队列Q
|
||||
*/
|
||||
Status QueueTraverse(SqQueue Q, void(Visit)(QElemType)) {
|
||||
int i;
|
||||
|
||||
if(Q.base == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for(i = Q.front; i != Q.rear; i = (i + 1) % MAXQSIZE) {
|
||||
Visit(Q.base[i]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*=============================
|
||||
* 队列的顺序存储结构(顺序队列)
|
||||
==============================*/
|
||||
|
||||
#ifndef SQQUEUE_H
|
||||
#define SQQUEUE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define MAXQSIZE 1000 //最大队列长度
|
||||
|
||||
/* 循环队列元素类型定义 */
|
||||
typedef int QElemType;
|
||||
|
||||
// 循环队列的顺序存储结构
|
||||
typedef struct {
|
||||
QElemType* base; // 动态分配存储空间
|
||||
int front; // 头指针,若队列不空,指向队头元素
|
||||
int rear; // 尾指针,若队列不空,指向队列尾元素的下一个位置
|
||||
} SqQueue;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的顺序队列。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列是循环队列
|
||||
*/
|
||||
Status InitQueue(SqQueue* Q);
|
||||
|
||||
/*
|
||||
* 销毁(结构)
|
||||
*
|
||||
* 释放循环顺序队列所占内存。
|
||||
*/
|
||||
Status ClearQueue(SqQueue* Q);
|
||||
|
||||
/*
|
||||
* 置空(内容)
|
||||
*
|
||||
* 只是清理循环顺序队列中存储的数据,不释放顺序栈所占内存。
|
||||
*/
|
||||
Status DestroyQueue(SqQueue* Q);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断循环顺序队列中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 循环顺序队列为空
|
||||
* FALSE: 循环顺序队列不为空
|
||||
*/
|
||||
Status QueueEmpty(SqQueue Q);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回循环顺序队列包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(SqQueue Q);
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(SqQueue Q, QElemType* e);
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(SqQueue* Q, QElemType e);
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(SqQueue* Q, QElemType* e);
|
||||
|
||||
/*
|
||||
* 遍历
|
||||
*
|
||||
* 用visit函数访问队列Q
|
||||
*/
|
||||
Status QueueTraverse(SqQueue Q, void(Visit)(QElemType));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "BankQueuing.h" //**▲03 栈和队列**//
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Bank_Simulation_1(); //算法3.6
|
||||
|
||||
// Bank_Simulation_2(); //算法3.7,跟3.6类似
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
/*==================
|
||||
* 模拟银行排队
|
||||
*
|
||||
* 包含算法: 3.6、3.7
|
||||
===================*/
|
||||
|
||||
#ifndef BANKQUEUING_C
|
||||
#define BANKQUEUING_C
|
||||
|
||||
#include "BankQueuing.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* ████████ 算法3.6 ████████
|
||||
*
|
||||
* 银行业务模拟,统计一天内客户在银行逗留的平均时间
|
||||
*/
|
||||
void Bank_Simulation_1()
|
||||
{
|
||||
char eventType;
|
||||
|
||||
OpenForDay(); // 银行开门
|
||||
|
||||
// 如果事件表不为空
|
||||
while(MoreEvent()) {
|
||||
EventDrived(&eventType); // 事件驱动
|
||||
|
||||
switch(eventType) {
|
||||
case 'A':
|
||||
CustomerArrived(); // 处理客户到达事件
|
||||
break;
|
||||
case 'D':
|
||||
CustomerDeparture(); // 处理客户离开事件
|
||||
break;
|
||||
default :
|
||||
Invalid();
|
||||
}
|
||||
}
|
||||
|
||||
CloseForDay();
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法3.7 ████████
|
||||
*
|
||||
* 银行业务模拟,统计一天内客户在银行逗留的平均时间
|
||||
* 算法内容与3.7是类似的
|
||||
*/
|
||||
void Bank_Simulation_2() {
|
||||
OpenForDay(); // 银行开门
|
||||
|
||||
while(!ListEmpty(gEv)) {
|
||||
ListDelete(gEv, 1, &gEn);
|
||||
|
||||
if(gEn.NType == Arrive) {
|
||||
CustomerArrived(); // 处理客户到达事件
|
||||
} else {
|
||||
CustomerDeparture(); // 处理客户离开事件
|
||||
}
|
||||
}
|
||||
|
||||
CloseForDay(); // 银行关门
|
||||
}
|
||||
|
||||
/*
|
||||
* 银行开门,初始化运行环境
|
||||
*/
|
||||
void OpenForDay() {
|
||||
int i;
|
||||
|
||||
// 关门时间,假设银行每天营业8小时,480分
|
||||
gCloseTime = 480;
|
||||
|
||||
// 初始化累计时间和客户数为0
|
||||
gTotalTime = 0;
|
||||
gCustomerNum = 0;
|
||||
|
||||
// 初始化事件链表为空表
|
||||
InitList(&gEv);
|
||||
|
||||
// 设定第一个客户到达事件
|
||||
gEn.OccurTime = 0;
|
||||
gEn.NType = Arrive;
|
||||
|
||||
// 插入事件表
|
||||
OrderInsert(gEv, gEn, cmp);
|
||||
|
||||
// 初始化4个空队列
|
||||
for(i = 1; i <= N; ++i) {
|
||||
InitQueue(&gQ[i]);
|
||||
}
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
/*
|
||||
* 银行关门
|
||||
*
|
||||
* 释放资源,打印统计信息
|
||||
*/
|
||||
void CloseForDay() {
|
||||
printf("当天总共有%d个客户,平均逗留时间为%d分钟。\n", gCustomerNum, gTotalTime / gCustomerNum);
|
||||
}
|
||||
|
||||
/*
|
||||
* 判断事件表是否为空。
|
||||
* 即是否存在未处理的事件。
|
||||
*/
|
||||
Status MoreEvent() {
|
||||
return !ListEmpty(gEv);
|
||||
}
|
||||
|
||||
/*
|
||||
* 将待处理事件从事件表中移除,并将该事件存储到全局变量gEn中。
|
||||
* event用来存储该事件的类型
|
||||
*/
|
||||
void EventDrived(char* eventType) {
|
||||
// 从事件表中获取待处理事件
|
||||
ListDelete(gEv, 1, &gEn);
|
||||
|
||||
// 识别事件类型
|
||||
if(gEn.NType == Arrive) {
|
||||
*eventType = 'A';
|
||||
} else {
|
||||
*eventType = 'D';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理客户到达事件,gEn.NType=0
|
||||
*/
|
||||
void CustomerArrived() {
|
||||
Event en; // 事件
|
||||
QElemType customer; // 客户记录
|
||||
|
||||
int durtime; // 当前客户办理业务需要的时间
|
||||
|
||||
int intertime; // 下一个客户达到时间间隔
|
||||
int t; // 下一个客户到达时间
|
||||
|
||||
int i; // 队列编号
|
||||
|
||||
// 总客户数增一
|
||||
++gCustomerNum;
|
||||
|
||||
// 生成当前客户办理业务需要的时间和下一个客户达到时间间隔
|
||||
Random(&durtime, &intertime);
|
||||
|
||||
// 下一个客户到达时间
|
||||
t = gEn.OccurTime + intertime;
|
||||
|
||||
// 如果银行尚未关门,将下一客户"到达"事件插入事件表
|
||||
if(t < gCloseTime) {
|
||||
en.OccurTime = t; // 下一客户的到达时间
|
||||
en.NType = Arrive; // "到达"事件类型
|
||||
OrderInsert(gEv, en, cmp); // "到达"事件插入事件表
|
||||
}
|
||||
|
||||
// 获取当前长度最短的队列编号
|
||||
i = Minimum(gQ);
|
||||
|
||||
// 记录当前客户信息
|
||||
customer.ArrivedTime = gEn.OccurTime; // 到达时间
|
||||
customer.Duration = durtime; // 办理业务所需时间
|
||||
customer.Count = gCustomerNum; // 客户编号
|
||||
|
||||
// 当前客户进入最短队列排队
|
||||
EnQueue(&gQ[i], customer);
|
||||
printf("第%3d个客户到队列 %d 中排队...\n", customer.Count, i);
|
||||
Show();
|
||||
|
||||
/*
|
||||
* 如果当前队列中只有这一个客户排队,则需要计算其离开时间,
|
||||
* 并构造一个"离开"事件插入到事件表中
|
||||
*/
|
||||
if(QueueLength(gQ[i]) == 1) {
|
||||
en.OccurTime = gEn.OccurTime + durtime; // 当前客户的离开时间
|
||||
en.NType = i; // "离开"事件类型,值为1-4,指示从第几个队列中离开
|
||||
OrderInsert(gEv, en, cmp); // "离开"事件插入事件表
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 处理客户离开事件,gEn.NType>0
|
||||
*/
|
||||
void CustomerDeparture() {
|
||||
Event en; // 事件
|
||||
QElemType customer; // 客户记录
|
||||
int i = gEn.NType; // 队列编号
|
||||
|
||||
// 第i个队列的队头客户完成业务并出队
|
||||
DeQueue(&gQ[i], &customer);
|
||||
printf("第%3d个客户从队列 %d 中离开...\n", customer.Count, i);
|
||||
Show();
|
||||
|
||||
// 累计客户逗留时间
|
||||
gTotalTime += gEn.OccurTime - customer.ArrivedTime;
|
||||
|
||||
/*
|
||||
* 如果当前队列中仍然存在排队的客户,则需要计算该队列中队头客户的离开时间
|
||||
* 注:之所以在这里计算,是因为只有上一个客户离开了,下一个客户的离开时间才会有意义
|
||||
*/
|
||||
if(!QueueEmpty(gQ[i])) {
|
||||
// 获取队头客户
|
||||
GetHead(gQ[i], &customer);
|
||||
en.OccurTime = gEn.OccurTime + customer.Duration; // "离开"事件发生的时间
|
||||
en.NType = i; // "离开"事件类型
|
||||
OrderInsert(gEv, en, cmp); // "离开"事件插入事件表
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 代表遇到了无效的事件
|
||||
*/
|
||||
void Invalid() {
|
||||
printf("运行错误!");
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
/*
|
||||
* 将事件en插入到事件表ev中(ev是按发生时间从早到晚排列的事件表)
|
||||
* cmp用来比较两个事件发生的早晚,en会作为第二个实参传进去
|
||||
*/
|
||||
Status OrderInsert(EventList ev, Event en, int(cmp)(Event, Event)) {
|
||||
EventList p, pre, s;
|
||||
|
||||
if(ev == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for(pre = ev; pre->next != NULL && cmp(pre->next->data, en) < 0; pre = pre->next) {
|
||||
// 查找
|
||||
}
|
||||
|
||||
s = (LinkList) malloc(sizeof(LNode));
|
||||
if(s == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
s->data = en;
|
||||
|
||||
s->next = pre->next;
|
||||
pre->next = s;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 比较两事件发生次序
|
||||
*/
|
||||
int cmp(Event a, Event b) {
|
||||
if(a.OccurTime < b.OccurTime) {
|
||||
return -1; // a发生比较早
|
||||
} else if(a.OccurTime > b.OccurTime) {
|
||||
return 1; // a发生比较晚
|
||||
} else {
|
||||
return 0; // 同时发生
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 生成随机数
|
||||
*
|
||||
* durtime :当前客服办理业务所需时间
|
||||
* intertime:下一客户到达间隔的时间
|
||||
*/
|
||||
void Random(int* durtime, int* intertime) {
|
||||
srand((unsigned) time(NULL));
|
||||
*durtime = rand() % DurationTime + 1; // 办业务时间持续1到20分钟
|
||||
*intertime = rand() % IntervalTime + 1; // 下一个顾客到来的时间为间隔1到10分钟
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回长度最短的队列的序号
|
||||
*/
|
||||
int Minimum() {
|
||||
int i1 = QueueLength(gQ[1]);
|
||||
int i2 = QueueLength(gQ[2]);
|
||||
int i3 = QueueLength(gQ[3]);
|
||||
int i4 = QueueLength(gQ[4]);
|
||||
|
||||
if(i1 <= i2 && i1 <= i3 && i1 <= i4) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(i2 < i1 && i2 <= i3 && i2 <= i4) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(i3 < i1 && i3 < i2 && i3 <= i4) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(i4 < i1 && i4 < i2 && i4 < i3) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 显示所有客户队列的排队情况
|
||||
*/
|
||||
void Show() {
|
||||
int i;
|
||||
QueuePtr p; // 记录到来的客户是第几个
|
||||
|
||||
// 遍历所有客户队列
|
||||
for(i = 1; i <= N; i++) {
|
||||
for(p = gQ[i].front; p; p = p->next) {
|
||||
if(p == gQ[i].front) {
|
||||
if(i == 1) {
|
||||
printf("柜台①●");
|
||||
}
|
||||
if(i == 2) {
|
||||
printf("柜台②●");
|
||||
}
|
||||
if(i == 3) {
|
||||
printf("柜台③●");
|
||||
}
|
||||
if(i == 4) {
|
||||
printf("柜台④●");
|
||||
}
|
||||
} else {
|
||||
printf("(%03d)", p->data.Count);
|
||||
}
|
||||
|
||||
|
||||
if(p == gQ[i].rear) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
Wait(SleepTime);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,121 @@
|
||||
/*==================
|
||||
* 模拟银行排队
|
||||
*
|
||||
* 包含算法: 3.6、3.7
|
||||
===================*/
|
||||
|
||||
#ifndef BANKQUEUING_H
|
||||
#define BANKQUEUING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include <time.h> // 提供time原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "LinkList.h" //**▲02 线性表**//
|
||||
#include "LinkQueue.h" //**▲03 栈和队列**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define N 4 // 客户队列数量
|
||||
#define SleepTime 1 // SleepTime代表休眠时间
|
||||
#define DurationTime 20 // 办理业务持续时间从1到DurationTime分钟不等
|
||||
#define IntervalTime 8 // 下一个客户到来时间间隔为1到IntervalTime分钟不等
|
||||
|
||||
/* 类型定义 */
|
||||
typedef LinkList EventList; //事件链表类型,定义为有序链表
|
||||
|
||||
/* 全局变量(变量名称前面都加了g标记) */
|
||||
int gTotalTime; // 累计客户数
|
||||
int gCustomerNum; // 累计客户逗留时间
|
||||
|
||||
int gCloseTime; // 关门时间,假设银行每天营业8小时,480分
|
||||
|
||||
EventList gEv; // 事件表,存储所有待处理事件
|
||||
Event gEn; // 当前正在处理的事件
|
||||
|
||||
LinkQueue gQ[N+1]; // 4个客户队列,0号单元弃用
|
||||
|
||||
|
||||
/*
|
||||
* ████████ 算法3.6 ████████
|
||||
*
|
||||
* 银行业务模拟,统计一天内客户在银行逗留的平均时间
|
||||
*/
|
||||
void Bank_Simulation_1();
|
||||
|
||||
/*
|
||||
* ████████ 算法3.7 ████████
|
||||
*
|
||||
* 银行业务模拟,统计一天内客户在银行逗留的平均时间
|
||||
* 算法内容与3.7是类似的
|
||||
*/
|
||||
void Bank_Simulation_2();
|
||||
|
||||
/*
|
||||
* 银行开门,初始化运行环境
|
||||
*/
|
||||
void OpenForDay();
|
||||
|
||||
/*
|
||||
* 银行关门
|
||||
*
|
||||
* 释放资源,打印统计信息
|
||||
*/
|
||||
void CloseForDay();
|
||||
|
||||
/*
|
||||
* 判断事件表是否为空。
|
||||
* 即是否存在未处理的事件。
|
||||
*/
|
||||
Status MoreEvent();
|
||||
|
||||
/*
|
||||
* 将待处理事件从事件表中移除,并将该事件存储到全局变量gEn中。
|
||||
* event用来存储该事件的类型
|
||||
*/
|
||||
void EventDrived(char* event);
|
||||
|
||||
/*
|
||||
* 处理客户到达事件,gEn.NType=0
|
||||
*/
|
||||
void CustomerArrived();
|
||||
|
||||
/*
|
||||
* 处理客户离开事件,gEn.NType>0
|
||||
*/
|
||||
void CustomerDeparture();
|
||||
|
||||
/*
|
||||
* 代表遇到了无效的事件
|
||||
*/
|
||||
void Invalid();
|
||||
|
||||
/*
|
||||
* 将事件en插入到事件表ev中(ev是按发生时间从早到晚排列的事件表)
|
||||
* cmp用来比较两个事件发生的早晚,en会作为第二个实参传进去
|
||||
*/
|
||||
Status OrderInsert(EventList gEv, Event gEn, int(cmp)(Event, Event));
|
||||
|
||||
/*
|
||||
* 比较两事件发生次序
|
||||
*/
|
||||
int cmp(Event a, Event b);
|
||||
|
||||
/*
|
||||
* 生成随机数
|
||||
*
|
||||
* durtime :当前客服办理业务所需时间
|
||||
* intertime:下一客户到达间隔的时间
|
||||
*/
|
||||
void Random(int* durtime, int* intertime);
|
||||
|
||||
/*
|
||||
* 返回长度最短的队列的序号
|
||||
*/
|
||||
int Minimum();
|
||||
|
||||
/*
|
||||
* 显示所有客户队列的排队情况
|
||||
*/
|
||||
void Show();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(BankQueuing LinkList.h LinkList.c LinkQueue.h LinkQueue.c BankQueuing.h BankQueuing.c BankQueuing-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(BankQueuing Scanf_lib)
|
||||
@@ -0,0 +1,130 @@
|
||||
/*===============================
|
||||
* 线性表的链式存储结构(链表)
|
||||
*
|
||||
* 包含算法: 2.8、2.9、2.10、2.11
|
||||
================================*/
|
||||
|
||||
#include "LinkList.h" //**▲02 线性表**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 只是初始化一个头结点。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitList(LinkList* L) {
|
||||
(*L) = (LinkList) malloc(sizeof(LNode));
|
||||
if(*L == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*L)->next = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链表中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链表为空
|
||||
* FALSE: 链表不为空
|
||||
*/
|
||||
Status ListEmpty(LinkList L) {
|
||||
// 链表只有头结点时,认为该链表为空
|
||||
if(L != NULL && L->next == NULL) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法2.9 ████████
|
||||
*
|
||||
* 插入
|
||||
*
|
||||
* 向链表第i个位置上插入e,插入成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【备注】
|
||||
* 教材中i的含义是元素位置,从1开始计数
|
||||
*/
|
||||
Status ListInsert(LinkList L, int i, ElemType e) {
|
||||
LinkList p, s;
|
||||
int j;
|
||||
|
||||
// 确保链表存
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = L;
|
||||
j = 0;
|
||||
|
||||
// 寻找第i-1个结点,且保证该结点本身不为NULL
|
||||
while(p != NULL && j < i - 1) {
|
||||
p = p->next;
|
||||
++j;
|
||||
}
|
||||
|
||||
// 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
|
||||
if(p == NULL || j > i - 1) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 生成新结点
|
||||
s = (LinkList) malloc(sizeof(LNode));
|
||||
if(s == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法2.10 ████████
|
||||
*
|
||||
* 删除
|
||||
*
|
||||
* 删除链表第i个位置上的元素,并将被删除元素存储到e中。
|
||||
* 删除成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【备注】
|
||||
* 教材中i的含义是元素位置,从1开始计数
|
||||
*/
|
||||
Status ListDelete(LinkList L, int i, ElemType* e) {
|
||||
LinkList p, q;
|
||||
int j;
|
||||
|
||||
// 确保链表存在且不为空表
|
||||
if(L == NULL || L->next == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = L;
|
||||
j = 0;
|
||||
|
||||
// 寻找第i-1个结点,且保证该结点的后继不为NULL
|
||||
while(p->next != NULL && j < i - 1) {
|
||||
p = p->next;
|
||||
++j;
|
||||
}
|
||||
|
||||
// 如果遍历到头了,或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
|
||||
if(p->next == NULL || j > i - 1) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 删除第i个结点
|
||||
q = p->next;
|
||||
p->next = q->next;
|
||||
*e = q->data;
|
||||
free(q);
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*===============================
|
||||
* 线性表的链式存储结构(链表)
|
||||
*
|
||||
* 包含算法: 2.8、2.9、2.10、2.11
|
||||
================================*/
|
||||
|
||||
#ifndef LINKLIST_H
|
||||
#define LINKLIST_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
// 事件类型枚举常量,0代表到达事件,1至4表示四个窗口的离开事件
|
||||
typedef enum {
|
||||
Arrive, Leave_1, Leave_2, Leave_3, Leave_4
|
||||
} EventType;
|
||||
|
||||
/* 事件链表元素类型定义 */
|
||||
typedef struct
|
||||
{
|
||||
int OccurTime; // 事件发生时刻
|
||||
EventType NType; // 事件类型
|
||||
} Event, ElemType; // 事件链表元素
|
||||
|
||||
/*
|
||||
* 单链表结构
|
||||
*
|
||||
* 注:这里的单链表存在头结点
|
||||
*/
|
||||
typedef struct LNode {
|
||||
ElemType data; // 数据结点
|
||||
struct LNode* next; // 指向下一个结点的指针
|
||||
} LNode;
|
||||
|
||||
// 指向单链表结点的指针
|
||||
typedef LNode* LinkList;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*/
|
||||
Status InitList(LinkList* L);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链表中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链表为空
|
||||
* FALSE: 链表不为空
|
||||
*/
|
||||
Status ListEmpty(LinkList L);
|
||||
|
||||
/*
|
||||
* ████████ 算法2.9 ████████
|
||||
*
|
||||
* 插入
|
||||
*
|
||||
* 向链表第i个位置上插入e,插入成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【备注】
|
||||
* 教材中i的含义是元素位置,从1开始计数
|
||||
*/
|
||||
Status ListInsert(LinkList L, int i, ElemType e);
|
||||
|
||||
/*
|
||||
* ████████ 算法2.10 ████████
|
||||
*
|
||||
* 删除
|
||||
*
|
||||
* 删除链表第i个位置上的元素,并将被删除元素存储到e中。
|
||||
* 删除成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【备注】
|
||||
* 教材中i的含义是元素位置,从1开始计数
|
||||
*/
|
||||
Status ListDelete(LinkList L, int i, ElemType* e);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*=========================
|
||||
* 队列的链式存储结构(链队)
|
||||
==========================*/
|
||||
|
||||
#ifndef LINKQUEUE_C
|
||||
#define LINKQUEUE_C
|
||||
|
||||
#include "LinkQueue.h" //**▲03 栈和队列**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的链队。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列带有头结点
|
||||
*/
|
||||
Status InitQueue(LinkQueue* Q) {
|
||||
if(Q == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
(*Q).front = (*Q).rear = (QueuePtr) malloc(sizeof(QNode));
|
||||
if(!(*Q).front) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
(*Q).front->next = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链队中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链队为空
|
||||
* FALSE: 链队不为空
|
||||
*/
|
||||
Status QueueEmpty(LinkQueue Q) {
|
||||
if(Q.front == Q.rear) {
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回链队包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(LinkQueue Q) {
|
||||
int count = 0;
|
||||
QueuePtr p = Q.front;
|
||||
|
||||
while(p != Q.rear) {
|
||||
count++;
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(LinkQueue Q, QElemType* e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q.front == NULL || Q.front == Q.rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = Q.front->next;
|
||||
*e = p->data;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(LinkQueue* Q, QElemType e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q == NULL || (*Q).front == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = (QueuePtr) malloc(sizeof(QNode));
|
||||
if(!p) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
p->data = e;
|
||||
p->next = NULL;
|
||||
|
||||
(*Q).rear->next = p;
|
||||
(*Q).rear = p;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(LinkQueue* Q, QElemType* e) {
|
||||
QueuePtr p;
|
||||
|
||||
if(Q == NULL || (*Q).front == NULL || (*Q).front == (*Q).rear) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
p = (*Q).front->next;
|
||||
*e = p->data;
|
||||
|
||||
(*Q).front->next = p->next;
|
||||
if((*Q).rear == p) {
|
||||
(*Q).rear = (*Q).front;
|
||||
}
|
||||
|
||||
free(p);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
/*=========================
|
||||
* 队列的链式存储结构(链队)
|
||||
==========================*/
|
||||
|
||||
#ifndef LINKQUEUE_H
|
||||
#define LINKQUEUE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 链队元素类型定义,这里记录客户信息 */
|
||||
typedef struct {
|
||||
int ArrivedTime; // 客户到达时间
|
||||
int Duration; // 办理业务所需的时间
|
||||
int Count; // 此变量记录来到每个队列的客户是第几个【教材中无此变量,增加此变量的目的是观察排队状况。】
|
||||
} QElemType; //队列的数据元素类型
|
||||
|
||||
// 队列元素结构
|
||||
typedef struct QNode {
|
||||
QElemType data;
|
||||
struct QNode* next;
|
||||
} QNode, * QueuePtr;
|
||||
|
||||
// 队列结构
|
||||
typedef struct {
|
||||
QueuePtr front; // 队头指针
|
||||
QueuePtr rear; // 队尾指针
|
||||
} LinkQueue; // 队列的链式存储表示
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个空的链队。
|
||||
* 初始化成功则返回OK,否则返回ERROR。
|
||||
*
|
||||
*【注】
|
||||
* 这里的队列带有头结点
|
||||
*/
|
||||
Status InitQueue(LinkQueue* Q);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断链队中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 链队为空
|
||||
* FALSE: 链队不为空
|
||||
*/
|
||||
Status QueueEmpty(LinkQueue Q);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回链队包含的有效元素的数量。
|
||||
*/
|
||||
int QueueLength(LinkQueue Q);
|
||||
|
||||
/*
|
||||
* 取值
|
||||
*
|
||||
* 获取队头元素,将其存储到e中。
|
||||
* 如果可以找到,返回OK,否则,返回ERROR。
|
||||
*/
|
||||
Status GetHead(LinkQueue Q, QElemType* e);
|
||||
|
||||
/*
|
||||
* 入队
|
||||
*
|
||||
* 将元素e添加到队列尾部。
|
||||
*/
|
||||
Status EnQueue(LinkQueue* Q, QElemType e);
|
||||
|
||||
/*
|
||||
* 出队
|
||||
*
|
||||
* 移除队列头部的元素,将其存储到e中。
|
||||
*/
|
||||
Status DeQueue(LinkQueue* Q, QElemType* e);
|
||||
|
||||
#endif
|
||||
@@ -9,3 +9,13 @@ add_subdirectory(0208_DuLinkList)
|
||||
add_subdirectory(0209_ELinkList)
|
||||
add_subdirectory(0210_MergeEList)
|
||||
add_subdirectory(0211_Polynomial)
|
||||
|
||||
add_subdirectory(0301_SqStack)
|
||||
add_subdirectory(0302_Conversion)
|
||||
add_subdirectory(0303_LineEdit)
|
||||
add_subdirectory(0304_Maze)
|
||||
add_subdirectory(0305_Expression)
|
||||
add_subdirectory(0306_Hanoi)
|
||||
add_subdirectory(0307_LinkQueue)
|
||||
add_subdirectory(0308_SqQueue)
|
||||
add_subdirectory(0309_BankQueuing)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include "SqStack.h" //**▲03 栈和队列**//
|
||||
|
||||
// 测试函数,打印元素
|
||||
void PrintElem(SElemType e);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
SqStack S;
|
||||
int i;
|
||||
SElemType e;
|
||||
|
||||
printf("████████ 函数 InitStack \n");
|
||||
{
|
||||
printf("█ 初始化顺序栈 S ...\n");
|
||||
InitStack(&S);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackEmpty \n");
|
||||
{
|
||||
StackEmpty(S) ? printf("█ S 为空!!\n") : printf("█ S 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 Push \n");
|
||||
{
|
||||
for(i = 1; i <= 6; i++) {
|
||||
Push(&S, 2 * i);
|
||||
printf("█ 将 \"%2d\" 压入栈 S ...\n", 2 * i);
|
||||
}
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackTraverse \n");
|
||||
{
|
||||
printf("█ S 中的元素为:S = ");
|
||||
StackTraverse(S, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 StackLength \n");
|
||||
{
|
||||
i = StackLength(S);
|
||||
printf("█ S 的长度为 %d \n", i);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 Pop \n");
|
||||
{
|
||||
Pop(&S, &e);
|
||||
printf("█ 栈顶元素 \"%d\" 出栈...\n", e);
|
||||
printf("█ S 中的元素为:S = ");
|
||||
StackTraverse(S, PrintElem);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 GetTop \n");
|
||||
{
|
||||
GetTop(S, &e);
|
||||
printf("█ 栈顶元素的值为 \"%d\" \n", e);
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 ClearStack \n");
|
||||
{
|
||||
printf("█ 清空 S 前:");
|
||||
StackEmpty(S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
|
||||
|
||||
ClearStack(&S);
|
||||
|
||||
printf("█ 清空 S 后:");
|
||||
StackEmpty(S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
printf("████████ 函数 DestroyStack \n");
|
||||
{
|
||||
printf("█ 销毁 S 前:");
|
||||
S.base != NULL && S.top != NULL ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
|
||||
|
||||
DestroyStack(&S);
|
||||
|
||||
printf("█ 销毁 S 后:");
|
||||
S.base != NULL && S.top != NULL ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
|
||||
}
|
||||
PressEnterToContinue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 测试函数,打印元素
|
||||
void PrintElem(SElemType e) {
|
||||
printf("%d ", e);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user