本文收录自【C语言数据结构】思想+代码集】
【数据结构】线性表
思想
代码
头文件及函数定义 SqList.h
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 2.3、2.4、2.5、2.6
=============================*/
#ifndef SQLIST_H
#define SQLIST_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。q
*/
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
函数源码SqList.cpp
/*=============================
* 线性表的顺序存储结构(顺序表)
*
* 包含算法: 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");
}
main函数
#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;
}