♻️ 线性表章节的源码重构

This commit is contained in:
康建伟
2019-10-20 19:06:37 +08:00
parent 372c4d28df
commit 2ea439c0bc
653 changed files with 51720 additions and 11 deletions

8
CLion/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.14)
project(CLion C)
set(CMAKE_C_STANDARD 90)
add_subdirectory(Status)
add_subdirectory(CourseBook)
add_subdirectory(ExerciseBook)

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(SqList SqList.h SqList.c SqList-main.c)
# 链接公共库
target_link_libraries(SqList Scanf_lib)

View File

@@ -0,0 +1,170 @@
#include <stdio.h>
#include "SqList.h" //**02 线性表**//
// 判断data>e是否成立
Status CmpGreater(ElemType data, ElemType e) {
return data > e ? TRUE : FALSE;
}
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
SqList L; // 待操作的顺序表
int i;
ElemType e;
printf("████████ InitList \n");
{
printf("█ 初始化顺序表 L ...\n");
InitList(&L);
}
PressEnterToContinue();
printf("████████ ListEmpty \n");
{
if(ListEmpty(L) == TRUE) {
printf("█ L 为空!!\n");
} else {
printf("█ L 不为空!\n");
}
}
PressEnterToContinue();
printf("████████ ListInsert \n");
{
for(i = 1; i <= 8; i++) {
printf("█ 作为示范,在 L 第 %d 个位置插入 \"%d\"...\n", i, 2 * i);
ListInsert(&L, i, 2 * i);
}
}
PressEnterToContinue();
printf("████████ ListTraverse \n");
{
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ ListLength \n");
{
i = ListLength(L);
printf("█ L 的长度为 %d \n", i);
}
PressEnterToContinue();
printf("████████ ListDelete \n");
{
printf("█ 删除前的元素L = ");
ListTraverse(L, PrintElem);
printf("█ 尝试删除 L 中第 6 个元素...\n");
if(ListDelete(&L, 6, &e) == OK) {
printf("█ 删除成功,被删除元素是:\"%d\"\n", e);
} else {
printf("█ 删除失败,第 6 个元素不存在!\n");
}
printf("█ 删除后的元素L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ GetElem \n");
{
GetElem(L, 4, &e);
printf("█ L 中第 4 个位置的元素为 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ LocateElem \n");
{
i = LocateElem(L, 7, CmpGreater);
printf("█ L 中第一个元素值大于 \"7\" 的元素是 \"%d\" \n", L.elem[i - 1]);
}
PressEnterToContinue();
printf("████████ PriorElem \n");
{
ElemType cur_e = 6;
if(PriorElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的前驱为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的前驱不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ NextElem \n");
{
ElemType cur_e = 6;
if(NextElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的后继为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的后继不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ ClearList \n");
{
printf("█ 清空 L 前:");
if(ListEmpty(L) == TRUE) {
printf(" L 为空!!\n");
} else {
printf(" L 不为空!\n");
}
ClearList(&L);
printf("█ 清空 L 后:");
if(ListEmpty(L) == TRUE) {
printf(" L 为空!!\n");
} else {
printf(" L 不为空!\n");
}
}
PressEnterToContinue();
printf("████████ DestroyList \n");
{
printf("█ 销毁 L 前:");
if(L.elem != NULL) {
printf(" L 存在!\n");
} else {
printf(" L 不存在!!\n");
}
DestroyList(&L);
printf("█ 销毁 L 后:");
if(L.elem != NULL) {
printf(" L 存在!\n");
} else {
printf(" L 不存在!!\n");
}
}
PressEnterToContinue();
return 0;
}

View File

@@ -0,0 +1,337 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#include "SqList.h"
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L) {
// 分配指定容量的内存如果分配失败则返回NULL
(*L).elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType));
if((*L).elem == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
(*L).length = 0; // 初始化顺序表长度为0
(*L).listsize = LIST_INIT_SIZE; // 顺序表初始内存分配量
return OK; // 初始化成功
}
/*
* 销毁(结构)
*
* 释放顺序表所占内存。
*/
Status DestroyList(SqList* L) {
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
// 释放顺序表内存
free((*L).elem);
// 释放内存后置空指针
(*L).elem = NULL;
// 顺序表长度跟容量都归零
(*L).length = 0;
(*L).listsize = 0;
return OK;
}
/*
* 置空(内容)
*
* 只是清理顺序表中存储的数据,不释放顺序表所占内存。
*/
Status ClearList(SqList* L) {
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
(*L).length = 0;
return OK;
}
/*
* 判空
*
* 判断顺序表中是否包含有效数据。
*
* 返回值:
* TRUE : 顺序表为空
* FALSE: 顺序表不为空
*/
Status ListEmpty(SqList L) {
return L.length == 0 ? TRUE : FALSE;
}
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L) {
return L.length;
}
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e) {
// 因为i的含义是位置所以其合法范围是[1, length]
if(i < 1 || i > L.length) {
return ERROR; //i值不合法
}
*e = L.elem[i - 1];
return OK;
}
/*
* ████████ 算法2.6 ████████
*
* 查找
*
* 返回顺序表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType)) {
int i;
ElemType* p;
// 确保顺序表结构存在
if(L.elem == NULL) {
return ERROR;
}
/*
* i的初值为第1个元素的位序
*
* 其实更自然的写法是将i初始化为第1个元素的索引
* 但由于教材中是按位序计数的,所以这里仍写作位序
*/
i = 1;
// p的初值为第1个元素的存储位置
p = L.elem;
// 遍历顺序表
while(i <= L.length && !Compare(*p++, e)) {
++i;
}
if(i <= L.length) {
return i;
} else {
return 0;
}
}
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(SqList L, ElemType cur_e, ElemType* pre_e) {
int i;
// 确保顺序表结构存在,且最少包含两个元素
if(L.elem == NULL || L.length < 2) {
return ERROR;
}
// 这里的i初始化为第1个元素的【索引】
i = 0;
// 从第1个元素开始查找cur_e的位置
while(i < L.length && L.elem[i] != cur_e) {
++i;
}
// 如果cur_e是首个元素(没有前驱)或者没找到元素cur_e返回ERROR
if(i==0 || i >= L.length) {
return ERROR;
}
// 存储cur_e的前驱
*pre_e = L.elem[i - 1];
return OK;
}
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(SqList L, ElemType cur_e, ElemType* next_e) {
int i;
// 确保顺序表结构存在,且最少包含两个元素
if(L.elem == NULL || L.length < 2) {
return ERROR;
}
// 这里的i初始化为第1个元素的【索引】
i = 0;
// 从第1个元素开始查找cur_e的位置
while(i < L.length-1 && L.elem[i] != cur_e) {
++i;
}
// 如果cur_e是最后1个元素(没有前驱)或者没找到元素cur_e返回ERROR
if(i >= L.length-1) {
return ERROR;
}
// 存储cur_e的前驱
*next_e = L.elem[i + 1];
return OK;
}
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e) {
ElemType* newbase;
ElemType* p, * q;
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
// i值越界
if(i < 1 || i > (*L).length + 1) {
return ERROR;
}
// 若存储空间已满,则增加新空间
if((*L).length >= (*L).listsize) {
// 基于现有空间扩容
newbase = (ElemType*) realloc((*L).elem, ((*L).listsize + LISTINCREMENT) * sizeof(ElemType));
if(newbase == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
// 新基址
(*L).elem = newbase;
// 存的存储空间
(*L).listsize += LISTINCREMENT;
}
// q为插入位置
q = &(*L).elem[i - 1];
// 1.右移元素,腾出位置
for(p = &(*L).elem[(*L).length - 1]; p >= q; --p) {
*(p + 1) = *p;
}
// 2.插入e
*q = e;
// 3.表长增1
(*L).length++;
return OK;
}
/*
* ████████ 算法2.5 ████████
*
* 删除
*
* 删除顺序表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(SqList* L, int i, ElemType* e) {
ElemType* p, * q;
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
// i值越界
if(i < 1 || i > (*L).length) {
return ERROR;
}
// p为被删除元素的位置
p = &(*L).elem[i - 1];
// 1.获取被删除元素
*e = *p;
// 表尾元素位置
q = (*L).elem + (*L).length - 1;
// 2.左移元素,被删除元素的位置上会有新元素进来
for(++p; p <= q; ++p) {
*(p - 1) = *p;
}
// 3.表长减1
(*L).length--;
return OK;
}
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void(Visit)(ElemType)) {
int i;
for(i = 0; i < L.length; i++) {
Visit(L.elem[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,149 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#ifndef SEQUENCELIST_H
#define SEQUENCELIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define LIST_INIT_SIZE 100 // 顺序表存储空间的初始分配量
#define LISTINCREMENT 10 // 顺序表存储空间的分配增量
/* 顺序表元素类型定义 */
typedef int ElemType;
/*
* 顺序表结构
*
* 注elem在使用前需要先为其分配内存且元素从elem[0]处开始存储
*/
typedef struct {
ElemType* elem; // 顺序表存储空间的基址(指向顺序表所占内存的起始位置)
int length; // 当前顺序表长度(包含多少元素)
int listsize; // 当前分配的存储容量(可以存储多少元素)
} SqList;
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L);
/*
* 销毁(结构)
*
* 释放顺序表所占内存。
*/
Status DestroyList(SqList* L);
/*
* 置空(内容)
*
* 只是清理顺序表中存储的数据,不释放顺序表所占内存。
*/
Status ClearList(SqList* L);
/*
* 判空
*
* 判断顺序表中是否包含有效数据。
*
* 返回值:
* TRUE : 顺序表为空
* FALSE: 顺序表不为空
*/
Status ListEmpty(SqList L);
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L);
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e);
/*
* ████████ 算法2.6 ████████
*
* 查找
*
* 返回顺序表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(SqList L, ElemType cur_e, ElemType* pre_e);
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(SqList L, ElemType cur_e, ElemType* next_e);
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e);
/*
* ████████ 算法2.5 ████████
*
* 删除
*
* 删除顺序表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(SqList* L, int i, ElemType* e);
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void (Visit)(ElemType));
#endif

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(Union SqList.h SqList.c Union.h Union.c Union-main.c)
# 链接公共库
target_link_libraries(Union Scanf_lib)

View File

@@ -0,0 +1,172 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#include "SqList.h"
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L) {
// 分配指定容量的内存如果分配失败则返回NULL
(*L).elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType));
if((*L).elem == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
(*L).length = 0; // 初始化顺序表长度为0
(*L).listsize = LIST_INIT_SIZE; // 顺序表初始内存分配量
return OK; // 初始化成功
}
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L) {
return L.length;
}
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e) {
// 因为i的含义是位置所以其合法范围是[1, length]
if(i < 1 || i > L.length) {
return ERROR; //i值不合法
}
*e = L.elem[i - 1];
return OK;
}
/*
* ████████ 算法2.6 ████████
*
* 查找
*
* 返回顺序表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType)) {
int i;
ElemType* p;
// 确保顺序表结构存在
if(L.elem == NULL) {
return ERROR;
}
/*
* i的初值为第1个元素的位序
*
* 其实更自然的写法是将i初始化为第1个元素的索引
* 但由于教材中是按位序计数的,所以这里仍写作位序
*/
i = 1;
// p的初值为第1个元素的存储位置
p = L.elem;
// 遍历顺序表
while(i <= L.length && !Compare(*p++, e)) {
++i;
}
if(i <= L.length) {
return i;
} else {
return 0;
}
}
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e) {
ElemType* newbase;
ElemType* p, * q;
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
// i值越界
if(i < 1 || i > (*L).length + 1) {
return ERROR;
}
// 若存储空间已满,则增加新空间
if((*L).length >= (*L).listsize) {
// 基于现有空间扩容
newbase = (ElemType*) realloc((*L).elem, ((*L).listsize + LISTINCREMENT) * sizeof(ElemType));
if(newbase == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
// 新基址
(*L).elem = newbase;
// 存的存储空间
(*L).listsize += LISTINCREMENT;
}
// q为插入位置
q = &(*L).elem[i - 1];
// 1.右移元素,腾出位置
for(p = &(*L).elem[(*L).length - 1]; p >= q; --p) {
*(p + 1) = *p;
}
// 2.插入e
*q = e;
// 3.表长增1
(*L).length++;
return OK;
}
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void(Visit)(ElemType)) {
int i;
for(i = 0; i < L.length; i++) {
Visit(L.elem[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,93 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#ifndef SEQUENCELIST_H
#define SEQUENCELIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define LIST_INIT_SIZE 100 // 顺序表存储空间的初始分配量
#define LISTINCREMENT 10 // 顺序表存储空间的分配增量
/* 顺序表元素类型定义 */
typedef int ElemType;
/*
* 顺序表结构
*
* 注elem在使用前需要先为其分配内存且元素从elem[0]处开始存储
*/
typedef struct {
ElemType* elem; // 顺序表存储空间的基址(指向顺序表所占内存的起始位置)
int length; // 当前顺序表长度(包含多少元素)
int listsize; // 当前分配的存储容量(可以存储多少元素)
} SqList;
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L);
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L);
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e);
/*
* ████████ 算法2.6 ████████
*
* 查找
*
* 返回顺序表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(SqList L, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e);
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void (Visit)(ElemType));
#endif

View File

@@ -0,0 +1,43 @@
#include <stdio.h>
#include "Union.h" //**▲02 线性表**//
#include "SqList.h" //**▲02 线性表**//
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
ElemType a[5] = {5, 2, 1, 3, 9};
ElemType b[7] = {7, 2, 6, 9, 11, 3, 10};
SqList La, Lb;
int i;
// 初始化La
InitList(&La);
for(i = 1; i <= 5; i++) {
ListInsert(&La, i, a[i - 1]);
}
// 初始化Lb
InitList(&Lb);
for(i = 1; i <= 7; i++) {
ListInsert(&Lb, i, b[i - 1]);
}
// 输出La
printf("La = ");
ListTraverse(La, PrintElem);
// 输出Lb
printf("Lb = ");
ListTraverse(Lb, PrintElem);
// 输出新表La的内容
printf("La = LaLb = ");
Union(&La, Lb);
ListTraverse(La, PrintElem);
return 0;
}

View File

@@ -0,0 +1,45 @@
/*==============
* 求并集
*
* 包含算法: 2.1
===============*/
#include "Union.h" //**▲02 线性表**//
/*
* ████████ 算法2.1 ████████
*
* A=AB
*
* 计算La与Lb的并集并返回。
* 由于生成的并集会拼接在La上所以La的入参为指针类型。
*/
void Union(SqList* La, SqList Lb) {
int La_len, Lb_len;
int i;
ElemType e;
// 求顺序表长度
La_len = ListLength(*La);
Lb_len = ListLength(Lb);
for(i = 1; i <= Lb_len; i++) {
// 取Lb中第i个元素赋给e
GetElem(Lb, i, &e);
// 若e不在La中则插入
if(!LocateElem(*La, e, equal)) {
ListInsert(La, ++La_len, e);
}
}
}
/*
* 判等
*
* 判断两元素是否相等。
* 如果相等则返回TRUE否则返回FALSE。
*/
Status equal(ElemType e1, ElemType e2) {
return e1 == e2 ? TRUE : FALSE;
}

View File

@@ -0,0 +1,33 @@
/*==============
* 求并集
*
* 包含算法: 2.1
===============*/
#ifndef UNION_H
#define UNION_H
#include <stdio.h>
#include "Status.h" //**▲01 绪论**//
#include "SqList.h" //**▲02 线性表**//
/*
* ████████ 算法2.1 ████████
*
* A=AB
*
* 计算La与Lb的并集并返回。
* 由于生成的并集会拼接在La上所以La的入参为指针类型。
*/
void Union(SqList* La, SqList Lb);
/*
* 判等
*
* 判断两元素是否相等。
* 如果相等则返回TRUE否则返回FALSE。
*/
Status equal(ElemType e1, ElemType e2);
#endif

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(MergeSqList SqList.h SqList.c MergeSqList.h MergeSqList.c MergeSqList-main.c)
# 链接公共库
target_link_libraries(MergeSqList Scanf_lib)

View File

@@ -0,0 +1,49 @@
#include <stdio.h>
#include "SqList.h"
#include "MergeSqList.h" //**▲02 线性表**//
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
ElemType a[4] = {3, 5, 8, 11};
ElemType b[7] = {2, 6, 8, 9, 11, 15, 20};
SqList La, Lb, Lc1, Lc2;
int i;
// 初始化La
InitList(&La);
for(i = 1; i <= 4; i++) {
ListInsert(&La, i, a[i - 1]);
}
// 初始化Lb
InitList(&Lb);
for(i = 1; i <= 7; i++) {
ListInsert(&Lb, i, b[i - 1]);
}
// 输出La
printf("La = ");
ListTraverse(La, PrintElem);
// 输出Lb
printf("Lb = ");
ListTraverse(Lb, PrintElem);
// 归并顺序表La和Lb算法2.2
MergeSqList_1(La, Lb, &Lc1);
printf("归并La和Lb为Lc1 = ");
ListTraverse(Lc1, PrintElem);
// 归并顺序表La和Lb算法2.7
MergeSqList_2(La, Lb, &Lc2);
printf("归并La和Lb为Lc2 = ");
ListTraverse(Lc2, PrintElem);
return 0;
}

View File

@@ -0,0 +1,104 @@
/*==================
* 归并非降序顺序表
*
* 包含算法: 2.2、2.7
===================*/
#include "MergeSqList.h" //**▲02 线性表**//
/*
* ████████ 算法2.2 ████████
*
* 非递减链表归并C=A+B
*
* 归并顺序表La和Lb生成新的顺序表Lc。
* 其中La、Lb、Lc均为非递减序列。
*/
void MergeSqList_1(SqList La, SqList Lb, SqList* Lc) {
int La_len, Lb_len;
int i, j, k;
ElemType ai, bj;
i = j = 1;
k = 0;
// 初始化Lc
InitList(Lc);
// 获取La、Lb的长度
La_len = ListLength(La);
Lb_len = ListLength(Lb);
// 如果La及Lb均未遍历完
while(i <= La_len && j <= Lb_len) {
GetElem(La, i, &ai);
GetElem(Lb, j, &bj);
// 比较遍历到的元素先将比较小的元素加入顺序表Lc
if(ai <= bj) {
ListInsert(Lc, ++k, ai);
i++;
} else {
ListInsert(Lc, ++k, bj);
j++;
}
}
// 如果Lb已遍历完但La还未遍历完将La中剩余元素加入Lc
while(i <= La_len) {
GetElem(La, i++, &ai);
ListInsert(Lc, ++k, ai);
}
// 如果La已遍历完但Lb还未遍历完将Lb中剩余元素加入Lc
while(j <= Lb_len) {
GetElem(Lb, j++, &bj);
ListInsert(Lc, ++k, bj);
}
}
/*
* ████████ 算法2.7 ████████
*
* 非递减链表归并C=A+B
*
* 归并顺序表La和Lb生成新的顺序表Lc。
* 其中La、Lb、Lc均为非递减序列。
*/
void MergeSqList_2(SqList La, SqList Lb, SqList* Lc) {
ElemType* pa, * pb, * pc;
ElemType* pa_last, * pb_last;
pa = La.elem; // 指向La第一个元素
pb = Lb.elem; // 指向Lb第一个元素
// 没有使用InitList创建Lc
(*Lc).listsize = (*Lc).length = La.length + Lb.length;
pc = (*Lc).elem = (ElemType*) malloc((*Lc).listsize * sizeof(ElemType));
if(pc == NULL) {
exit(OVERFLOW);
}
pa_last = La.elem + La.length - 1; // 指向La最后一个元素
pb_last = Lb.elem + Lb.length - 1; // 指向Lb最后一个元素
// 如果La及Lb均未遍历完
while(pa <= pa_last && pb <= pb_last) {
if(*pa <= *pb) {
*pc++ = *pa++;
} else {
*pc++ = *pb++;
}
}
// 如果Lb已遍历完但La还未遍历完将La中剩余元素加入Lc
while(pa <= pa_last) {
*pc++ = *pa++;
}
// 如果La已遍历完但Lb还未遍历完将Lb中剩余元素加入Lc
while(pb <= pb_last) {
*pc++ = *pb++;
}
}

View File

@@ -0,0 +1,35 @@
/*==================
* 归并非降序顺序表
*
* 包含算法: 2.2、2.7
===================*/
#ifndef MERGESQLIST_H
#define MERGESQLIST_H
#include <stdio.h>
#include <stdlib.h>
#include "SqList.h" //**▲02 线性表**//
/*
* ████████ 算法2.2 ████████
*
* 非递减链表归并C=A+B
*
* 归并顺序表La和Lb生成新的顺序表Lc。
* 其中La、Lb、Lc均为非递减序列。
*/
void MergeSqList_1(SqList La, SqList Lb, SqList* Lc);
/*
* ████████ 算法2.7 ████████
*
* 非递减链表归并C=A+B
*
* 归并顺序表La和Lb生成新的顺序表Lc。
* 其中La、Lb、Lc均为非递减序列。
*/
void MergeSqList_2(SqList La, SqList Lb, SqList* Lc);
#endif

View File

@@ -0,0 +1,129 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#include "SqList.h"
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L) {
// 分配指定容量的内存如果分配失败则返回NULL
(*L).elem = (ElemType*) malloc(LIST_INIT_SIZE * sizeof(ElemType));
if((*L).elem == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
(*L).length = 0; // 初始化顺序表长度为0
(*L).listsize = LIST_INIT_SIZE; // 顺序表初始内存分配量
return OK; // 初始化成功
}
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L) {
return L.length;
}
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e) {
// 因为i的含义是位置所以其合法范围是[1, length]
if(i < 1 || i > L.length) {
return ERROR; //i值不合法
}
*e = L.elem[i - 1];
return OK;
}
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e) {
ElemType* newbase;
ElemType* p, * q;
// 确保顺序表结构存在
if(L == NULL || (*L).elem == NULL) {
return ERROR;
}
// i值越界
if(i < 1 || i > (*L).length + 1) {
return ERROR;
}
// 若存储空间已满,则增加新空间
if((*L).length >= (*L).listsize) {
// 基于现有空间扩容
newbase = (ElemType*) realloc((*L).elem, ((*L).listsize + LISTINCREMENT) * sizeof(ElemType));
if(newbase == NULL) {
// 存储内存失败
exit(OVERFLOW);
}
// 新基址
(*L).elem = newbase;
// 存的存储空间
(*L).listsize += LISTINCREMENT;
}
// q为插入位置
q = &(*L).elem[i - 1];
// 1.右移元素,腾出位置
for(p = &(*L).elem[(*L).length - 1]; p >= q; --p) {
*(p + 1) = *p;
}
// 2.插入e
*q = e;
// 3.表长增1
(*L).length++;
return OK;
}
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void(Visit)(ElemType)) {
int i;
for(i = 0; i < L.length; i++) {
Visit(L.elem[i]);
}
printf("\n");
}

View File

@@ -0,0 +1,80 @@
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#ifndef SEQUENCELIST_H
#define SEQUENCELIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define LIST_INIT_SIZE 100 // 顺序表存储空间的初始分配量
#define LISTINCREMENT 10 // 顺序表存储空间的分配增量
/* 顺序表元素类型定义 */
typedef int ElemType;
/*
* 顺序表结构
*
* 注elem在使用前需要先为其分配内存且元素从elem[0]处开始存储
*/
typedef struct {
ElemType* elem; // 顺序表存储空间的基址(指向顺序表所占内存的起始位置)
int length; // 当前顺序表长度(包含多少元素)
int listsize; // 当前分配的存储容量(可以存储多少元素)
} SqList;
/*
* ████████ 算法2.3 ████████
*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(SqList* L);
/*
* 计数
*
* 返回顺序表包含的有效元素的数量。
*/
int ListLength(SqList L);
/*
* 取值
*
* 获取顺序表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SqList L, int i, ElemType* e);
/*
* ████████ 算法2.4 ████████
*
* 插入
*
* 向顺序表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SqList* L, int i, ElemType e);
/*
* 遍历
*
* 用visit函数访问顺序表L
*/
void ListTraverse(SqList L, void (Visit)(ElemType));
#endif

View File

@@ -0,0 +1,12 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(LinkedList LinkList.h LinkList.c LinkList-main.c)
# 链接公共库
target_link_libraries(LinkedList Scanf_lib)
# 记录要拷贝到*.exe目录下的资源文件
file(GLOB TestData TestData*.txt)
# 将资源文件拷贝到*.exe目录下不然无法加载
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -0,0 +1,170 @@
#include <stdio.h>
#include "Status.h"
#include "LinkList.h" //**▲02 线性表**//
// 判断data>e是否成立
Status CmpGreater(ElemType data, ElemType e) {
return data > e ? TRUE : FALSE;
}
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
LinkList L;
int i;
ElemType e;
printf("████████ InitList \n");
{
printf("█ 初始化单链表 L ...\n");
InitList(&L);
}
PressEnterToContinue();
printf("████████ ListEmpty \n");
{
ListEmpty(L) ? printf("█ L 为空!!\n") : printf("█ L 不为空!\n");
}
PressEnterToContinue();
printf("████████ ListInsert \n");
{
for(i = 1; i <= 8; i++) {
printf("█ 在 L 第 %d 个位置插入 \"%d\" ...\n", i, 2 * i);
ListInsert(L, i, 2 * i);
}
}
PressEnterToContinue();
printf("████████ ListTraverse \n");
{
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ ListLength \n");
{
printf("█ L 的长度为 %d \n", ListLength(L));
}
PressEnterToContinue();
printf("████████ ListDelete \n");
{
printf("█ 删除前的元素L = ");
ListTraverse(L, PrintElem);
printf("█ 尝试删除 L 中第 6 个元素...\n");
if(ListDelete(L, 6, &e) == OK) {
printf("█ 删除成功,被删除元素是:\"%d\"\n", e);
} else {
printf("█ 删除失败,第 6 个元素不存在!\n");
}
printf("█ 删除后的元素L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ GetElem \n");
{
GetElem(L, 4, &e);
printf("█ L 中第 4 个位置的元素为 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ LocateElem \n");
{
i = LocateElem(L, 7, CmpGreater);
GetElem(L, i, &e);
printf("█ L 中第一个元素值大于 \"7\" 的元素是 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ PriorElem \n");
{
ElemType cur_e = 6;
if(PriorElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的前驱为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的前驱不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ NextElem \n");
{
ElemType cur_e = 6;
if(NextElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的后继为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的后继不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ ClearList \n");
{
printf("█ 清空 L 前:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
ClearList(L);
printf("█ 清空 L 后:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
}
PressEnterToContinue();
printf("████████ DestroyList \n");
{
printf("█ 销毁 L 前:");
L ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
DestroyList(&L);
printf("█ 销毁 L 后:");
L ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
}
PressEnterToContinue();
printf("████████ CreateList_Head \n");
{
LinkList L;
printf("█ 头插法建立单链表 L = ");
CreateList_Head("TestData_Head.txt", &L, 5);
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ CreateList_Tail \n");
{
LinkList L;
printf("█ 尾插法建立单链表 L = ");
CreateList_Tail("TestData_Tail.txt", &L, 5);
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
return 0;
}

View File

@@ -0,0 +1,488 @@
/*===============================
* 线性表的链式存储结构(链表)
*
* 包含算法: 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;
}
/*
* 销毁(结构)
*
* 释放链表所占内存,头结点也会被清理。
*/
Status DestroyList(LinkList* L) {
LinkList p;
// 确保链表结构存在
if(L == NULL || *L == NULL) {
return ERROR;
}
p = *L;
while(p != NULL) {
p = (*L)->next;
free(*L);
(*L) = p;
}
*L = NULL;
return OK;
}
/*
* 置空(内容)
*
* 这里需要释放链表中非头结点处的空间。
*/
Status ClearList(LinkList L) {
LinkList pre, p;
// 确保链表存在
if(L == NULL) {
return ERROR;
}
p = L->next;
// 释放链表上所有结点所占内存
while(p != NULL) {
pre = p;
p = p->next;
free(pre);
}
L->next = NULL;
return OK;
}
/*
* 判空
*
* 判断链表中是否包含有效数据。
*
* 返回值:
* TRUE : 链表为空
* FALSE: 链表不为空
*/
Status ListEmpty(LinkList L) {
// 链表只有头结点时,认为该链表为空
if(L != NULL && L->next == NULL) {
return TRUE;
} else {
return FALSE;
}
}
/*
* 计数
*
* 返回链表包含的有效元素的数量。
*/
int ListLength(LinkList L) {
LinkList p;
int i;
// 确保链表存在且不为空表
if(L == NULL || L->next == NULL) {
return 0;
}
i = 0;
p = L->next;
// 遍历所有结点
while(p != NULL) {
i++;
p = p->next;
}
return i;
}
/*
* ████████ 算法2.8 ████████
*
* 取值
*
* 获取链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(LinkList L, int i, ElemType* e) {
LinkList p;
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;
}
*e = p->next->data;
return OK;
}
/*
* 查找
*
* 返回链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(LinkList L, ElemType e, Status(Compare)(ElemType, ElemType)) {
int i;
LinkList p;
// 确保链表存在且不为空表
if(L == NULL || L->next == NULL) {
return 0;
}
i = 1; // i的初值为第1个元素的位序
p = L->next; // p的初值为第1个元素的指针
while(p != NULL && !Compare(p->data, e)) {
i++;
p = p->next;
}
if(p != NULL) {
return i;
} else {
return 0;
}
}
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(LinkList L, ElemType cur_e, ElemType* pre_e) {
LinkList pre, next;
// 确保链表存在且不为空表
if(L == NULL || L->next == NULL) {
return ERROR;
}
// 指向第1个元素
pre = L->next;
// 第1个元素没有前驱
if(pre->data == cur_e) {
return ERROR;
}
// 指向第2个元素
next = pre->next;
// 从第2个元素开始查找cur_e的位置
while(next != NULL && next->data != cur_e) {
pre = next;
next = next->next;
}
// 如果没找到元素cur_e查找失败返回ERROR
if(next == NULL) {
return ERROR;
}
*pre_e = pre->data;
return OK;
}
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(LinkList L, ElemType cur_e, ElemType* next_e) {
LinkList pre;
// 确保链表存在且不为空表
if(L == NULL || L->next == NULL) {
return ERROR;
}
// 指向第1个元素
pre = L->next;
// 从第1个元素开始查找cur_e的位置且保证该结点的后继不为NULL
while(pre->next != NULL && pre->data != cur_e) {
pre = pre->next;
}
// 如果没找到cur_e或者找到了但它没有后继均返回ERROR
if(pre->next == NULL) {
return ERROR;
}
*next_e = pre->next->data;
return OK;
}
/*
* ████████ 算法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;
}
/*
* 遍历
*
* 用visit函数访问链表L
*/
void ListTraverse(LinkList L, void(Visit)(ElemType)) {
LinkList p;
// 确保链表存在且不为空表
if(L == NULL || L->next == NULL) {
return;
}
p = L->next;
while(p != NULL) {
Visit(p->data);
p = p->next;
}
printf("\n");
}
/*
* ████████ 算法2.11 ████████
*
* 头插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
* 另教材中的算法采用的是头插法所以此处的实现加了后缀_Head。
*/
Status CreateList_Head(char path[], LinkList* L, int n) {
FILE* fp;
int i;
LinkList p;
ElemType e;
// 建立头结点
*L = (LinkList) malloc(sizeof(LNode));
if(*L == NULL) {
exit(OVERFLOW);
}
(*L)->next = NULL;
// 打开文件,准备读取测试数据
fp = fopen(path, "r");
if(fp == NULL) {
return ERROR;
}
for(i = 1; i <= n; ++i) {
// 如果成功读取到输入数据
if(ReadData(fp, "%d", &e) == 1) {
// 生成新结点
p = (LinkList) malloc(sizeof(LNode));
if(p == NULL) {
fclose(fp);
exit(OVERFLOW);
}
// 填充数据,并插入到链表中
p->data = e;
p->next = (*L)->next;
(*L)->next = p;
} else {
fclose(fp);
return ERROR;
}
}
fclose(fp);
return OK;
}
/*
* 尾插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
*/
Status CreateList_Tail(char path[], LinkList* L, int n) {
FILE* fp;
int i;
LinkList p, q;
ElemType e;
// 建立头结点
*L = (LinkList) malloc(sizeof(LNode));
if(*L==NULL) {
exit(OVERFLOW);
}
(*L)->next = NULL;
// 打开文件,准备读取测试数据
fp = fopen(path, "r");
if(fp == NULL) {
return ERROR;
}
for(i = 1, q = *L; i <= n; ++i) {
// 如果成功读取到输入数据
if(ReadData(fp, "%d", &e) == 1) {
p = (LinkList) malloc(sizeof(LNode));
if(p==NULL) {
fclose(fp);
exit(OVERFLOW);
}
// 填充数据,并插入到链表中
p->data = e;
q->next = p;
q = q->next;
} else {
fclose(fp);
return ERROR;
}
}
q->next = NULL;
fclose(fp);
return OK;
}

View File

@@ -0,0 +1,168 @@
/*===============================
* 线性表的链式存储结构(链表)
*
* 包含算法: 2.8、2.9、2.10、2.11
================================*/
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 单链表元素类型定义 */
typedef int ElemType;
/*
* 单链表结构
*
* 注:这里的单链表存在头结点
*/
typedef struct LNode {
ElemType data; // 数据结点
struct LNode* next; // 指向下一个结点的指针
} LNode;
// 指向单链表结点的指针
typedef LNode* LinkList;
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(LinkList* L);
/*
* 销毁(结构)
*
* 释放链表所占内存。
*/
Status DestroyList(LinkList* L);
/*
* 置空(内容)
*
* 这里需要释放链表中非头结点处的空间。
*/
Status ClearList(LinkList L);
/*
* 判空
*
* 判断链表中是否包含有效数据。
*
* 返回值:
* TRUE : 链表为空
* FALSE: 链表不为空
*/
Status ListEmpty(LinkList L);
/*
* 计数
*
* 返回链表包含的有效元素的数量。
*/
int ListLength(LinkList L);
/*
* ████████ 算法2.8 ████████
*
* 取值
*
* 获取链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(LinkList L, int i, ElemType* e);
/*
* 查找
*
* 返回链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(LinkList L, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(LinkList L, ElemType cur_e, ElemType* pre_e);
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(LinkList L, ElemType cur_e, ElemType* next_e);
/*
* ████████ 算法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);
/*
* 遍历
*
* 用visit函数访问链表L
*/
void ListTraverse(LinkList L, void(Visit)(ElemType));
/*
* ████████ 算法2.11 ████████
*
* 头插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
* 另教材中的算法采用的是头插法所以此处的实现加了后缀_Head。
*/
Status CreateList_Head(char path[], LinkList* L, int n);
/*
* 尾插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据
*/
Status CreateList_Tail(char path[], LinkList* L, int n);
#endif

View File

@@ -0,0 +1 @@
降序9 7 5 3 1

View File

@@ -0,0 +1 @@
升序2 4 6 8 10

View File

@@ -0,0 +1,12 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(MergeList LinkList.h LinkList.c MergeList.h MergeList.c MergeList-main.c)
# 链接公共库
target_link_libraries(MergeList Scanf_lib)
# 记录要拷贝到*.exe目录下的资源文件
file(GLOB TestData TestData*.txt)
# 将资源文件拷贝到*.exe目录下不然无法加载
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -0,0 +1,138 @@
/*===============================
* 线性表的链式存储结构(链表)
*
* 包含算法: 2.8、2.9、2.10、2.11
================================*/
#include "LinkList.h" //**▲02 线性表**//
/*
* 遍历
*
* 用visit函数访问链表L
*/
void ListTraverse(LinkList L, void(Visit)(ElemType)) {
LinkList p;
// 确保链表存在
if(L == NULL || L->next == NULL) {
return;
}
p = L->next;
while(p != NULL) {
Visit(p->data);
p = p->next;
}
printf("\n");
}
/*
* ████████ 算法2.11 ████████
*
* 头插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
* 另教材中的算法采用的是头插法所以此处的实现加了后缀_Head。
*/
Status CreateList_Head(char path[], LinkList* L, int n) {
FILE* fp;
int i;
LinkList p;
ElemType e;
// 建立头结点
*L = (LinkList) malloc(sizeof(LNode));
if(*L == NULL) {
exit(OVERFLOW);
}
(*L)->next = NULL;
// 打开文件,准备读取测试数据
fp = fopen(path, "r");
if(fp == NULL) {
return ERROR;
}
for(i = 1; i <= n; ++i) {
// 如果成功读取到输入数据
if(ReadData(fp, "%d", &e) == 1) {
// 生成新结点
p = (LinkList) malloc(sizeof(LNode));
if(p == NULL) {
fclose(fp);
exit(OVERFLOW);
}
// 填充数据,并插入到链表中
p->data = e;
p->next = (*L)->next;
(*L)->next = p;
} else {
fclose(fp);
return ERROR;
}
}
fclose(fp);
return OK;
}
/*
* 尾插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
*/
Status CreateList_Tail(char path[], LinkList* L, int n) {
FILE* fp;
int i;
LinkList p, q;
ElemType e;
// 建立头结点
*L = (LinkList) malloc(sizeof(LNode));
if(*L==NULL) {
exit(OVERFLOW);
}
(*L)->next = NULL;
// 打开文件,准备读取测试数据
fp = fopen(path, "r");
if(fp == NULL) {
return ERROR;
}
for(i = 1, q = *L; i <= n; ++i) {
// 如果成功读取到输入数据
if(ReadData(fp, "%d", &e) == 1) {
p = (LinkList) malloc(sizeof(LNode));
if(p==NULL) {
fclose(fp);
exit(OVERFLOW);
}
// 填充数据,并插入到链表中
p->data = e;
q->next = p;
q = q->next;
} else {
fclose(fp);
return ERROR;
}
}
q->next = NULL;
fclose(fp);
return OK;
}

View File

@@ -0,0 +1,61 @@
/*===============================
* 线性表的链式存储结构(链表)
*
* 包含算法: 2.8、2.9、2.10、2.11
================================*/
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 单链表元素类型定义 */
typedef int ElemType;
/*
* 单链表结构
*
* 注:这里的单链表存在头结点
*/
typedef struct LNode {
ElemType data; // 数据结点
struct LNode* next; // 指向下一个结点的指针
} LNode;
// 指向单链表结点的指针
typedef LNode* LinkList;
/*
* 遍历
*
* 用visit函数访问链表L
*/
void ListTraverse(LinkList L, void(Visit)(ElemType));
/*
* ████████ 算法2.11 ████████
*
* 头插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
* 另教材中的算法采用的是头插法所以此处的实现加了后缀_Head。
*/
Status CreateList_Head(char path[], LinkList* L, int n);
/*
* 尾插法创建链表
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据
*/
Status CreateList_Tail(char path[], LinkList* L, int n);
#endif

View File

@@ -0,0 +1,31 @@
#include <stdio.h>
#include "MergeList.h" //**▲02 线性表**//
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
LinkList La, Lb, Lc;
int m = 5;
printf("作为示例La长度设定为 %d Lb长度设定为 %d 创建La和Lb...\n", m, m);
CreateList_Head("TestData_Head.txt", &La, m);
printf("La = ");
ListTraverse(La, PrintElem);
CreateList_Tail("TestData_Tail.txt", &Lb, m);
printf("Lb = ");
ListTraverse(Lb, PrintElem);
printf("归并La和Lb为Lc = ");
MergeList(&La, &Lb, &Lc);
ListTraverse(Lc, PrintElem);
return 0;
}

View File

@@ -0,0 +1,45 @@
/*===============
* 归并非降序链表
*
* 包含算法: 2.12
================*/
#include "MergeList.h" //**▲02 线性表**//
/*
* ████████ 算法2.12 ████████
*
* 非递减链表归并C=A+B
*
* 将链表A和B归并为C且保持元素相对位置不变。
* Lc利用La的头结点Lb中结点均插入新链表Lc中。
*/
void MergeList(LinkList* La, LinkList* Lb, LinkList* Lc) {
LinkList pa, pb, pc;
pa = (*La)->next;
pb = (*Lb)->next;
pc = *Lc = *La; // 用La的头结点作为Lc的头结点
// 遍历La和Lb
while(pa && pb) {
if(pa->data <= pb->data) {
pc->next = pa;
pc = pa;
pa = pa->next;
} else {
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
// 插入剩余段
pc->next = pa ? pa : pb;
// 释放Lb的头结点所占内存
free(*Lb);
*La = NULL;
*Lb = NULL;
}

View File

@@ -0,0 +1,24 @@
/*===============
* 归并非降序链表
*
* 包含算法: 2.12
================*/
#ifndef MERGELIST_H
#define MERGELIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "LinkList.h" //**▲02 线性表**//
/*
* ████████ 算法2.12 ████████
*
* 非递减链表归并C=A+B
*
* 将链表A和B归并为C且保持元素相对位置不变。
* Lc利用La的头结点Lb中结点均插入新链表Lc中。
*/
void MergeList(LinkList* La, LinkList* Lb, LinkList* Lc);
#endif

View File

@@ -0,0 +1 @@
降序9 7 5 3 1

View File

@@ -0,0 +1 @@
升序2 4 6 8 10

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(SLinkList SLinkList.h SLinkList.c SLinkList-main.c)
# 链接公共库
target_link_libraries(SLinkList Scanf_lib)

View File

@@ -0,0 +1,152 @@
#include <stdio.h>
#include "Status.h"
#include "SLinkList.h" //**▲02 线性表**//
// 判断data>e是否成立
Status CmpGreater(ElemType data, ElemType e) {
return data > e ? TRUE : FALSE;
}
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
SLinkList space; // 备用空间
int S; // 静态链表头结点索引
int i;
ElemType e;
printf("████████ InitList \n");
{
printf("█ 初始化单链表 S ...\n");
InitList(space, &S);
}
PressEnterToContinue();
printf("████████ ListEmpty \n");
{
ListEmpty(space, S) ? printf("█ S 为空!!\n") : printf("█ S 不为空!\n");
}
PressEnterToContinue();
printf("████████ ListInsert \n");
{
for(i = 1; i <= 8; i++) {
printf("█ 在 S 第 %d 个位置插入 \"%d\" ...\n", i, 2 * i);
ListInsert(space, S, i, 2 * i);
}
}
PressEnterToContinue();
printf("████████ ListTraverse \n");
{
printf("█ S 中的元素为S = ");
ListTraverse(space, S, PrintElem);
}
PressEnterToContinue();
printf("████████ ListLength \n");
{
printf("█ S 的长度为 %d \n", ListLength(space, S));
}
PressEnterToContinue();
printf("████████ ListDelete \n");
{
printf("█ 删除前的元素S = ");
ListTraverse(space, S, PrintElem);
printf("█ 尝试删除 S 中第 6 个元素...\n");
if(ListDelete(space, S, 6, &e) == OK) {
printf("█ 删除成功,被删除元素是:\"%d\"\n", e);
} else {
printf("█ 删除失败,第 6 个元素不存在!\n");
}
printf("█ 删除后的元素S = ");
ListTraverse(space, S, PrintElem);
}
PressEnterToContinue();
printf("████████ GetElem \n");
{
GetElem(space, S, 4, &e);
printf("█ S 中第 4 个位置的元素为 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ LocateElem \n");
{
i = LocateElem(space, S, 7, CmpGreater);
GetElem(space, S, i, &e);
printf("█ S 中第一个元素值大于 \"7\" 的元素是 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ PriorElem \n");
{
ElemType cur_e = 6;
if(PriorElem(space, S, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的前驱为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的前驱不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ NextElem \n");
{
ElemType cur_e = 6;
if(NextElem(space, S, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的后继为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的后继不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ ClearList \n");
{
printf("█ 清空 S 前:");
ListEmpty(space, S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
ClearList(space, S);
printf("█ 清空 S 后:");
ListEmpty(space, S) ? printf(" S 为空!!\n") : printf(" S 不为空!\n");
}
PressEnterToContinue();
printf("████████ DestroyList \n");
{
printf("█ 销毁 S 前:");
S!=0 ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
DestroyList(space, &S);
printf("█ 销毁 S 后:");
S!=0 ? printf(" S 存在!\n") : printf(" S 不存在!!\n");
}
PressEnterToContinue();
return 0;
}

View File

@@ -0,0 +1,470 @@
/*=================================
* 静态链表
*
* 包含算法: 2.13、2.14、2.15、2.16
==================================*/
#include "SLinkList.h"
/*
* ██ 提示 ██
*
* 以下所有函数的形参中:
* space指示备用空间
* S :指示静态链表头结点索引
*/
/*━━━━━━━━━━━━━━━━━━━━━━ 备用空间操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* ████████ 算法2.14 ████████
*
* 初始化备用空间
*
* 备用空间为静态链表提供可用的内存。
*/
void InitSpace(SLinkList space) {
int i;
for(i = 0; i < MAXSIZE - 1; ++i) {
space[i].cur = i + 1;
}
space[MAXSIZE - 1].cur = 0;
}
/*
* ████████ 算法2.15 ████████
*
* 申请空间
*
* 为静态链表从备用空间申请结点空间,
* 如果申请成功,返回可用空间的索引,
* 申请失败时返回0。
*/
int Malloc(SLinkList space) {
int i = space[0].cur;
if(i != 0) {
// 将申请到的空间从备用空间中删去
space[0].cur = space[i].cur;
}
// 返回新申请结点下标
return i;
}
/*
* ████████ 算法2.16 ████████
*
* 回收空间
*
* 回收索引k处的结点空间以供静态链表后续复用
* 回收方式就是将该结点空间从静态链表上移除,
* 并将其添加到备用空间的列表当中。
*/
void Free(SLinkList space, int k) {
space[k].cur = space[0].cur;
space[0].cur = k;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 静态链表操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 先初始化备用空间,而后从备用空间中申请头结点的空间,进而完成静态链表的初始化。
* 初始化成功则使用S存储头结点索引且返回OK否则返回ERROR。
*/
Status InitList(SLinkList space, int* S) {
int index;
// 初始化备用空间
InitSpace(space);
// 申请头结点空间
index = Malloc(space);
if(index == 0) {
return ERROR;
}
space[index].cur = 0;
*S = index;
return OK;
}
/*
* 销毁(结构)
*
* 释放静态链表所占内存,即将静态链表所有结点空间移入备用空间列表中。
*/
Status DestroyList(SLinkList space, int* S) {
int cur;
// 确保静态链表存在
if(S == NULL || *S == 0) {
return ERROR;
}
while(*S != 0) {
// 暂存下一个结点的索引
cur = space[*S].cur;
// 回收当前结点所占空间
Free(space, *S);
// 前进到下一个结点的索引处
*S = cur;
}
return OK;
}
/*
* 置空(内容)
*
* 这里需要释放静态链表中非头结点处的空间。
*/
Status ClearList(SLinkList space, int S) {
int P;
int cur;
// 确保静态链表存在
if(S == 0) {
return ERROR;
}
// 获取静态链表首个结点的索引
P = space[S].cur;
while(P != 0) {
// 暂存下一个结点的索引
cur = space[P].cur;
// 回收当前结点所占空间
Free(space, P);
// 前进到下一个结点的索引处
P = cur;
}
space[S].cur = 0;
return OK;
}
/*
* 判空
*
* 判断静态链表中是否包含有效数据。
*
* 返回值:
* TRUE : 静态链表为空
* FALSE: 静态链表不为空
*/
Status ListEmpty(SLinkList space, int S) {
// 只存在头结点的静态链表被视为空表
if(S != 0 && space[S].cur == 0) {
return TRUE;
} else {
return FALSE;
}
}
/*
* 计数
*
* 返回静态链表包含的有效元素的数量。
*/
int ListLength(SLinkList space, int S) {
int count;
// 静态链表不存在或静态链表为空时返回0
if(S == 0 || space[S].cur == 0) {
return 0;
}
// 获取静态链表首个元素的索引
S = space[S].cur;
count = 0;
while(S != 0) {
count++;
S = space[S].cur;
}
return count;
}
/*
* 取值
*
* 获取静态链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SLinkList space, int S, int i, ElemType* e) {
int count;
// 确保静态链表存在且不为空
if(S == 0 || space[S].cur == 0) {
return ERROR;
}
// 获取静态链表首个元素的索引
S = space[S].cur;
count = 0;
while(S != 0 && count < i - 1) {
count++;
S = space[S].cur;
}
if(S == 0 || count > i - 1) {
return ERROR;
}
*e = space[S].data;
return OK;
}
/*
* ████████ 算法2.13 ████████
*
* 查找
*
* 返回静态链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 1.元素e是Compare函数第二个形参
* 2.这里的实现与教材上的算法2.13不相同,原因参见顶部的“注意”信息
*/
int LocateElem(SLinkList space, int S, ElemType e, Status(Compare)(ElemType, ElemType)) {
int i;
int p;
// 确保静态链表存在且不为空
if(S == 0 || space[S].cur == 0) {
return 0;
}
i = 1; // i的初值为第1个元素的位序
p = space[S].cur; // p的初值为第1个元素的索引
while(p != 0 && !Compare(space[p].data, e)) {
i++;
p = space[p].cur;
}
if(p != 0) {
return i;
} else {
return 0;
}
}
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(SLinkList space, int S, ElemType cur_e, ElemType* pre_e) {
int pre, next;
// 确保静态链表存在且不为空
if(S == 0 || space[S].cur == 0) {
return ERROR;
}
// 指向第1个元素
pre = space[S].cur;
// 第1个元素没有前驱
if(space[pre].data == cur_e) {
return ERROR;
}
// 指向第2个元素
next = space[pre].cur;
// 从第2个元素开始查找cur_e的位置
while(next != 0 && space[next].data != cur_e) {
pre = next;
next = space[next].cur;
}
// 如果没找到元素cur_e查找失败返回ERROR
if(next == 0) {
return ERROR;
}
*pre_e = space[pre].data;
return OK;
}
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(SLinkList space, int S, ElemType cur_e, ElemType* next_e) {
int pre;
// 确保静态链表存在且不为空
if(S == 0 || space[S].cur == 0) {
return ERROR;
}
// 指向第1个元素
pre = space[S].cur;
// 从第1个元素开始查找cur_e的位置且保证该结点的后继存在
while(space[pre].cur != 0 && space[pre].data != cur_e) {
pre = space[pre].cur;
}
// 如果没找到cur_e或者找到了但它没有后继均返回ERROR
if(space[pre].cur == 0) {
return ERROR;
}
*next_e = space[space[pre].cur].data;
return OK;
}
/*
* 插入
*
* 向静态链表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SLinkList space, int S, int i, ElemType e) {
int p, s;
int j;
// 确保静态链表存在
if(S == 0) {
return ERROR;
}
p = S;
j = 0;
// 寻找第i-1个结点且保证该结点本身存在
while(p != 0 && j < i - 1) {
p = space[p].cur;
++j;
}
// 如果遍历到头了或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
if(p == 0 || j > i - 1) {
return ERROR;
}
// 生成新结点
s = Malloc(space);
space[s].data = e;
space[s].cur = space[p].cur;
space[p].cur = s;
return OK;
}
/*
* 删除
*
* 删除静态链表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(SLinkList space, int S, int i, ElemType* e) {
int p, q;
int j;
// 确保静态链表存在
if(S == 0) {
return ERROR;
}
p = S;
j = 0;
// 寻找第i-1个结点且保证该结点的后继存在
while(space[p].cur != 0 && j < i - 1) {
p = space[p].cur;
++j;
}
// 如果遍历到头了或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
if(space[p].cur == 0 || j > i - 1) {
return ERROR;
}
// 删除第i个结点
q = space[p].cur;
space[p].cur = space[q].cur;
*e = space[q].data;
Free(space, q);
return OK;
}
/*
* 遍历
*
* 用visit函数访问静态链表S
*/
void ListTraverse(SLinkList space, int S, void(Visit)(ElemType)) {
int p;
// 确保链表存在
if(S == 0 || space[S].cur == 0) {
return;
}
p = space[S].cur;
while(p != 0) {
Visit(space[p].data);
p = space[p].cur;
}
printf("\n");
}
/*━━━━━━━━━━━━━━━━━━━━━━ 图形化输出 ━━━━━━━━━━━━━━━━━━━━━━*/
// 以图形化形式输出当前结构,仅限内部测试使用
void PrintGraph(SLinkList space, int S) {
int i = 0;
printf("==== 备用空间 ====\n");
while(i < 20) {
printf("%2d | %2d | %2d |\n", i, space[i].data, space[i].cur);
i = space[i].cur;
}
printf("==== 静态链表 ====\n");
i = S;
while(i>0 && i < 20) {
printf("%2d | %2d | %2d |\n", i, space[i].data, space[i].cur);
i = space[i].cur;
}
}

View File

@@ -0,0 +1,208 @@
/*=================================
* 静态链表
*
* 包含算法: 2.13、2.14、2.15、2.16
==================================*/
#ifndef SLINKLIST_H
#define SLINKLIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define MAXSIZE 1000 // 备用空间最大容量,近似于静态链表的最大长度
/* 静态链表元素类型定义 */
typedef int ElemType;
/*
* 静态链表结构
*
* 注:静态链表依托于一个数组,该数组包含了已占用空间和空闲空间
*/
typedef struct SLinkNode {
ElemType data;
int cur; // cur是游标做指针用用来链接下一个结点区别于数组下标
} SLinkList[MAXSIZE]; // 链表空间类型
/*
* ████ 提示 ████
*
* 以下所有函数的形参中:
* space指示备用空间
* S :指示静态链表头结点索引
*/
/*
* ████ 注意 ████
*
* 教材中算法2.13和算法2.14~2.17中都是涉及静态链表的操作,
* 但这两处算法预设的数据结构却不同。
*
* 算法2.13假设整个预设数组都是静态链表(操作简单)
* 算法2.14~2.17只是将预设的数组作为备用空间,
* 静态链表所需空间是基于备用空间进行申请和释放的(操作稍难)。
*
* 这里实现的代码中以算法2.14~2.17处预设的数据结构为准,
* 所以对于算法2.13,其实现会有教材有所不同。
*/
/*━━━━━━━━━━━━━━━━━━━━━━ 备用空间操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* ████████ 算法2.14 ████████
*
* 初始化备用空间
*
* 备用空间为静态链表提供可用的内存。
*/
void InitSpace(SLinkList space);
/*
* ████████ 算法2.15 ████████
*
* 申请空间
*
* 为静态链表从备用空间申请结点空间,
* 如果申请成功,返回可用空间的索引,
* 申请失败时返回0。
*/
int Malloc(SLinkList space);
/*
* ████████ 算法2.16 ████████
*
* 回收空间
*
* 回收索引k处的结点空间以供静态链表后续复用
* 回收方式就是将该结点空间从静态链表上移除,
* 并将其添加到备用空间的列表当中。
*/
void Free(SLinkList space, int k);
/*━━━━━━━━━━━━━━━━━━━━━━ 静态链表操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 先初始化备用空间,而后从备用空间中申请头结点的空间,进而完成静态链表的初始化。
* 初始化成功则使用S存储头结点索引且返回OK否则返回ERROR。
*/
Status InitList(SLinkList space, int* S);
/*
* 销毁(结构)
*
* 释放静态链表所占内存,即将静态链表所有结点空间移入备用空间列表中。
*/
Status DestroyList(SLinkList space, int* S);
/*
* 置空(内容)
*
* 这里需要释放静态链表中非头结点处的空间。
*/
Status ClearList(SLinkList space, int S);
/*
* 判空
*
* 判断静态链表中是否包含有效数据。
*
* 返回值:
* TRUE : 静态链表为空
* FALSE: 静态链表不为空
*/
Status ListEmpty(SLinkList space, int S);
/*
* 计数
*
* 返回静态链表包含的有效元素的数量。
*/
int ListLength(SLinkList space, int S);
/*
* 取值
*
* 获取静态链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(SLinkList space, int S, int i, ElemType* e);
/*
* ████████ 算法2.13 ████████
*
* 查找
*
* 返回静态链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 1.元素e是Compare函数第二个形参
* 2.这里的实现与教材上的算法2.13不相同,原因参见顶部的“注意”信息
*/
int LocateElem(SLinkList space, int S, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(SLinkList space, int S, ElemType cur_e, ElemType* pre_e);
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(SLinkList space, int S, ElemType cur_e, ElemType* next_e);
/*
* 插入
*
* 向静态链表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(SLinkList space, int S, int i, ElemType e);
/*
* 删除
*
* 删除静态链表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(SLinkList space, int S, int i, ElemType* e);
/*
* 遍历
*
* 用visit函数访问静态链表S
*/
void ListTraverse(SLinkList space, int S, void(Visit)(ElemType));
/*━━━━━━━━━━━━━━━━━━━━━━ 图形化输出 ━━━━━━━━━━━━━━━━━━━━━━*/
// 以图形化形式输出当前结构,仅限内部测试使用
void PrintGraph(SLinkList space, int S);
#endif

View File

@@ -0,0 +1,12 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(Difference SLinkList.h SLinkList.c Difference.h Difference.c Difference-main.c)
# 链接公共库
target_link_libraries(Difference Scanf_lib)
# 记录要拷贝到*.exe目录下的资源文件
file(GLOB TestData TestData*.txt)
# 将资源文件拷贝到*.exe目录下不然无法加载
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

View File

@@ -0,0 +1,19 @@
#include <stdio.h>
#include "Difference.h" //**▲02 线性表**//
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
SLinkList space; // 备用空间
int S; // 静态链表
printf("S = (A-B)(B-A) = ");
difference("TestData.txt", space, &S);
ListTraverse(space, S, PrintElem);
return 0;
}

View File

@@ -0,0 +1,95 @@
/*===============
* 静态链表去重
*
* 包含算法: 2.17
================*/
#include "Difference.h"
/*
* ████████ 算法2.17 ████████
*
* S = (A-B)(B-A)
*
* 对集合A和集合B进行(A-B)(B-A)计算计算结果存入静态链表S
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
*/
void difference(char path[], SLinkList space, int* S) {
FILE* fp;
int m, n; // 集合A和集合B中元素数量
int j; // 循环计数器
int R; // 指向静态链表最后一个结点
int i, k, p;
int b; // 临时存储从集合B中读到的数据
// 初始化备用空间
InitSpace(space);
// 获取静态链表头结点
*S = Malloc(space);
// 让R执行静态链表最后的结点
R = *S;
// 打开文件,准备读取测试数据
fp = fopen(path, "r");
if(fp == NULL) {
exit(ERROR);
}
// 读取集合A和集合B的元素个数
ReadData(fp, "%d%d", &m, &n);
// 录入集合A的数据
for(j = 1; j <= m; ++j) {
// 分配结点
i = Malloc(space);
// 输入集合A的元素值
ReadData(fp, "%d", &space[i].data);
// 将新结点插入到表尾
space[R].cur = i;
R = i;
}
// 尾结点的指针置空
space[R].cur = 0;
// 录入集合B的数据
for(j = 1; j <= n; ++j) {
// 输入集合B的元素值
ReadData(fp, "%d", &b);
p = *S; // 指向静态链表头结点后续总是指向k的前一个位置
k = space[*S].cur; // 指向静态链表中的首个元素
// 在当前静态链表中查找是否存在b元素
while(k != space[R].cur && space[k].data != b) {
p = k;
k = space[k].cur;
}
// 如果该元素不存在,则加入静态链表
if(k == space[R].cur) {
i = Malloc(space);
space[i].data = b;
space[i].cur = space[R].cur;
space[R].cur = i;
// 如果该元素已存在,则需要移除
} else {
space[p].cur = space[k].cur;
Free(space, k);
if(R == k) {
R = p;
}
}
}
fclose(fp);
}

View File

@@ -0,0 +1,28 @@
/*===============
* 静态链表去重
*
* 包含算法: 2.17
================*/
#ifndef DIFFERENCE_H
#define DIFFERENCE_H
#include <stdio.h>
#include "Status.h" //**▲01 绪论**//
#include "SLinkList.h" //**▲02 线性表**//
/*
* ████████ 算法2.17 ████████
*
* S = (A-B)(B-A)
*
* 对集合A和集合B进行(A-B)(B-A)计算计算结果存入静态链表S
*
*【备注】
* 教材中默认从控制台读取数据。
* 这里为了方便测试,避免每次运行都手动输入数据,
* 因而选择了从预设的文件path中读取测试数据。
*/
void difference(char path[], SLinkList space, int* S);
#endif

View File

@@ -0,0 +1,116 @@
/*=================================
* 静态链表
*
* 包含算法: 2.13、2.14、2.15、2.16
==================================*/
#include "SLinkList.h"
/*
* ██ 提示 ██
*
* 以下所有函数的形参中:
* space指示备用空间
* S :指示静态链表头结点索引
*/
/*━━━━━━━━━━━━━━━━━━━━━━ 备用空间操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* ████████ 算法2.14 ████████
*
* 初始化备用空间
*
* 备用空间为静态链表提供可用的内存。
*/
void InitSpace(SLinkList space) {
int i;
for(i = 0; i < MAXSIZE - 1; ++i) {
space[i].cur = i + 1;
}
space[MAXSIZE - 1].cur = 0;
}
/*
* ████████ 算法2.15 ████████
*
* 申请空间
*
* 为静态链表从备用空间申请结点空间,
* 如果申请成功,返回可用空间的索引,
* 申请失败时返回0。
*/
int Malloc(SLinkList space) {
int i = space[0].cur;
if(i != 0) {
// 将申请到的空间从备用空间中删去
space[0].cur = space[i].cur;
}
// 返回新申请结点下标
return i;
}
/*
* ████████ 算法2.16 ████████
*
* 回收空间
*
* 回收索引k处的结点空间以供静态链表后续复用
* 回收方式就是将该结点空间从静态链表上移除,
* 并将其添加到备用空间的列表当中。
*/
void Free(SLinkList space, int k) {
space[k].cur = space[0].cur;
space[0].cur = k;
}
/*━━━━━━━━━━━━━━━━━━━━━━ 静态链表操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 遍历
*
* 用visit函数访问静态链表S
*/
void ListTraverse(SLinkList space, int S, void(Visit)(ElemType)) {
int p;
// 确保链表存在
if(S == 0 || space[S].cur == 0) {
return;
}
p = space[S].cur;
while(p != 0) {
Visit(space[p].data);
p = space[p].cur;
}
printf("\n");
}
/*━━━━━━━━━━━━━━━━━━━━━━ 图形化输出 ━━━━━━━━━━━━━━━━━━━━━━*/
// 以图形化形式输出当前结构,仅限内部测试使用
void PrintGraph(SLinkList space, int S) {
int i = 0;
printf("==== 备用空间 ====\n");
while(i < 20) {
printf("%2d | %2d | %2d |\n", i, space[i].data, space[i].cur);
i = space[i].cur;
}
printf("==== 静态链表 ====\n");
i = S;
while(i>0 && i < 20) {
printf("%2d | %2d | %2d |\n", i, space[i].data, space[i].cur);
i = space[i].cur;
}
}

View File

@@ -0,0 +1,89 @@
/*=================================
* 静态链表
*
* 包含算法: 2.13、2.14、2.15、2.16
==================================*/
#ifndef SLINKLIST_H
#define SLINKLIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 宏定义 */
#define MAXSIZE 1000 // 备用空间最大容量,近似于静态链表的最大长度
/* 静态链表元素类型定义 */
typedef int ElemType;
/*
* 静态链表结构
*
* 注:静态链表依托于一个数组,该数组包含了已占用空间和空闲空间
*/
typedef struct SLinkLNode {
ElemType data;
int cur; // cur是游标做指针用用来链接下一个结点区别于数组下标
} SLinkList[MAXSIZE]; // 链表空间类型
/*
* ████ 提示 ████
*
* 以下所有函数的形参中:
* space指示备用空间
* S :指示静态链表头结点索引
*/
/*━━━━━━━━━━━━━━━━━━━━━━ 备用空间操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* ████████ 算法2.14 ████████
*
* 初始化备用空间
*
* 备用空间为静态链表提供可用的内存。
*/
void InitSpace(SLinkList space);
/*
* ████████ 算法2.15 ████████
*
* 申请空间
*
* 为静态链表从备用空间申请结点空间,
* 如果申请成功,返回可用空间的索引,
* 申请失败时返回0。
*/
int Malloc(SLinkList space);
/*
* ████████ 算法2.16 ████████
*
* 回收空间
*
* 回收索引k处的结点空间以供静态链表后续复用
* 回收方式就是将该结点空间从静态链表上移除,
* 并将其添加到备用空间的列表当中。
*/
void Free(SLinkList space, int k);
/*━━━━━━━━━━━━━━━━━━━━━━ 静态链表操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 遍历
*
* 用visit函数访问静态链表S
*/
void ListTraverse(SLinkList space, int S, void(Visit)(ElemType));
/*━━━━━━━━━━━━━━━━━━━━━━ 图形化输出 ━━━━━━━━━━━━━━━━━━━━━━*/
// 以图形化形式输出当前结构,仅限内部测试使用
void PrintGraph(SLinkList space, int S);
#endif

View File

@@ -0,0 +1,4 @@
集合A的长度3
集合B的长度5
集合A中元素2 3 4
集合B中元素1 3 4 6 7

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(DuLinkList DuLinkList.h DuLinkList.c DuLinkList-main.c)
# 链接公共库
target_link_libraries(DuLinkList Scanf_lib)

View File

@@ -0,0 +1,149 @@
#include <stdio.h>
#include "Status.h"
#include "DuLinkList.h" //**▲02 线性表**//
// 判断data>e是否成立
Status CmpGreater(ElemType data, ElemType e) {
return data > e ? TRUE : FALSE;
}
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
DuLinkList L;
int i;
ElemType e;
printf("████████ InitList \n");
{
printf("█ 初始化双向循环链表 L ...\n");
InitList(&L);
}
PressEnterToContinue();
printf("████████ ListEmpty \n");
{
ListEmpty(L) ? printf("█ L 为空!!\n") : printf("█ L 不为空!\n");
}
PressEnterToContinue();
printf("████████ ListInsert \n");
{
for(i = 1; i <= 8; i++) {
printf("█ 在 L 第 %d 个位置插入 \"%d\" ...\n", i, 2 * i);
ListInsert(L, i, 2 * i);
}
}
PressEnterToContinue();
printf("████████ ListTraverse \n");
{
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ ListLength \n");
{
printf("█ L 的长度为 %d \n", ListLength(L));
}
PressEnterToContinue();
printf("████████ ListDelete \n");
{
printf("█ 删除前的元素L = ");
ListTraverse(L, PrintElem);
printf("█ 尝试删除 L 中第 6 个元素...\n");
if(ListDelete(L, 6, &e) == OK) {
printf("█ 删除成功,被删除元素是:\"%d\"\n", e);
} else {
printf("█ 删除失败,第 6 个元素不存在!\n");
}
printf("█ 删除后的元素L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ GetElem \n");
{
GetElem(L, 4, &e);
printf("█ L 中第 4 个位置的元素为 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ LocateElem \n");
{
i = LocateElem(L, 7, CmpGreater);
GetElem(L, i, &e);
printf("█ L 中第一个元素值大于 \"7\" 的元素是 \"%d\" \n", e);
}
PressEnterToContinue();
printf("████████ PriorElem \n");
{
ElemType cur_e = 6;
if(PriorElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的前驱为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的前驱不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ NextElem \n");
{
ElemType cur_e = 6;
if(NextElem(L, cur_e, &e) == OK) {
printf("█ 元素 \"%d\" 的后继为 \"%d\" \n", cur_e, e);
} else {
printf("█ 元素 \"%d\" 的后继不存在!\n", cur_e);
}
}
PressEnterToContinue();
printf("████████ ClearList \n");
{
printf("█ 清空 L 前:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
ClearList(L);
printf("█ 清空 L 后:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
}
PressEnterToContinue();
printf("████████ DestroyList \n");
{
printf("█ 销毁 L 前:");
L ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
DestroyList(&L);
printf("█ 销毁 L 后:");
L ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
}
PressEnterToContinue();
return 0;
}

View File

@@ -0,0 +1,402 @@
/*=====================
* 双向循环链表
*
* 包含算法: 2.18、2.19
======================*/
#include "DuLinkList.h" //**▲02 线性表**//
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(DuLinkList* L) {
*L = (DuLinkList) malloc(sizeof(DuLNode));
if(*L == NULL) {
exit(OVERFLOW);
}
// 前驱和后继均指向自身
(*L)->next = (*L)->prior = *L;
return OK;
}
/*
* 销毁(结构)
*
* 释放双向循环链表所占内存。
*/
Status DestroyList(DuLinkList* L) {
// 确保链表结构存在
if(L == NULL || *L == NULL) {
return ERROR;
}
ClearList(*L);
free(*L);
*L = NULL;
return OK;
}
/*
* 置空(内容)
*
* 这里需要释放双向循环链表中非头结点处的空间。
*/
Status ClearList(DuLinkList L) {
DuLinkList p, q;
if(L == NULL) {
return ERROR;
}
p = L->next;
while(p != L) {
q = p->next;
free(p);
p = q;
}
L->next = L->prior = L;
return OK;
}
/*
* 判空
*
* 判断双向循环链表中是否包含有效数据。
*
* 返回值:
* TRUE : 双向循环链表为空
* FALSE: 双向循环链表不为空
*/
Status ListEmpty(DuLinkList L) {
if(L != NULL && L->next == L && L->prior == L) {
return TRUE;
} else {
return FALSE;
}
}
/*
* 计数
*
* 返回双向循环链表包含的有效元素的数量。
*/
int ListLength(DuLinkList L) {
DuLinkList p;
int i;
if(L == NULL || L->next == L || L->prior == L) {
return 0;
}
i = 0;
p = L->next;
// 遍历所有结点
while(p != L) {
i++;
p = p->next;
}
return i;
}
/*
* 取值
*
* 获取双向循环链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(DuLinkList L, int i, ElemType* e) {
DuLinkList p;
int j;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return ERROR;
}
p = L;
j = 0;
// 寻找第i-1个结点且保证该结点的后继不为L
while(p->next != L && j < i - 1) {
p = p->next;
++j;
}
// 如果遍历到头了或者i的值不合规(比如i<=0),说明没找到合乎目标的结点
if(p->next == L || j > i - 1) {
return ERROR;
}
*e = p->next->data;
return OK;
}
/*
* 查找
*
* 返回双向循环链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(DuLinkList L, ElemType e, Status(Compare)(ElemType, ElemType)) {
int i;
DuLinkList p;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return 0;
}
i = 1; // i的初值为第1个元素的位序
p = L->next; // p的初值为第1个元素的指针
while(p != L && !Compare(p->data, e)) {
i++;
p = p->next;
}
if(p != L) {
return i;
} else {
return 0;
}
}
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(DuLinkList L, ElemType cur_e, ElemType* pre_e) {
DuLinkList p;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return ERROR;
}
// 指向第1个元素
p = L->next;
// 第1个元素没有前驱
if(p->data == cur_e) {
return ERROR;
}
// 指向第2个元素
p = p->next;
// 从第2个元素开始查找cur_e的位置
while(p != L && p->data != cur_e) {
p = p->next;
}
// 如果没找到元素cur_e查找失败返回ERROR
if(p == L) {
return ERROR;
}
*pre_e = p->prior->data;
return OK;
}
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(DuLinkList L, ElemType cur_e, ElemType* next_e) {
DuLinkList p;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return ERROR;
}
// 指向第1个元素
p = L->next;
// 从第1个元素开始查找cur_e的位置且保证该结点的后继不为L
while(p->next != L && p->data != cur_e) {
p = p->next;
}
// 如果没找到cur_e或者找到了但它没有后继均返回ERROR
if(p->next == L) {
return ERROR;
}
*next_e = p->next->data;
return OK;
}
/*
* ████████ 算法2.18 ████████
*
* 插入
*
* 向双向循环链表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(DuLinkList L, int i, ElemType e) {
DuLinkList p, s;
// 确保双向循环链表存在(但可能为空表)
if(L == NULL) {
return ERROR;
}
// 查找第i个结点位置引用
if((p = GetElemP(L, i)) == NULL) {
return ERROR;
}
// 创建新结点
s = (DuLinkList) malloc(sizeof(DuLNode));
if(s == NULL) {
exit(OVERFLOW);
}
s->data = e;
// 将s插入到p的前面称为第i个结点
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
return OK;
}
/*
* ████████ 算法2.19 ████████
*
* 删除
*
* 删除双向循环链表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(DuLinkList L, int i, ElemType* e) {
DuLinkList p;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return ERROR;
}
// 查找第i个结点位置引用
if((p = GetElemP(L, i)) == NULL) {
return ERROR;
}
// 如果p==L说明待删除元素是第len+1个元素不合规
if(p == L) {
return ERROR;
}
*e = p->data;
// 移除p结点
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
/*
* 遍历
*
* 用visit函数访问双向循环链表L
*/
void ListTraverse(DuLinkList L, void(Visit)(ElemType)) {
DuLinkList p;
// 确保双向循环链表存在
if(L == NULL || L->next == L || L->prior == L) {
return;
}
p = L->next;
while(p != L) {
Visit(p->data);
p = p->next;
}
printf("\n");
}
/*
* 获取循环链表L上第i个元素的引用
*
* ▓▓▓▓ 注 ▓▓▓▓
* 1.加static的含义是当前函数只在DuLinkList中使用不会被别的文件引用
* 2.假设链表长度为len且需要获取第len+1个元素的引用时由于这里是循环链表所以返回的是头结点
*/
static DuLinkList GetElemP(DuLinkList L, int i) {
DuLinkList p;
int count;
// 确保双向循环链表存在(但可能为空表)
if(L == NULL) {
return NULL;
}
// 位置不合规
if(i < 1) {
return NULL;
}
p = L;
count = 0;
// 尝试查找第i个元素
while(p->next != L && count < i) {
p = p->next;
++count;
}
// 恰好找到第i个元素
if(count == i) {
return p;
}
// 至此说明p->next==L此时需要判断i是否过大
if(count + 1 < i) {
return NULL;
}
// 至此说明需要在len+1的位置上插入元素
return L;
}

View File

@@ -0,0 +1,153 @@
/*=====================
* 双向循环链表
*
* 包含算法: 2.18、2.19
======================*/
#ifndef DULINKLIST_H
#define DULINKLIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "Status.h" //**▲01 绪论**//
/* 双向循环链表元素类型定义 */
typedef int ElemType;
/*
* 双向循环链表结构
*
* 注:这里的双向循环链表存在头结点
*/
typedef struct DuLNode {
ElemType data;
struct DuLNode* prior; // 前驱
struct DuLNode* next; // 后继
} DuLNode;
// 指向双向循环链表结点的指针
typedef DuLNode* DuLinkList;
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(DuLinkList* L);
/*
* 销毁(结构)
*
* 释放双向循环链表所占内存。
*/
Status DestroyList(DuLinkList* L);
/*
* 置空(内容)
*
* 这里需要释放双向循环链表中非头结点处的空间。
*/
Status ClearList(DuLinkList L);
/*
* 判空
*
* 判断双向循环链表中是否包含有效数据。
*
* 返回值:
* TRUE : 双向循环链表为空
* FALSE: 双向循环链表不为空
*/
Status ListEmpty(DuLinkList L);
/*
* 计数
*
* 返回双向循环链表包含的有效元素的数量。
*/
int ListLength(DuLinkList L);
/*
* 取值
*
* 获取双向循环链表中第i个元素将其存储到e中。
* 如果可以找到返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数但这不符合编码的通用约定。
* 通常i的含义应该指索引即从0开始计数。
*/
Status GetElem(DuLinkList L, int i, ElemType* e);
/*
* 查找
*
* 返回双向循环链表中首个与e满足Compare关系的元素位序。
* 如果不存在这样的元素则返回0。
*
*【备注】
* 元素e是Compare函数第二个形参
*/
int LocateElem(DuLinkList L, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* 前驱
*
* 获取元素cur_e的前驱
* 如果存在将其存储到pre_e中返回OK
* 如果不存在则返回ERROR。
*/
Status PriorElem(DuLinkList L, ElemType cur_e, ElemType* pre_e);
/*
* 后继
*
* 获取元素cur_e的后继
* 如果存在将其存储到next_e中返回OK
* 如果不存在则返回ERROR。
*/
Status NextElem(DuLinkList L, ElemType cur_e, ElemType* next_e);
/*
* ████████ 算法2.18 ████████
*
* 插入
*
* 向双向循环链表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListInsert(DuLinkList L, int i, ElemType e);
/*
* ████████ 算法2.19 ████████
*
* 删除
*
* 删除双向循环链表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
*/
Status ListDelete(DuLinkList L, int i, ElemType* e);
/*
* 遍历
*
* 用visit函数访问双向循环链表L
*/
void ListTraverse(DuLinkList L, void(Visit)(ElemType));
/*
* 获取循环链表L上第i个元素的引用
*
* ▓▓▓▓ 注意 ▓▓▓▓
* 1.加static的含义是当前函数只在DuLinkList中使用不会被别的文件引用
* 2.假设链表长度为len且需要获取第len+1个元素的引用时由于这里是循环链表所以返回的是头结点
*/
static DuLinkList GetElemP(DuLinkList L, int i);
#endif

View File

@@ -0,0 +1,7 @@
# 包含公共库
include_directories(${CMAKE_SOURCE_DIR}/Status)
# 生成可执行文件
add_executable(ELinkList ELinkList.h ELinkList.c ELinkList-main.c)
# 链接公共库
target_link_libraries(ELinkList Scanf_lib)

View File

@@ -0,0 +1,224 @@
#include <stdio.h>
#include "ELinkList.h" //**▲02 线性表**//
// 判断data>e是否成立
Status CmpGreater(ElemType data, ElemType e) {
return data > e ? TRUE : FALSE;
}
// 测试函数,打印元素
void PrintElem(ElemType e) {
printf("%d ", e);
}
int main(int argc, char** argv) {
ELinkList L;
Link p, q, s;
Position r;
int i;
ElemType e;
printf("████████ InitList \n");
{
printf("█ 初始化扩展的线性链表 L ...\n");
InitList(&L);
}
PressEnterToContinue();
printf("████████ ListEmpty \n");
{
ListEmpty(L) ? printf("█ L 为空!!\n") : printf("█ L 不为空!\n");
}
PressEnterToContinue();
printf("████████ ListInsert \n");
{
for(i = 1; i <= 8; i++) {
printf("█ 在 L 第 %d 个位置插入 \"%d\" ...\n", i, 2 * i);
ListInsert(&L, i, 2 * i);
}
}
PressEnterToContinue();
printf("████████ ListTraverse \n");
{
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ ListLength \n");
{
printf("█ L 的长度为 %d \n", ListLength(L));
}
PressEnterToContinue();
printf("████████ ListDelete \n");
{
printf("█ 删除前的元素L = ");
ListTraverse(L, PrintElem);
printf("█ 尝试删除 L 中第 6 个元素...\n");
if(ListDelete(&L, 6, &e) == OK) {
printf("█ 删除成功,被删除元素是:\"%d\"\n", e);
} else {
printf("█ 删除失败,第 6 个元素不存在!\n");
}
printf("█ 删除后的元素L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ LocateElem \n");
{
r = LocateElem(L, 7, CmpGreater);
printf("█ L 中第一个元素值大于 \"7\" 的元素是 \"%d\" \n", r->data);
}
PressEnterToContinue();
printf("████████ PriorElem \n");
{
Position pre = PriorPos(L, r);
if(pre !=NULL) {
printf("█ 元素 \"%d\" 的前驱为 \"%d\" \n", r->data, pre->data);
} else {
printf("█ 元素 \"%d\" 的前驱不存在!\n", r->data);
}
}
PressEnterToContinue();
printf("████████ NextElem \n");
{
Position next = NextPos(L, r);
if(next !=NULL) {
printf("█ 元素 \"%d\" 的后继为 \"%d\" \n", r->data, next->data);
} else {
printf("█ 元素 \"%d\" 的后继不存在!\n", r->data);
}
}
PressEnterToContinue();
printf("████████ MakeNode \n");
{
printf("█ 创建结点 \"100\" ...\n");
MakeNode(&p, 100);
printf("█ 创建结点 \"200\" ...\n");
MakeNode(&q, 200);
printf("█ 创建结点 \"300\" ...\n");
MakeNode(&s, 300);
}
PressEnterToContinue();
printf("████████ InsFirst \n");
{
printf("█ 将元素 \"%d\" 插入到元素 \"%d\" 之后...\n", s->data, r->data);
InsFirst(&L, r, s);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ DelFirst \n");
{
printf("█ 删除元素 \"%d\" 之后的元素...\n", r->data);
DelFirst(&L, r, &s);
printf("█ 被删除的元素为:\"%d\"\n", s->data);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ InsBefore \n");
{
Link t = r;
printf("█ 将元素 \"%d\" 插入到元素 \"%d\" 之前...\n", p->data, t->data);
InsBefore(&L, &t, p);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ InsAfter \n");
{
Link t = r;
printf("█ 将元素 \"%d\" 插入到元素 \"%d\" 之后...\n", q->data, t->data);
InsAfter(&L, &t, q);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ Append \n");
{
printf("█ 创建结点 \"400\" ...\n");
MakeNode(&s, 400);
printf("█ 将元素 %d 插入到链表最后...\n", s->data);
Append(&L, s);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ Remove \n");
{
printf("█ 从链表最后删除元素...\n");
Remove(&L, &s);
printf("█ 被删除的元素为:\"%d\"\n", s->data);
printf("█ L 中的元素为L = ");
ListTraverse(L, PrintElem);
}
PressEnterToContinue();
printf("████████ ClearList \n");
{
printf("清空 L 前:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
ClearList(&L);
printf("清空 L 后:");
ListEmpty(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
}
PressEnterToContinue();
printf("████████ DestroyList \n");
{
printf("销毁 L 前:");
L.head != NULL && L.tail != NULL ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
DestroyList(&L);
printf("销毁 L 后:");
L.head != NULL && L.tail != NULL ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
}
PressEnterToContinue();
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
/*=======================
* 扩展的单链表(线性链表)
*
* 包含算法: 2.20
========================*/
#ifndef ELINKLIST_H
#define ELINKLIST_H
#include <stdio.h>
#include <stdlib.h> // 提供malloc、realloc、free、exit原型
#include <limits.h> // 提供一些极限常量
#include "Status.h" //**▲01 绪论**//
/*
* ████ 注意 ████
*
* 教材中的线性链表命名为LinkList
* 这里为了与单链表区分故将其命名为ELinkList。
* 线性链表可以理解成对普通链表的一种扩展。
*/
/* 线性链表元素类型定义 */
typedef int ElemType;
/*
* 线性链表结构
*
* 注:这里的线性链表存在头结点
*/
typedef struct LNode {
ElemType data;
struct LNode* next;
} LNode, * Link, * Position;
/* 维护线性链表头尾指针及长度信息 */
typedef struct {
Link head, tail; // 分别指向线性链表中的头结点和尾结点
int len; // 指示线性链表中数据元素的个数
} ELinkList;
/*━━━━━━━━━━━━━━━━━━━━━━ 内存操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 内存分配
*
* 为线性链表申请一个结点并存入指定的数据e。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
Status MakeNode(Link* p, ElemType e);
/*
* 内存回收
*
* 释放线性链表中指定的结点。
*
*【备注】
* static修饰的含义是该函数仅限当前文件内使用
*/
void FreeNode(Link* p);
/*━━━━━━━━━━━━━━━━━━━━━━ 链表常规操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 初始化
*
* 初始化成功则返回OK否则返回ERROR。
*/
Status InitList(ELinkList* L);
/*
* 销毁(结构)
*
* 释放链表所占内存。
*/
Status DestroyList(ELinkList* L);
/*
* 置空(内容)
*
* 这里需要释放链表中非头结点处的空间。
*/
Status ClearList(ELinkList* L);
/*
* 判空
*
* 判断链表中是否包含有效数据。
*
* 返回值:
* TRUE : 链表为空
* FALSE: 链表不为空
*/
Status ListEmpty(ELinkList L);
/*
* 计数
*
* 返回链表包含的有效元素的数量。
*/
int ListLength(ELinkList L);
/*
* 查找
*
* 返回链表中首个与e满足Compare关系的元素引用。
* 如果不存在这样的元素则返回NULL。
*
*【备注】
* 1.元素e是Compare函数第二个形参
* 2.这里的返回值是目标元素的引用,而不是其位序
*/
Position LocateElem(ELinkList L, ElemType e, Status(Compare)(ElemType, ElemType));
/*
* ████████ 算法2.20 ████████
*
* 插入
*
* 向链表第i个位置上插入e插入成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
* 可以看做是算法2.9的改写
*/
Status ListInsert(ELinkList* L, int i, ElemType e);
/*
* 删除
*
* 删除链表第i个位置上的元素并将被删除元素存储到e中。
* 删除成功则返回OK否则返回ERROR。
*
*【备注】
* 教材中i的含义是元素位置从1开始计数
* 可以看做是算法2.10的改写
*/
Status ListDelete(ELinkList* L, int i, ElemType* e);
/*
* 遍历
*
* 用visit函数访问链表L
*/
void ListTraverse(ELinkList L, void(Visit)(ElemType));
/*━━━━━━━━━━━━━━━━━━━━━━ 链表扩展操作 ━━━━━━━━━━━━━━━━━━━━━━*/
/*
* 取值
*
* 获取结点p的元素值。
*/
ElemType GetCurElem(Link p);
/*
* 设值
*
* 为结点p设置元素值。
*/
Status SetCurElem(Link p, ElemType e);
/*
* 头结点
*
* 获取头结点引用。
*/
Position GetHead(ELinkList L);
/*
* 尾结点
*
* 获取尾结点引用。
*/
Position GetLast(ELinkList L);
/*
* 前驱
*
* 获取结点p的前驱如果不存在则返回NULL。
*/
Position PriorPos(ELinkList L, Link p);
/*
* 后继
*
* 获取结点p的后继如果不存在则返NULL。
*/
Position NextPos(ELinkList L, Link p);
/*
* 查找
*
* 查找链表L中第i(允许为0)个结点并将其引用存入p且返回OK
* 如果i值不合规则返回ERROR
* 特别注意当i为0时p存储的是头结点的引用
*/
Status LocatePos(ELinkList L, int i, Link* p);
/*
* 插入
*
* 将s结点插入到h结点后面成为h后面的第一个结点
*
*【备注】
* 教材中对于该方法的描述有些问题,这里是修正过的版本
*/
Status InsFirst(ELinkList* L, Link h, Link s);
/*
* 删除
*
* 删除h结点后的第一个结点并用q存储被删除结点的引用
*
*【备注】
* 教材中对于该方法的定义略显粗糙,这里是修正过的版本
*/
Status DelFirst(ELinkList* L, Link h, Link* q);
/*
* 前向插入
*
* 将s结点插入到p结点之前并将p指向新结点
*/
Status InsBefore(ELinkList* L, Link* p, Link s);
/*
* 后向插入
*
* 将s结点插入到p结点之前并将p指向新结点
*/
Status InsAfter(ELinkList* L, Link* p, Link s);
/*
* 向尾部添加
*
* 将s所指的一串结点链接在链表L后面
*/
Status Append(ELinkList* L, Link s);
/*
* 从尾部移除
*
* 将链表的尾结点移除并将被移除的结点引用存储在q中
*/
Status Remove(ELinkList* L, Link* q);
#endif

Some files were not shown because too many files have changed in this diff Show More