mirror of
https://github.com/kangjianwei/Data-Structure.git
synced 2026-02-06 00:07:05 +08:00
♻️ 线性表章节的源码重构
This commit is contained in:
8
CLion/CMakeLists.txt
Normal file
8
CLion/CMakeLists.txt
Normal 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)
|
||||
7
CLion/CourseBook/0201_SqList/CMakeLists.txt
Normal file
7
CLion/CourseBook/0201_SqList/CMakeLists.txt
Normal 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)
|
||||
170
CLion/CourseBook/0201_SqList/SqList-main.c
Normal file
170
CLion/CourseBook/0201_SqList/SqList-main.c
Normal 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;
|
||||
}
|
||||
337
CLion/CourseBook/0201_SqList/SqList.c
Normal file
337
CLion/CourseBook/0201_SqList/SqList.c
Normal 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");
|
||||
}
|
||||
149
CLion/CourseBook/0201_SqList/SqList.h
Normal file
149
CLion/CourseBook/0201_SqList/SqList.h
Normal 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
|
||||
7
CLion/CourseBook/0202_Union/CMakeLists.txt
Normal file
7
CLion/CourseBook/0202_Union/CMakeLists.txt
Normal 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)
|
||||
172
CLion/CourseBook/0202_Union/SqList.c
Normal file
172
CLion/CourseBook/0202_Union/SqList.c
Normal 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");
|
||||
}
|
||||
93
CLion/CourseBook/0202_Union/SqList.h
Normal file
93
CLion/CourseBook/0202_Union/SqList.h
Normal 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
|
||||
43
CLion/CourseBook/0202_Union/Union-main.c
Normal file
43
CLion/CourseBook/0202_Union/Union-main.c
Normal 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 = La∪Lb = ");
|
||||
Union(&La, Lb);
|
||||
ListTraverse(La, PrintElem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
45
CLion/CourseBook/0202_Union/Union.c
Normal file
45
CLion/CourseBook/0202_Union/Union.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*==============
|
||||
* 求并集
|
||||
*
|
||||
* 包含算法: 2.1
|
||||
===============*/
|
||||
|
||||
#include "Union.h" //**▲02 线性表**//
|
||||
|
||||
/*
|
||||
* ████████ 算法2.1 ████████
|
||||
*
|
||||
* A=A∪B
|
||||
*
|
||||
* 计算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;
|
||||
}
|
||||
33
CLion/CourseBook/0202_Union/Union.h
Normal file
33
CLion/CourseBook/0202_Union/Union.h
Normal 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=A∪B
|
||||
*
|
||||
* 计算La与Lb的并集并返回。
|
||||
* 由于生成的并集会拼接在La上,所以La的入参为指针类型。
|
||||
*/
|
||||
void Union(SqList* La, SqList Lb);
|
||||
|
||||
/*
|
||||
* 判等
|
||||
*
|
||||
* 判断两元素是否相等。
|
||||
* 如果相等,则返回TRUE,否则,返回FALSE。
|
||||
*/
|
||||
Status equal(ElemType e1, ElemType e2);
|
||||
|
||||
#endif
|
||||
7
CLion/CourseBook/0203_MergeSqList/CMakeLists.txt
Normal file
7
CLion/CourseBook/0203_MergeSqList/CMakeLists.txt
Normal 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)
|
||||
49
CLion/CourseBook/0203_MergeSqList/MergeSqList-main.c
Normal file
49
CLion/CourseBook/0203_MergeSqList/MergeSqList-main.c
Normal 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;
|
||||
}
|
||||
104
CLion/CourseBook/0203_MergeSqList/MergeSqList.c
Normal file
104
CLion/CourseBook/0203_MergeSqList/MergeSqList.c
Normal 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++;
|
||||
}
|
||||
}
|
||||
35
CLion/CourseBook/0203_MergeSqList/MergeSqList.h
Normal file
35
CLion/CourseBook/0203_MergeSqList/MergeSqList.h
Normal 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
|
||||
129
CLion/CourseBook/0203_MergeSqList/SqList.c
Normal file
129
CLion/CourseBook/0203_MergeSqList/SqList.c
Normal 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");
|
||||
}
|
||||
80
CLion/CourseBook/0203_MergeSqList/SqList.h
Normal file
80
CLion/CourseBook/0203_MergeSqList/SqList.h
Normal 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
|
||||
12
CLion/CourseBook/0204_LinkList/CMakeLists.txt
Normal file
12
CLion/CourseBook/0204_LinkList/CMakeLists.txt
Normal 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})
|
||||
170
CLion/CourseBook/0204_LinkList/LinkList-main.c
Normal file
170
CLion/CourseBook/0204_LinkList/LinkList-main.c
Normal 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;
|
||||
}
|
||||
|
||||
488
CLion/CourseBook/0204_LinkList/LinkList.c
Normal file
488
CLion/CourseBook/0204_LinkList/LinkList.c
Normal 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;
|
||||
}
|
||||
168
CLion/CourseBook/0204_LinkList/LinkList.h
Normal file
168
CLion/CourseBook/0204_LinkList/LinkList.h
Normal 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
|
||||
1
CLion/CourseBook/0204_LinkList/TestData_Head.txt
Normal file
1
CLion/CourseBook/0204_LinkList/TestData_Head.txt
Normal file
@@ -0,0 +1 @@
|
||||
降序:9 7 5 3 1
|
||||
1
CLion/CourseBook/0204_LinkList/TestData_Tail.txt
Normal file
1
CLion/CourseBook/0204_LinkList/TestData_Tail.txt
Normal file
@@ -0,0 +1 @@
|
||||
升序:2 4 6 8 10
|
||||
12
CLion/CourseBook/0205_MergeList/CMakeLists.txt
Normal file
12
CLion/CourseBook/0205_MergeList/CMakeLists.txt
Normal 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})
|
||||
138
CLion/CourseBook/0205_MergeList/LinkList.c
Normal file
138
CLion/CourseBook/0205_MergeList/LinkList.c
Normal 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;
|
||||
}
|
||||
61
CLion/CourseBook/0205_MergeList/LinkList.h
Normal file
61
CLion/CourseBook/0205_MergeList/LinkList.h
Normal 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
|
||||
31
CLion/CourseBook/0205_MergeList/MergeList-main.c
Normal file
31
CLion/CourseBook/0205_MergeList/MergeList-main.c
Normal 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;
|
||||
}
|
||||
|
||||
45
CLion/CourseBook/0205_MergeList/MergeList.c
Normal file
45
CLion/CourseBook/0205_MergeList/MergeList.c
Normal 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;
|
||||
}
|
||||
24
CLion/CourseBook/0205_MergeList/MergeList.h
Normal file
24
CLion/CourseBook/0205_MergeList/MergeList.h
Normal 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
|
||||
1
CLion/CourseBook/0205_MergeList/TestData_Head.txt
Normal file
1
CLion/CourseBook/0205_MergeList/TestData_Head.txt
Normal file
@@ -0,0 +1 @@
|
||||
降序:9 7 5 3 1
|
||||
1
CLion/CourseBook/0205_MergeList/TestData_Tail.txt
Normal file
1
CLion/CourseBook/0205_MergeList/TestData_Tail.txt
Normal file
@@ -0,0 +1 @@
|
||||
升序:2 4 6 8 10
|
||||
7
CLion/CourseBook/0206_SLinkList/CMakeLists.txt
Normal file
7
CLion/CourseBook/0206_SLinkList/CMakeLists.txt
Normal 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)
|
||||
152
CLion/CourseBook/0206_SLinkList/SLinkList-main.c
Normal file
152
CLion/CourseBook/0206_SLinkList/SLinkList-main.c
Normal 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;
|
||||
}
|
||||
|
||||
470
CLion/CourseBook/0206_SLinkList/SLinkList.c
Normal file
470
CLion/CourseBook/0206_SLinkList/SLinkList.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
208
CLion/CourseBook/0206_SLinkList/SLinkList.h
Normal file
208
CLion/CourseBook/0206_SLinkList/SLinkList.h
Normal 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
|
||||
12
CLion/CourseBook/0207_Difference/CMakeLists.txt
Normal file
12
CLion/CourseBook/0207_Difference/CMakeLists.txt
Normal 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})
|
||||
19
CLion/CourseBook/0207_Difference/Difference-main.c
Normal file
19
CLion/CourseBook/0207_Difference/Difference-main.c
Normal 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;
|
||||
}
|
||||
95
CLion/CourseBook/0207_Difference/Difference.c
Normal file
95
CLion/CourseBook/0207_Difference/Difference.c
Normal 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);
|
||||
}
|
||||
28
CLion/CourseBook/0207_Difference/Difference.h
Normal file
28
CLion/CourseBook/0207_Difference/Difference.h
Normal 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
|
||||
116
CLion/CourseBook/0207_Difference/SLinkList.c
Normal file
116
CLion/CourseBook/0207_Difference/SLinkList.c
Normal 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;
|
||||
}
|
||||
}
|
||||
89
CLion/CourseBook/0207_Difference/SLinkList.h
Normal file
89
CLion/CourseBook/0207_Difference/SLinkList.h
Normal 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
|
||||
4
CLion/CourseBook/0207_Difference/TestData.txt
Normal file
4
CLion/CourseBook/0207_Difference/TestData.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
集合A的长度:3
|
||||
集合B的长度:5
|
||||
集合A中元素:2 3 4
|
||||
集合B中元素:1 3 4 6 7
|
||||
7
CLion/CourseBook/0208_DuLinkList/CMakeLists.txt
Normal file
7
CLion/CourseBook/0208_DuLinkList/CMakeLists.txt
Normal 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)
|
||||
149
CLion/CourseBook/0208_DuLinkList/DuLinkList-main.c
Normal file
149
CLion/CourseBook/0208_DuLinkList/DuLinkList-main.c
Normal 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;
|
||||
}
|
||||
402
CLion/CourseBook/0208_DuLinkList/DuLinkList.c
Normal file
402
CLion/CourseBook/0208_DuLinkList/DuLinkList.c
Normal 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;
|
||||
}
|
||||
153
CLion/CourseBook/0208_DuLinkList/DuLinkList.h
Normal file
153
CLion/CourseBook/0208_DuLinkList/DuLinkList.h
Normal 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
|
||||
7
CLion/CourseBook/0209_ELinkList/CMakeLists.txt
Normal file
7
CLion/CourseBook/0209_ELinkList/CMakeLists.txt
Normal 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)
|
||||
224
CLion/CourseBook/0209_ELinkList/ELinkList-main.c
Normal file
224
CLion/CourseBook/0209_ELinkList/ELinkList-main.c
Normal 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;
|
||||
}
|
||||
|
||||
630
CLion/CourseBook/0209_ELinkList/ELinkList.c
Normal file
630
CLion/CourseBook/0209_ELinkList/ELinkList.c
Normal file
File diff suppressed because it is too large
Load Diff
253
CLion/CourseBook/0209_ELinkList/ELinkList.h
Normal file
253
CLion/CourseBook/0209_ELinkList/ELinkList.h
Normal 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
Reference in New Issue
Block a user