第 2 章 线性表 (线性表的单链表存储结构实现)

1. 背景说明

2. 示例代码

  1. status.h
cpp 复制代码
/* DataStructure 预定义常量和类型头文件 */

#ifndef STATUS_H
#define STATUS_H

#define CHECK_NULL(pointer) if (!(pointer)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_NULL_PTR); \
	return NULL; \
}

#define CHECK_RET(ret) if (ret != RET_OK) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ret); \
	return ret; \
}

#define CHECK_VALUE(value, ERR_CODE) if (value) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return ERR_CODE; \
}

#define CHECK_FALSE(value, ERR_CODE) if (!(value)) { \
	printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_CODE); \
	return FALSE; \
} 

/* 函数结果状态码 */
#define TRUE 					1			/* 返回值为真 */
#define FALSE 					0			/* 返回值为假 */
#define RET_OK 					0			/* 返回值正确 */
#define INFEASIABLE    		   	2			/* 返回值未知 */
#define ERR_MEMORY     		   	3			/* 访问内存错 */
#define ERR_NULL_PTR   			4			/* 空指针错误 */
#define ERR_MEMORY_ALLOCATE		5			/* 内存分配错 */
#define ERR_NULL_STACK			6			/* 栈元素为空 */
#define ERR_PARA				7			/* 函数参数错 */
#define ERR_OPEN_FILE			8			/* 打开文件错 */
#define ERR_NULL_QUEUE			9			/* 队列为空错 */
#define ERR_FULL_QUEUE			10			/* 队列为满错 */
#define ERR_NOT_FOUND			11			/* 表项不存在 */
typedef int Status;							/* Status 是函数的类型,其值是函数结果状态代码,如 RET_OK 等 */
typedef int Bollean;						/* Boolean 是布尔类型,其值是 TRUE 或 FALSE */

#endif // !STATUS_H
  1. singleLinkList.h
cpp 复制代码
/* 线性表的单链表存储结构头文件 */

#ifndef SINGLELINKLIST_H
#define SINGLELINKLIST_H

#include "status.h"

typedef int ElemType;

typedef struct LNode {
	ElemType data;
	struct LNode *next;
} *LinkList;

/* 辅助函数,创建一个新的节点 */
LinkList MakeNewLNode(ElemType e);

/* 操作结果:构造一个空的线性表 L */
Status InitList(LinkList *L);

/* 初始条件:线性表 L 已存在。操作结果:销毁线性表 L */
Status DestroyList(LinkList *L);

/* 初始条件:线性表 L 已存在。操作结果:将 L 重置为空表 */
Status ClearList(LinkList L);

/* 初始条件:线性表 L 已存在。操作结果:若 L 为空表,则返回 TRUE,否则返回 FALSE */
Status ListEmpty(LinkList L);

/* 初始条件:线性表 L 已存在。操作结果:返回 L 中数据元素个数 */
int ListLength(LinkList L);

/* 算法 2.8,L 为带头结点的单链表的头指针。当第 i 个元素存在时, 其值赋给 e 并返回 OK,否则返回 ERROR */
Status GetElem(LinkList L, int i, ElemType *e);

/* 初始条件: 线性表 L 已存在, compare() 是数据元素判定函数(满足为 1,否则为 0)
   操作结果: 返回 L 中第 1 个与 e 满足关系 compare() 的数据元素的位序。
   若这样的数据元素不存在,则返回值为 0 */
int LocateElem(LinkList L, ElemType e, Bollean(*compare)(ElemType, ElemType));

/* 初始条件: 线性表 L 已存在
   操作结果: 若 cur_e 是 L 的数据元素,且不是第一个,则用 pre_e 返回它的前驱
   函数返回 OK;否则操作失败, pre_e 无定义, 返回 INFEASIBLE */
Status PriorElem(LinkList L, ElemType cur_e, ElemType *pre_e);

/* 初始条件:线性表 L 已存在
   操作结果:若 cur_e 是 L 的数据元素,且不是最后一个,则用 next_e 返回它的后继
   函数返回 OK;否则操作失败,next_e无定义,返回 INFEASIBLE */
Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e);

/* 算法 2.9,在带头结点的单链线性表 L 中第 i 个位置之前插入元素 e */
Status ListInsert(LinkList L, int i, ElemType e);

/* 算法 2.10,在带头结点的单链线性表 L 中,删除第 i 个元素,并由 e 返回其值 */
Status ListDelete(LinkList L, int i, ElemType *e);

/* 初始条件:线性表 L 已存在
   操作结果:依次对 L 的每个数据元素调用函数 vi()。一旦 vi() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*vi)(ElemType));

#endif
  1. singleLinkList.c
cpp 复制代码
/* 线性表的单链表存储结构源文件实现 */

#include "singleLinkList.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/* 辅助函数,创建一个新的节点 */
LinkList MakeNewLNode(ElemType e)
{
	LinkList newLNode = (LinkList)malloc(sizeof(struct LNode));
	if (!newLNode) {
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_MEMORY_ALLOCATE);
		return NULL;
	}

	newLNode->data = e;
	newLNode->next = NULL;

	return newLNode;
}

/* 操作结果:构造一个空的线性表 L */
Status InitList(LinkList *L)
{
	*L = (LinkList)malloc(sizeof(struct LNode));
	if (!(*L)) {
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_MEMORY_ALLOCATE);
		return ERR_MEMORY_ALLOCATE;
	}

	(*L)->next = NULL;

	return RET_OK;
}

/* 初始条件:线性表 L 已存在。操作结果:销毁线性表 L */
Status DestroyList(LinkList *L)
{
	LinkList q;
	while (*L) {
		q = (*L)->next;
		free(*L);
		*L = q;
	}

	return RET_OK;
}

/* 初始条件:线性表 L 已存在。操作结果:将 L 重置为空表 */
Status ClearList(LinkList L)
{
	LinkList p, q;
	p = L->next;
	while (p) {
		q = p->next;
		free(p);
		p = q;
	}

	L->next = NULL;

	return RET_OK;
}

/* 初始条件:线性表 L 已存在。操作结果:若 L 为空表,则返回 TRUE,否则返回 FALSE */
Status ListEmpty(LinkList L)
{
	if (L->next) {
		return FALSE;
	}

	return TRUE;
}

/* 初始条件:线性表 L 已存在。操作结果:返回 L 中数据元素个数 */
int ListLength(LinkList L)
{
	int count = 0;
	LinkList p = L->next;
	while (p) {
		++count;
		p = p->next;
	}

	return count;
}

/* 算法 2.8,L 为带头结点的单链表的头指针。当第 i 个元素存在时, 其值赋给 e 并返回 RET_OK,否则返回 ERROR */
Status GetElem(LinkList L, int i, ElemType *e)
{
	int j = 1;
	LinkList p = L->next;
	while (p && j < i) {
		p = p->next;
		++j;
	}

	if (!p || j > i) {			/* j > i 适用于 i < 1 时,如 i = 0 */
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_PARA);
		return ERR_PARA;
	}

	*e = p->data;

	return RET_OK;
}

/* 初始条件: 线性表 L 已存在, compare() 是数据元素判定函数(满足为 1,否则为 0)
   操作结果: 返回 L 中第 1 个与 e 满足关系 compare() 的数据元素的位序。
   若这样的数据元素不存在,则返回值为 0 */
int LocateElem(LinkList L, ElemType e, Bollean(*compare)(ElemType, ElemType))
{
	int i = 0;
	LinkList p = L->next;
	while (p) {
		++i;
		if (compare(p->data, e)) {
			return i;
		}

		p = p->next;
	}

	return 0;
}

/* 初始条件: 线性表 L 已存在
   操作结果: 若 cur_e 是 L 的数据元素,且不是第一个,则用 pre_e 返回它的前驱
   函数返回 RET_OK;否则操作失败, pre_e 无定义, 返回 INFEASIBLE */
Status PriorElem(LinkList L, ElemType cur_e, ElemType *pre_e)
{
	LinkList q, p = L->next;
	while (p->next) {
		q = p->next;
		if (q->data == cur_e) {
			*pre_e = p->data;
			return RET_OK;
		}

		p = q;
	}

	return INFEASIABLE;
}

/* 初始条件:线性表 L 已存在
   操作结果:若 cur_e 是 L 的数据元素,且不是最后一个,则用 next_e 返回它的后继
   函数返回 RET_OK;否则操作失败,next_e无定义,返回 INFEASIBLE */
Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e)
{
	LinkList p = L->next;
	while (p->next) {
		if (p->data == cur_e) {
			*next_e = p->next->data;
			return RET_OK;
		}

		p = p->next;
	}

	return INFEASIABLE;
}

/* 算法 2.9,在带头结点的单链线性表 L 中第 i 个位置之前插入元素 e */
Status ListInsert(LinkList L, int i, ElemType e)
{
	int j = 0;
	LinkList p = L;
	while (p && j < i - 1) {
		++j;
		p = p->next;
	}

	if (!p || j > i - 1) {				/* 超出表长或者 i < 1 */
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_PARA);
		return ERR_PARA;
	}

	LinkList newLNode = MakeNewLNode(e);
	if (!newLNode) {
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_MEMORY_ALLOCATE);
		return ERR_MEMORY_ALLOCATE;
	}

	newLNode->next = p->next;
	p->next = newLNode;

	return RET_OK;
}

/* 算法 2.10,在带头结点的单链线性表 L 中,删除第 i 个元素,并由 e 返回其值 */
Status ListDelete(LinkList L, int i, ElemType *e)
{
	int j = 0;
	LinkList p = L;
	while (p->next && j < i - 1) {
		++j;
		p = p->next;
	}

	if (!p->next || j > i - 1) {	/* 理论上 j 最多只能等于 i - 1, 但此处当参数不合法时可用, 建议单独判断参数合法性 */
		printf("FuncName: %-15s Line: %-5d ErrorCode: %-3d\n", __func__, __LINE__, ERR_PARA);
		return ERR_PARA;
	}

	LinkList q = p->next;
	p->next = q->next;
	*e = q->data;
	free(q);

	return RET_OK;
}

/* 初始条件:线性表 L 已存在
   操作结果:依次对 L 的每个数据元素调用函数 vi()。一旦 vi() 失败,则操作失败 */
Status ListTraverse(LinkList L, void(*vi)(ElemType))
{
	LinkList p = L->next;
	while (p) {
		vi(p->data);
		p = p->next;
	}

	return RET_OK;
}
  1. algorithm.h
cpp 复制代码
/* 算法定义头文件 */
#ifndef ALGORITHM_H
#define ALGORITHM_H

#include "singleLinkList.h"

/* 算法 2.11,逆位序(插在表头)输入 n 个元素的值,建立带表头结构的单链线性表 L */
void CreateList(LinkList *L, int n);

/* 正位序(插在表尾)输入 n 个元素的值,建立带表头结构的单链线性表 */
void CreateList2(LinkList *L, int n);

/* 算法 2.12,已知单链线性表 La 和 Lb 的元素按值非递减排列
   归并 La 和 Lb 得到新的单链线性表 Lc,Lc 的元素也按值非递减排列
   此处需要释放 Lb 头节点故需要传入 Lb 指针变量 */
void MergeList(LinkList La, LinkList *Lb, LinkList *Lc);

/* 算法 2.1 其他实现,将所有在线性表 Lb 中但不在 La 中的数据元素插入到 La 中 */
void Union(LinkList La, LinkList Lb);

/* 算法 2.2 其他实现,已知线性表 La 和 Lb 中的数据元素按值非递减排列
   归并 La 和 Lb 得到新的线性表 Lc,Lc 的数据元素也按值非递减排列 */
Status MergeList2(LinkList La, LinkList Lb, LinkList *Lc);

#endif // !ALGORITHM_H
  1. algorithm.c
cpp 复制代码
/* 算法实现源文件 */
#include "algorithm.h"
#include "auxiliary.h"
#include <stdlib.h>
#include <stdio.h>

/* 算法 2.11,逆位序(插在表头)输入 n 个元素的值,建立带表头结构的单链线性表 L */
void CreateList(LinkList *L, int n)
{
	(void)InitList(L);
	printf("Please input %d integers: ", n);
	int num;
	LinkList p;
	for (int i = 0; i < n; ++i) {
		scanf_s("%d", &num);
		p = MakeNewLNode(num);
		p->next = (*L)->next;
		(*L)->next = p;
	}
}

/* 正位序(插在表尾)输入 n 个元素的值,建立带表头结构的单链线性表 */
void CreateList2(LinkList *L, int n)
{
	(void)InitList(L);
	LinkList q = *L, p;
	int num;
	printf("Please input %d integers: ", n);
	for (int i = 0; i < n; ++i) {
		scanf_s("%d", &num);
		p = MakeNewLNode(num);
		q->next = p;
		q = p;
	}
}

/* 算法 2.12,已知单链线性表 La 和 Lb 的元素按值非递减排列
   归并 La 和 Lb 得到新的单链线性表 Lc,Lc 的元素也按值非递减排列 
   此处需要释放 Lb 头节点故需要传入 Lb 指针变量 */
void MergeList(LinkList La, LinkList *Lb, LinkList *Lc)
{
	LinkList pa = La->next, pb = (*Lb)->next, pc = NULL;
	*Lc = pc = La;
	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;
	free(*Lb);
}

/* 算法 2.1 其他实现,将所有在线性表 Lb 中但不在 La 中的数据元素插入到 La 中 */
void Union(LinkList La, LinkList Lb)
{
	int lengthLa = ListLength(La);
	int lengthLb = ListLength(Lb);
	ElemType e;
	for (int i = 0; i < lengthLb; ++i) {
		GetElem(Lb, i + 1, &e);
		if (!LocateElem(La, e, Equal)) {
			ListInsert(La, ++lengthLa, e);
		}
	}
}

/* 算法 2.2 其他实现,已知线性表 La 和 Lb 中的数据元素按值非递减排列
   归并 La 和 Lb 得到新的线性表 Lc,Lc 的数据元素也按值非递减排列 */
Status MergeList2(LinkList La, LinkList Lb, LinkList *Lc)
{
	CHECK_VALUE(!Lc, ERR_NULL_PTR)
	InitList(Lc);
	int lengthLa = ListLength(La);
	int lengthLb = ListLength(Lb);
	ElemType ai, bj;
	int i = 1, j = 1, k = 0;
	while ((i <= lengthLa) && (j <= lengthLb)) {
		GetElem(La, i, &ai);
		GetElem(Lb, j, &bj);
		if (ai <= bj) {
			ListInsert(*Lc, ++k, ai);
			++i;
		} else {
			ListInsert(*Lc, ++k, bj);
			++j;
		}
	}

	while (i <= lengthLa) {
		GetElem(La, i++, &ai);
		ListInsert(*Lc, ++k, ai);
	}

	while (j <= lengthLb) {
		GetElem(Lb, j++, &bj);
		ListInsert(*Lc, ++k, bj);
	}

	return RET_OK;
}
  1. auxiliary.h
cpp 复制代码
/* 辅助函数定义头文件 */

#ifndef AUXILIARY_H
#define AUXILIARY_H
#include "singleLinkList.h"

void Visit(ElemType e);
Bollean Equal(ElemType e1, ElemType e2);
void ShowList(char str[], LinkList L);

#endif // !AUXILIARY_H
  1. auxiliary.c
cpp 复制代码
/* 辅助函数实现源文件 */

#include "auxiliary.h"
#include <stdio.h>

void Visit(ElemType e)
{
	printf("%d ", e);
}

Bollean Equal(ElemType e1, ElemType e2)
{
	return (e1 == e2) ? TRUE : FALSE;
}

void ShowList(char str[], LinkList L)
{
	printf("%s", str);
	ListTraverse(L, Visit);
	putchar('\n');
}
  1. main.c
cpp 复制代码
/* 主函数入口源文件 */
#include "singleLinkList.h"
#include "algorithm.h"
#include "auxiliary.h"
#include <stdio.h>

int main(void)
{
	int n = 5;
	LinkList La;
	CreateList(&La, n);
	ShowList("La is: ", La);
	LinkList Lb;
	CreateList2(&Lb, n);
	ShowList("Lb is: ", Lb);
	LinkList Lc;
	MergeList(La, &Lb, &Lc);
	ShowList("Lc is: ", Lc);
	int ret = DestroyList(&Lc);
	CHECK_RET(ret);
	/* free() 的作用:仅仅是释放堆内存,不将指针置空 */
	printf("After Destroy list Lc, the address of Lc is %p\n", Lc);

	/* 算法 2.1 其他实现测试 */
	LinkList LA, LB;
	CreateList2(&LA, 5);
	CreateList2(&LB, 5);
	ShowList("LA is: ", LA);
	ShowList("LB is: ", LB);
	Union(LA, LB);
	ShowList("LA is: ", LA);
	ShowList("LB is: ", LB);
	DestroyList(&LA);
	DestroyList(&LB);

	/* 算法 2.2 其他实现测试 */
	LinkList Lm, Ln;
	CreateList2(&Lm, 5);
	CreateList2(&Ln, 5);
	ShowList("Lm is: ", Lm);
	ShowList("Ln is: ", Ln);
	LinkList Lu;
	MergeList2(Lm, Ln, &Lu);
	ShowList("Lu is: ", Lu);
	DestroyList(&Lm);
	DestroyList(&Ln);
	DestroyList(&Lu);

	return 0;
}

3. 运行示例

相关推荐
算法歌者11 分钟前
[算法]入门1.矩阵转置
算法
林开落L26 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色27 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
tyler_download28 分钟前
手撸 chatgpt 大模型:简述 LLM 的架构,算法和训练流程
算法·chatgpt
SoraLuna1 小时前
「Mac玩转仓颉内测版7」入门篇7 - Cangjie控制结构(下)
算法·macos·动态规划·cangjie
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
鸽鸽程序猿1 小时前
【算法】【优选算法】前缀和(上)
java·算法·前缀和
九圣残炎1 小时前
【从零开始的LeetCode-算法】2559. 统计范围内的元音字符串数
java·算法·leetcode
YSRM1 小时前
Experimental Analysis of Dedicated GPU in Virtual Framework using vGPU 论文分析
算法·gpu算力·vgpu·pci直通
iiiiiankor1 小时前
C/C++内存管理 | new的机制 | 重载自己的operator new
java·c语言·c++