第 4 章 串(图书关键字索引表实现)

1. 背景说明

需要从书目文件中获取其关键字及对应的书号索引

第 4 章 串(图书关键字索引表实现)_第1张图片

bookInfo.txt

005 Computer Data Structures
010 Introduction to Data Structures
023 Fundamentals of Data Structures
034 The Design and Analysis of Computer Algorithms
050 Introduction to Numerical Analysis
067 Numerical Analysis

normalWord.txt

5
A
An
In
Of
The

2. 示例代码

1) status.h

/* 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

2) linkList.h

/* 具有实用意义的线性链表(带头结点)实现头文件 */

#ifndef LINKLIST_H
#define LINKLIST_H

#include "status.h"

typedef int ElemType;

typedef struct LNode {
	ElemType data;
	struct LNode *next;
} LNode, *Link, *Position;

typedef struct LinkList {
	Link head;
	Link tail;
	int length;
} LinkList;

/* 分配由指向的值为 e 的结点,并返回节点地址;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e);

/* 释放 p 所指结点 */
Status FreeLNode(Link *p);

/* 构造一个空的线性链表 */
Status InitList(LinkList *list);

/* 将线性链表 list 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *list);

/* 销毁线性链表 list,list 不再存在 */
Status DestroyList(LinkList *list);

/* head 指向 list 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(Link head, Link s, LinkList *list);

/* head 指向 list 的一个结点,把 head 当做头结点,删除链表中的第一个结点并以 q 返回
   若链表为空( head 指向尾结点),q = NULL */
Status DelFirst(Link head, Link *q, LinkList *list);

/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一
   串结点链接在线性链表 list 的最后一个结点之后,并改变链表 list 的尾指针指向新的尾结点 */
Status Append(Link s, LinkList *list);

/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接前驱的位置若无前驱
   则用 *targetPos 返回 NULL */
Status PriorPos(const Link p, const LinkList *list, Position *targetPos);

/* 删除线性链表 list 中的尾结点并以 q 返回,改变链表 list 的尾指针指向新的尾结点 */
Status Remove(Link *q, LinkList *list);

/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之前
   并修改指针 *p 指向新插入的结点 */
Status InsBefore(Link s, Link *p, LinkList *list);

/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之后
   并修改指针 p 指向新插入的结点 */
Status InsAfter(Link s, Link *p, LinkList *list);

/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(ElemType e, Link p);

/* 已知 p 指向线性链表中的一个结点,用 *e 返回 p 所指结点中数据元素的值 */
Status GetCurrElem(Link p, ElemType *e);

/* 若线性链表 list 为空表,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status ListEmpty(const LinkList *list, Bollean *isEmpty);

/* 用 *num 返回线性链表 list 中元素个数 */
Status ListLength(const LinkList *list, int *num);

/* 用 *targetPos 返回线性链表 list 中头结点的位置 */
Status GetHead(const LinkList *L, Position *targetPos);

/* 用 *targetPos 返回线性链表 list 中最后一个结点的位置 */
Status GetLast(const LinkList *L, Position *targetPos);

/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接后继的位置
   若无后继,则用 *targetPos 返回 NULL */
Status NextPos(Link p, Position *targetPos);

/* 返回 p 指示线性链表 list 中第 i 个结点的位置,并返回 OK,i 值不合法时
   返回 ERROR, i = 0 为头结点 */
Status LocatePos(int i, const LinkList *list, Link *p);

/* 用 *targetPos 返回线性链表 list 中第 1 个与 e 满足函数 compare() 判定关系的元素的位
   置若不存在这样的元素,则用 *targetPos 返回 NULL */
Status LocateElem(ElemType e, Bollean(*compare)(ElemType, ElemType), const LinkList *list, Position *targetPos);

/* 依次对 list 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(void(*visit)(ElemType), const LinkList *list);

/* 已知 list 为有序线性链表,将元素 e 按非降序插入在 list 中 */
Status InsertAscend(ElemType e, int(*compare)(ElemType, ElemType), LinkList *list);

/* 若升序链表 list 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 list 中
   第一个值为 e 的结点的位置,并用 *find 返回 TRUE;否则 q 指示第一个与 e 满足判定函数
   compare() 取值 > 0 的元素的前驱的位置, 并用 *find 返回 FALSE */
Status LocateElemP(ElemType e, int(*compare)(ElemType, ElemType), const LinkList *list, Bollean *find, Position *q);

#endif // !LINKLIST_H

3) linkList.c

/* 具有实用意义的线性链表(带头结点)实现源文件 */

#include "linkList.h"
#include 
#include 

/* 分配由指向的值为 e 的结点,并返回节点地址;若分配失败, 则返回 NULL */
Link MakeNewLNode(ElemType e)
{
	Link newLNode = (Link)malloc(sizeof(LNode));
	CHECK_NULL(newLNode);
	newLNode->data = e;
	newLNode->next = NULL;

	return newLNode;
}

/* 释放 p 所指结点 */
Status FreeLNode(Link *p)
{
	CHECK_VALUE(!p || !(*p), ERR_NULL_PTR);
	free(*p);
	*p = NULL;

	return RET_OK;
}

/* 构造一个空的线性链表 */
Status InitList(LinkList *list)
{
	CHECK_VALUE(!list, ERR_NULL_PTR);
	Link newLNode = (Link)malloc(sizeof(LNode));
	CHECK_VALUE(!newLNode, ERR_MEMORY_ALLOCATE);
	newLNode->next = NULL;
	list->head = list->tail = newLNode;
	list->length = 0;

	return RET_OK;
}

/* 将线性链表 list 重置为空表,并释放原链表的结点空间 */
Status ClearList(LinkList *list)
{
	CHECK_VALUE(!list, ERR_NULL_PTR);
	if (list->head == list->tail) {
		return RET_OK;
	}

	Link p = list->head->next;
	list->head->next = NULL;
	Link q = NULL;
	while (p != list->tail) {
		q = p;
		p = p->next;
		free(q);
	}

	free(p);
	list->tail = list->head;
	list->length = 0;

	return RET_OK;
}

/* 销毁线性链表 list,list 不再存在 */
Status DestroyList(LinkList *list)
{
	CHECK_VALUE(!list, ERR_NULL_PTR);
	ClearList(list);
	FreeLNode(&(list->head));
	list->tail = NULL;

	return RET_OK;
}

/* head 指向 list 的一个结点,把 head 当做头结点,将 s 所指结点插入在第一个结点之前 */
Status InsFirst(Link head, Link s, LinkList *list)
{
	CHECK_VALUE(!head || !s || !list, ERR_NULL_PTR);
	s->next = head->next;
	head->next = s;
	if (head == list->tail) {
		list->tail = s;
	}

	++(list->length);

	return RET_OK;
}

/* head 指向 list 的一个结点,把 head 当做头结点,删除链表中的第一个结点并以 q 返回
   若链表为空( head 指向尾结点),q = NULL */
Status DelFirst(Link head, Link *q, LinkList *list)
{
	CHECK_VALUE(!head || !q || !(*q) || !list, ERR_NULL_PTR);
	*q = head->next;
	if (!(*q)) {
		return RET_OK;
	}

	head->next = (*q)->next;
	--(list->length);
	if (!(head->next)) {
		list->tail = head;
	}

	return RET_OK;
}

/* 将指针 s(s->data 为第一个数据元素)所指(彼此以指针相链,以 NULL 结尾)的一
   串结点链接在线性链表 list 的最后一个结点之后,并改变链表 list 的尾指针指向新的尾结点 */
Status Append(Link s, LinkList *list)
{
	CHECK_VALUE(!s || !list, ERR_NULL_PTR);
	int length = 1;
	list->tail->next = s;
	while (s->next) {
		++length;
		s = s->next;
	}

	list->tail = s;
	list->length += length;

	return RET_OK;
}

/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接前驱的位置若无前驱
   则用 *targetPos 返回 NULL */
Status PriorPos(const Link p, const LinkList *list, Position *targetPos)
{
	CHECK_VALUE(!p || !list || !targetPos, ERR_NULL_PTR);
	Link q = list->head->next;
	if (p == q) {
		*targetPos = NULL;
		return RET_OK;
	}

	while (q->next != p) {
		q = q->next;
	}

	*targetPos = q;

	return RET_OK;
}

/* 删除线性链表 list 中的尾结点并以 q 返回,改变链表 list 的尾指针指向新的尾结点 */
Status Remove(Link *q, LinkList *list)
{
	CHECK_VALUE(!q || !(*q) || !list, ERR_NULL_PTR);
	if (list->length == 0) {
		*q = NULL;
		return RET_OK;
	}

	*q = list->tail;
	Link p = list->head;
	while (p->next != list->tail) {
		p = p->next;
	}

	p->next = NULL;
	list->tail = p;
	--(list->length);

	return RET_OK;
}

/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之前
   并修改指针 *p 指向新插入的结点 */
Status InsBefore(Link s, Link *p, LinkList *list)
{
	CHECK_VALUE(!s || !p || !(*p) || !list, ERR_NULL_PTR);
	Link q;
	PriorPos(*p, list, &q);
	if (!q) {
		q = list->head;
	}

	s->next = *p;
	q->next = s;
	*p = s;
	++(list->length);

	return RET_OK;
}

/* 已知 *p 指向线性链表 list 中的一个结点,将 s 所指结点插入在 *p 所指结点之后
   并修改指针 p 指向新插入的结点 */
Status InsAfter(Link s, Link *p, LinkList *list)
{
	CHECK_VALUE(!s || !p || !(*p) || !list, ERR_NULL_PTR);
	if (*p == list->tail) {
		(*list).tail = s;
	}

	s->next = (*p)->next;
	(*p)->next = s;
	*p = s;
	++(list->length);

	return RET_OK;
}

/* 已知 p 指向线性链表中的一个结点,用 e 更新 p 所指结点中数据元素的值 */
Status SetCurrElem(ElemType e, Link p)
{
	CHECK_VALUE(!p, ERR_NULL_PTR);
	p->data = e;

	return RET_OK;
}

/* 已知 p 指向线性链表中的一个结点,用 *e 返回 p 所指结点中数据元素的值 */
Status GetCurrElem(Link p, ElemType *e)
{
	CHECK_VALUE(!p, ERR_NULL_PTR);
	*e = p->data;

	return RET_OK;
}

/* 若线性链表 list 为空表,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status ListEmpty(const LinkList *list, Bollean *isEmpty)
{
	CHECK_VALUE(!list || !isEmpty, ERR_NULL_PTR);
	*isEmpty = (list->length == 0) ? TRUE : FALSE;

	return RET_OK;
}

/* 用 *num 返回线性链表 list 中元素个数 */
Status ListLength(const LinkList *list, int *num)
{
	CHECK_VALUE(!list || !num, ERR_NULL_PTR);
	*num = list->length;

	return RET_OK;
}

/* 用 *targetPos 返回线性链表 list 中头结点的位置 */
Status GetHead(const LinkList *L, Position *targetPos)
{
	CHECK_VALUE(!L || !targetPos, ERR_NULL_PTR);
	*targetPos = L->head;

	return RET_OK;
}

/* 用 *targetPos 返回线性链表 list 中最后一个结点的位置 */
Status GetLast(const LinkList *L, Position *targetPos)
{
	CHECK_VALUE(!L || !targetPos, ERR_NULL_PTR);
	*targetPos = L->tail;

	return RET_OK;
}

/* 已知 p 指向线性链表 list 中的一个结点,用 *targetPos 返回 p 所指结点的直接后继的位置
   若无后继,则用 *targetPos 返回 NULL */
Status NextPos(Link p, Position *targetPos)
{
	CHECK_VALUE(!p || !targetPos, ERR_NULL_PTR);
	*targetPos = p->next;

	return RET_OK;
}

/* 返回 p 指示线性链表 list 中第 i 个结点的位置,并返回 OK,i 值不合法时
   返回 ERROR, i = 0 为头结点 */
Status LocatePos(int i, const LinkList *list, Link *p)
{
	CHECK_VALUE(!list || !p, ERR_NULL_PTR);
	CHECK_VALUE((i < 0 || i > list->length), ERR_PARA);
	*p = list->head;
	for (int j = 0; j < i; ++j) {
		*p = (*p)->next;
	}

	return RET_OK;
}

/* 用 *targetPos 返回线性链表 list 中第 1 个与 e 满足函数 compare() 判定关系的元素的位
   置若不存在这样的元素,则用 *targetPos 返回 NULL */
Status LocateElem(ElemType e, Bollean(*compare)(ElemType, ElemType), const LinkList *list, Position *targetPos)
{
	CHECK_VALUE(!compare || !list || !targetPos, ERR_NULL_PTR);
	Link p = list->head->next;
	while ((p) && !(compare(p->data, e))) {
		p = p->next;
	}

	*targetPos = p;

	return RET_OK;
}

/* 依次对 list 的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败 */
Status ListTraverse(void(*visit)(ElemType), const LinkList *list)
{
	CHECK_VALUE(!visit || !list, ERR_NULL_PTR);
	Link p = list->head->next;
	for (int i = 0; i < list->length; ++i) {
		visit(p->data);
		p = p->next;
	}

	return RET_OK;
}

/* 已知 list 为有序线性链表,将元素 e 按非降序插入在 list 中 */
Status InsertAscend(ElemType e, int(*compare)(ElemType, ElemType), LinkList *list)
{
	CHECK_VALUE(!compare || !list, ERR_NULL_PTR);
	Link q = list->head;
	Link p = q->next;
	while ((p) && (compare(p->data, e) < 0)) {
		q = p;
		p = p->next;
	}

	Link newLNode = MakeNewLNode(e);
	CHECK_VALUE(!newLNode, ERR_NULL_PTR);
	q->next = newLNode;
	newLNode->next = p;
	++(list->length);
	if (!p) {
		list->tail = newLNode;
	}

	return RET_OK;
}

/* 若升序链表 list 中存在与 e 满足判定函数 compare() 取值为 0 的元素,则 q 指示 list 中
   第一个值为 e 的结点的位置,并用 *find 返回 TRUE;否则 q 指示第一个与 e 满足判定函数
   compare() 取值 > 0 的元素的前驱的位置, 并用 *find 返回 FALSE */
Status LocateElemP(ElemType e, int(*compare)(ElemType, ElemType), const LinkList *list, Bollean *find, Position *q)
{
	CHECK_VALUE(!compare || !list || !find || !q, ERR_NULL_PTR);
	Link pos = list->head;
	Link p = pos->next;
	while ((p) && (compare(p->data, e) < 0)) {
		pos = p;
		p = p->next;
	}

	if ((!p) || (compare(p->data, e) > 0)) {
		*q = pos;
		*find = FALSE;
	}

	*q = p;
	*find = TRUE;

	return RET_OK;
}

4) hString.h

/* 串的堆分配存储实现头文件 */

#ifndef HSTRING_H
#define HSTRING_H

#include "status.h"

typedef struct {
	char *ch;
	int length;
} HString;

/* 初始化(产生空串)字符串 t */
Status InitString(HString *t);

/* 生成一个其值等于串常量 chars 的串 t */
Status StrAssign(const char *chars, HString *t);

/* 初始条件: 串 s 存在
   操作结果: 由串 s 复制得串 t */
Status StrCopy(const HString *s, HString *t);

/* 初始条件: 串 s 存在
   操作结果: 若 s 为空串,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status StrEmpty(const HString *s, Bollean *isEmpty);

/* 若 s > t,则返回值 > 0;若 s = t,则返回值 = 0;若 s < t,则返回值 < 0 */
Status StrCompare(const HString *s, const HString *t, int *ret);

/* 返回 s 的元素个数,称为串的长度 */
Status StrLength(const HString *s, int *length);

/* 将 s 清为空串 */
Status ClearString(HString *s);

/* 用 t 返回由 s1 和 s2 联接而成的新串 */
Status Concat(const HString *s1, const HString *s2, HString *t);

/* 用 sub 返回串 s 的第 pos 个字符起长度为 len 的子串
   其中,1 ≤ pos ≤ sLength 且 0 ≤ len ≤ sLength - pos + 1 */
Status SubString(int pos, int len, const HString *s, HString *sub);

/* 算法 4.1,t 为非空串。若主串 s 中第 pos 个字符之后存在与 t 相等的子串
   则返回第一个这样的子串在 s 中的位置,否则返回 0 */
Status Index(int pos, const HString *s, const HString *t, int *targetPos);

/* 算法 4.4, 在串 s 的第 pos 个字符之前插入串 t, 1 ≤ pos ≤ sLength + 1 */
Status StrInsert(int pos, const HString *t, HString *s);

/* 从串 s 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *s);

/* 初始条件: 串 s, t 和 v 存在, t 是非空串(此函数与串的存储结构无关)
   操作结果: 用 v 替换主串 s 中出现的所有与 t 相等的不重叠的子串 */
Status Replace(const HString *t, const HString *v, HString *s);

/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void);

/* 输出 t 字符串 */
Status StrPrint(const HString *t);

#endif // !HSTRING_H

5) hString.c

/* 串的堆分配存储实现源文件 */

#include "hString.h"
#include 
#include 
#include 

/* 初始化(产生空串)字符串 t */
Status InitString(HString *t)
{
	CHECK_VALUE(!t, ERR_NULL_PTR);
	t->length = 0;
	t->ch = NULL;

	return RET_OK;
}

/* 生成一个其值等于串常量 chars 的串 t */
Status StrAssign(const char *chars, HString *t)
{
	CHECK_VALUE(!chars || !t, ERR_NULL_PTR);
	if (t->ch) {
		free(t->ch);
	}

	int length = (int)strlen(chars);
	if (length == 0) {
		t->ch = NULL;
		t->length = 0;
		return RET_OK;
	}

	t->ch = (char *)malloc(sizeof(char) * length);
	CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);
	t->length = length;
	for (int i = 0; i < length; ++i) {
		t->ch[i] = chars[i];
	}

	return RET_OK;
}

/* 初始条件: 串 s 存在
   操作结果: 由串 s 复制得串 t */
Status StrCopy(const HString *s, HString *t)
{
	CHECK_VALUE(!s || !t, ERR_NULL_PTR);
	if (t->ch) {
		free(t->ch);
	}

	t->ch = (char *)malloc(sizeof(char) * (s->length));
	CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);
	t->length = s->length;
	for (int i = 0; i < s->length; ++i) {
		t->ch[i] = s->ch[i];
	}

	return RET_OK;
}

/* 初始条件: 串 s 存在
   操作结果: 若 s 为空串,则用 *isEmpty 返回 TRUE,否则用 *isEmpty 返回 FALSE */
Status StrEmpty(const HString *s, Bollean *isEmpty)
{
	CHECK_VALUE(!s || isEmpty, ERR_NULL_PTR);
	*isEmpty = ((s->length == 0) && (s->ch == NULL)) ? TRUE : FALSE;

	return RET_OK;
}

/* 若 s > t,则返回值 > 0;若 s = t,则返回值 = 0;若 s < t,则返回值 < 0 */
Status StrCompare(const HString *s, const HString *t, int *ret)
{
	CHECK_VALUE(!s || !t || !ret, ERR_NULL_PTR);
	for (int i = 0; (i < s->length) && (i < t->length); ++i) {
		if (s->ch[i] != t->ch[i]) {
			*ret = s->ch[i] - t->ch[i];
			return RET_OK;
		}
	}

	*ret = s->length - t->length;
	
	return RET_OK;
}

/* 返回 s 的元素个数,称为串的长度 */
Status StrLength(const HString *s, int *length)
{
	CHECK_VALUE(!s || !length, ERR_NULL_PTR);
	*length = s->length;

	return RET_OK;
}

/* 将 s 清为空串 */
Status ClearString(HString *s)
{
	CHECK_VALUE(!s, ERR_NULL_PTR);
	if (s->ch) {
		free(s->ch);
		s->ch = NULL;
	}

	s->length = 0;

	return RET_OK;
}

/* 用 t 返回由 s1 和 s2 联接而成的新串 */
Status Concat(const HString *s1, const HString *s2, HString *t)
{
	CHECK_VALUE(!s1 || !s2 || !t, ERR_NULL_PTR);
	if (t->ch) {
		free(t->ch);
	}

	int length = s1->length + s2->length;
	t->ch = (char *)malloc(sizeof(char) * length);
	CHECK_VALUE(!(t->ch), ERR_MEMORY_ALLOCATE);
	t->length = length;
	for (int i = 0; i < s1->length; ++i) {
		t->ch[i] = s1->ch[i];
	}

	for (int i = 0; i < s2->length; ++i) {
		t->ch[s1->length + i] = s2->ch[i];
	}

	return RET_OK;
}

/* 用 sub 返回串 s 的第 pos 个字符起长度为 len 的子串
   其中,1 ≤ pos ≤ sLength 且 0 ≤ len ≤ sLength - pos + 1 */
Status SubString(int pos, int len, const HString *s, HString *sub)
{
	CHECK_VALUE(!s || !sub, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > s->length) || (len < 1) || (len > s->length - pos + 1), ERR_PARA);
	if (sub->ch) {
		free(sub->ch);
	}

	if (len == 0) {
		sub->ch = NULL;
		sub->length = 0;
	} else {
		sub->ch = (char *)malloc(sizeof(char) * len);
		CHECK_VALUE(!(sub->ch), ERR_MEMORY_ALLOCATE);
		for (int i = 0; i < len; ++i) {
			sub->ch[i] = s->ch[pos - 1 + i];
		}

		sub->length = len;
	}

	return RET_OK;
}

/* 算法 4.1,t 为非空串。若主串 s 中第 pos 个字符之后存在与 t 相等的子串
   则返回第一个这样的子串在 s 中的位置,否则返回 0 */
Status Index(int pos, const HString *s, const HString *t, int *targetPos)
{
	CHECK_VALUE(!s || !t || !targetPos, ERR_NULL_PTR);
	/* 模块内部调用,不检查返回值,外部调用需要检查 */
	int sLength, tLength;
	StrLength(s, &sLength);
	StrLength(t, &tLength);
	int maxRange = sLength - tLength + 1;
	CHECK_VALUE((pos < 1) || (pos > maxRange), ERR_PARA);
	HString sub;
	InitString(&sub);
	Status subRet;
	int ret;
	while (pos <= maxRange) {
		subRet = SubString(pos, tLength, s, &sub);
		CHECK_VALUE(subRet != RET_OK, subRet);
		StrCompare(s, t, &ret);
		if (ret != 0) {
			++pos;
		} else {
			*targetPos = pos;
			return RET_OK;
		}
	}

	*targetPos = 0;

	return RET_OK;
}

/* 算法 4.4, 在串 s 的第 pos 个字符之前插入串 t, 1 ≤ pos ≤ sLength + 1 */
Status StrInsert(int pos, const HString *t, HString *s)
{
	CHECK_VALUE(!t || !s, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > s->length + 1), ERR_PARA);
	if (s->length == 0) {
		return RET_OK;
	}

	s->ch = (char *)realloc(s->ch, sizeof(char) * (unsigned long long)(s->length + s->length));
	CHECK_VALUE(!(s->ch), ERR_MEMORY_ALLOCATE);
	for (int i = s->length - 1; i >= pos - 1; --i) {
		s->ch[i + t->length] = s->ch[i];
	}

	s->length += t->length;
	for (int i = 0; i < t->length; ++i) {
		s->ch[pos - 1 + i] = t->ch[i];
	}

	return RET_OK;
}

/* 从串 s 中删除第 pos 个字符起长度为 len 的子串 */
Status StrDelete(int pos, int len, HString *s)
{
	CHECK_VALUE(!s, ERR_NULL_PTR);
	CHECK_VALUE((pos < 1) || (pos > s->length) || (len < 1) || (len > s->length - pos + 1), ERR_PARA);
	for (int i = pos - 1; i < s->length - len; ++i) {
		s->ch[i] = s->ch[i + len];
	}

	s->ch = (char *)realloc(s->ch, sizeof(char) * s->length);
	CHECK_VALUE(!s->ch, ERR_MEMORY_ALLOCATE);
	s->length -= len;

	return RET_OK;
}

/* 初始条件: 串 s, t 和 v 存在, t 是非空串(此函数与串的存储结构无关)
   操作结果: 用 v 替换主串 s 中出现的所有与 t 相等的不重叠的子串 */
Status Replace(const HString *t, const HString *v, HString *s)
{
	CHECK_VALUE(!t || !v || !s, ERR_NULL_PTR);
	int pos = 1, targetPos, tLength, vLength;
	StrLength(t, &tLength);
	StrLength(v, &vLength);
	Status ret;
	do {
		Index(pos, s, t, &targetPos);
		pos = targetPos;
		if (pos) {
			ret = StrDelete(pos, tLength, s);
			CHECK_VALUE(ret != RET_OK, ret);
			ret = StrInsert(pos, t, s);
			CHECK_VALUE(ret != RET_OK, ret);
			pos += vLength;
		}
	} while (pos);

	return RET_OK;
}

/* 堆分配类型的字符串无法销毁(结构体)*/
void DestroyString(void)
{
	printf("Do not need to destroy!\n");
}

/* 输出 t 字符串 */
Status StrPrint(const HString *t)
{
	CHECK_VALUE(!t, ERR_NULL_PTR);
	for (int i = 0; i < t->length; ++i) {
		putchar(t->ch[i]);
	}

	return RET_OK;
}

6) bookNameSort.h

/* 关键字索引表定义头文件 */

#ifndef BOOKNAMESORT_H
#define BOOKNAMESORT_H

#include "hString.h"
#include "linkList.h"

#define MAX_KEY_NUM 25			/* 索引表的最大容量(关键词的最大数) */
#define MAX_LINE_LEN 100		/* 书目串(书名 + 书号) buff 的最大长度 */
#define MAX_KEY_WORD_LEN 15		/* 关键字最大长度 */
#define MAX_WORD_NUM 10			/* 词表(一本书的关键词)的最大容量 */
#define MAX_NORMAL_NUM 10		/* 常用词(仅指大写)的最大数 */
#define MAX_BOOK_NUM 10			/* 最大书数量 */

typedef struct {
	char *item[MAX_WORD_NUM];
	int last;
} WordListType;

typedef struct {
	char *item[MAX_NORMAL_NUM];
	int last;
} NormalIdxType;

typedef struct {
	HString key;
	LinkList bookNumList;
} IdxTermType;

typedef struct {
	IdxTermType item[MAX_KEY_NUM];
	int last;
} IdxListType;

typedef struct {
	char bookName[MAX_LINE_LEN];
	int bookNum;
} BookTermType;

typedef struct {
	BookTermType item[MAX_BOOK_NUM];
	int last;
} BookListType;

/* 初始化操作,置索引表 idxlist 为空表,且在 idxliat.item[0] 设一空串 */
Status InitIdxList(IdxListType *idxList);

Status ExtractKeyWord(const char *buff, const NormalIdxType *normalIdx, int *bookNum, WordListType *wordList);

/* 用 keyWord 返回词表 wordList 中第 i 个关键词 */
Status GetKeyWord(int i, const WordListType *wordList, HString *keyWord);

/* 在索引表 idxList 中查询是否存在与 keyWord 相等的关键词。若存在,则用 *pos 返回其
   在索引表中的位置, 且 *exist 取值 TRUE; 否则用 *pos 返回插入位置,且 *exist 取值 FALSE */
Status LocateIdx(const IdxListType *idxList, const HString *keyWord, Bollean *exist, int *pos);

/* 在索引表 idxList 的第 i 项前插入新关键词 keyWord,并初始化书号索引的链表为空表 */
Status InsertNewKeyWord(int i, const HString *keyWord, IdxListType *idxList);

/* 在索引表 idxList 的第 i 项前插入书号为 bookNum 的索引 */
Status InsertBookNum(int i, int bookNum, IdxListType *idxList);

/* 将书号为 bookNum 的关键词插入索引表 */
Status InsIdxList(int bookNum, const WordListType *wordList, IdxListType *idxList);

/* 将生成的索引表 idxList 输出到文件 file */
Status SaveToFile(const char *fileName, const IdxListType *idxList);

#endif // !BOOKNAMESORT_H

7) bookNameSort.c

/* 关键字索引表实现源文件 */

#include "bookNameSort.h"
#include 
#include 
#include 
#include 

/* 初始化操作,置索引表 idxlist 为空表,且在 idxliat.item[0] 设一空串 */
Status InitIdxList(IdxListType *idxList)
{
	CHECK_VALUE(!idxList, ERR_NULL_PTR);
	idxList->last = 0;

	return RET_OK;
}

Status ExtractKeyWord(const char *buff, const NormalIdxType *normalIdx, int *bookNum, WordListType *wordList)
{
	CHECK_VALUE(!buff || !normalIdx || !bookNum || !wordList, ERR_NULL_PTR);
	CHECK_VALUE(((int)strlen(buff) > MAX_LINE_LEN) || (buff[0] < '0') || (buff[0] > '9'), ERR_PARA);
	for (int i = 0; i < wordList->last; ++i) {
		free(wordList->item[i]);
		wordList->item[i] = NULL;
	}

	wordList->last = 0;
	*bookNum = (buff[0] - '0') * 100 + (buff[1] - '0') * 10 + (buff[2] - '0');
	const char *s1, *s2 = buff + 3;
	Bollean getEnd = FALSE;
	int length = 0;
	int i = 0;
	do {
		s1 = s2 + 1;
		s2 = strchr(s1, ' ');
		if (s2 == NULL) {
			s2 = strchr(s1, '\12');
			getEnd = TRUE;
		}

		length = (int)(s2 - s1);
		CHECK_VALUE(length > MAX_KEY_WORD_LEN, ERR_PARA);
		if (isupper(s1[0])) {
			wordList->item[wordList->last] = (char *)malloc(sizeof(char) * (unsigned long long)(length + 1));
			CHECK_VALUE(!(wordList->item[wordList->last]), ERR_MEMORY_ALLOCATE);
			for (i = 0; i < length; ++i) {
				wordList->item[wordList->last][i] = s1[i];
			}

			wordList->item[wordList->last][length] = '\0';
			for (i = 0; i < normalIdx->last; ++i) {
				if (strcmp(wordList->item[wordList->last], normalIdx->item[i]) == 0) {
					break;
				}
			}

			if (i != normalIdx->last) {
				free(wordList->item[wordList->last]);
				wordList->item[wordList->last] = NULL;
				continue;
			}

			++(wordList->last);
		}
	} while (!getEnd);

	return RET_OK;
}

/* 用 keyWord 返回词表 wordList 中第 i 个关键词 */
Status GetKeyWord(int i, const WordListType *wordList, HString *keyWord)
{
	CHECK_VALUE(!wordList || !keyWord, ERR_NULL_PTR);
	CHECK_VALUE((i < 1) || (i > wordList->last + 1), ERR_PARA);
	Status ret = StrAssign(wordList->item[i - 1], keyWord);
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

/* 在索引表 idxList 中查询是否存在与 keyWord 相等的关键词。若存在,则用 *pos 返回其
   在索引表中的位置, 且 *exist 取值 TRUE; 否则用 *pos 返回插入位置,且 *exist 取值 FALSE */
Status LocateIdx(const IdxListType *idxList, const HString *keyWord, Bollean *exist, int *pos)
{
	CHECK_VALUE(!idxList || !keyWord || !exist || !pos, ERR_NULL_PTR);
	Status ret, retVal;
	for (int i = 0; i < idxList->last; ++i) {
		retVal = StrCompare(keyWord, &(idxList->item[i].key), &ret);
		CHECK_VALUE(retVal != RET_OK, retVal);
		if (ret == 0) {
			*exist = TRUE;
			*pos = i + 1;
			return RET_OK;
		}

		if (ret < 0) {
			*exist = FALSE;
			*pos = i + 1;
			return RET_OK;
		}
	}

	*exist = FALSE;
	*pos = idxList->last + 1;

	return RET_OK;
}

/* 在索引表 idxList 的第 i 项前插入新关键词 keyWord,并初始化书号索引的链表为空表 */
Status InsertNewKeyWord(int i, const HString *keyWord, IdxListType *idxList)
{
	CHECK_VALUE(!keyWord || !idxList, ERR_NULL_PTR);
	CHECK_VALUE((i < 1) || (i > idxList->last + 1) || (idxList->last == MAX_KEY_NUM), ERR_PARA);
	errno_t errRet;
	for (int j = idxList->last; j >= i; --j) {
		errRet = memcpy_s(&(idxList->item[j]), sizeof(IdxTermType), &(idxList->item[j - 1]), sizeof(IdxTermType));
		CHECK_VALUE(errRet != 0, errRet);
	}
	
	Status ret = InitString(&(idxList->item[i - 1].key));
	CHECK_VALUE(ret != RET_OK, ret);
	ret = StrCopy(keyWord, &(idxList->item[i - 1].key));
	CHECK_VALUE(ret != RET_OK, ret);
	ret = InitList(&(idxList->item[i - 1].bookNumList));
	CHECK_VALUE(ret != RET_OK, ret);
	++(idxList->last);

	return RET_OK;
}

/* 在索引表 idxList 的第 i 项前插入书号为 bookNum 的索引 */
Status InsertBookNum(int i, int bookNum, IdxListType *idxList)
{
	CHECK_VALUE(!idxList, ERR_NULL_PTR);
	CHECK_VALUE((i < 1) || (i > idxList->last + 1), ERR_PARA);
	Link newLNode = MakeNewLNode(bookNum);
	CHECK_VALUE(!newLNode, ERR_MEMORY_ALLOCATE);
	Status ret = Append(newLNode, &(idxList->item[i - 1].bookNumList));
	CHECK_VALUE(ret != RET_OK, ret);

	return RET_OK;
}

/* 将书号为 bookNum 的关键词插入索引表 */
Status InsIdxList(int bookNum, const WordListType *wordList, IdxListType *idxList)
{
	HString keyWord = { 0 };
	int pos;
	Bollean exist;
	Status ret = 0;
	for (int i = 0; i < wordList->last; ++i) {
		ret = GetKeyWord(i + 1, wordList, &keyWord);
		CHECK_VALUE(ret != RET_OK, ret);
		ret = LocateIdx(idxList, &keyWord, &exist, &pos);
		CHECK_VALUE(ret != RET_OK, ret);
		if (!exist) {
			ret = InsertNewKeyWord(pos, &keyWord, idxList);
			CHECK_VALUE(ret != RET_OK, ret);
		}

		ret = InsertBookNum(pos, bookNum, idxList);
		CHECK_VALUE(ret != RET_OK, ret);
	}

	return RET_OK;
}

/* 将生成的索引表 idxList 输出到文件 file */
Status SaveToFile(const char *fileName, const IdxListType *idxList)
{
	CHECK_VALUE(!fileName || !idxList, ERR_NULL_PTR);
	FILE *fp;
	errno_t err_ret = fopen_s(&fp, fileName, "w");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	fprintf_s(fp, "%d\n", idxList->last);
	for (int i = 0; i < idxList->last; ++i) {
		idxList->item[i].key.ch[0] = tolower(idxList->item[i].key.ch[0]);
		for (int j = 0; j < idxList->item[i].key.length; ++j) {
			fprintf_s(fp, "%c", idxList->item[i].key.ch[j]);
		}

		fprintf_s(fp, "\n%d\n", idxList->item[i].bookNumList.length);
		Link p = idxList->item[i].bookNumList.head;
		for (int j = 0; j < idxList->item[i].bookNumList.length; ++j) {
			p = p->next;
			fprintf_s(fp, "%d ", p->data);
		}

		fprintf_s(fp, "\n");
	}

	fclose(fp);

	return RET_OK;
}

8) main.c

#include "bookNameSort.h"
#include 
#include 
#include 
#include 

int main(void)
{
	/* Step 1 */
	FILE *fp;
	errno_t err_ret = fopen_s(&fp, "normalWord.txt", "r");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	NormalIdxType normalIdx = { 0 };
	fscanf_s(fp, "%d", &normalIdx.last);
	char buff[MAX_KEY_WORD_LEN + 1];
	unsigned int length;
	for (int i = 0; i < normalIdx.last; ++i) {
		fscanf_s(fp, "%s", buff, MAX_KEY_WORD_LEN + 1);
		length = (int)strlen(buff) + 1;
		normalIdx.item[i] = (char *)malloc(sizeof(char) * length);
		strcpy_s(normalIdx.item[i], sizeof(char) * length, buff);
	}

	fclose(fp);
	err_ret = fopen_s(&fp, "bookInfo.txt", "r");
	CHECK_VALUE(err_ret != RET_OK, ERR_OPEN_FILE);
	WordListType wordList;
	int bookNum;
	Status ret;
	IdxListType idxList;
	InitIdxList(&idxList);
	char bookInfoBuff[MAX_LINE_LEN];
	BookListType bookList = { 0 };
	int bookCount = 0;
	while (fgets(bookInfoBuff, MAX_LINE_LEN, fp) != NULL) {
		ret = ExtractKeyWord(bookInfoBuff, &normalIdx, &bookNum, &wordList);
		CHECK_VALUE(ret != RET_OK, ret);
		ret = InsIdxList(bookNum, &wordList, &idxList);
		CHECK_VALUE(ret != RET_OK, ret);
		bookInfoBuff[(int)(strlen(bookInfoBuff)) - 1] = '\0';
		bookList.item[bookCount].bookNum = (bookInfoBuff[0] - '0') * 100 + (bookInfoBuff[1] - '0') * 10 +
			(bookInfoBuff[2] - '0');
		err_ret = strcpy_s(bookList.item[bookCount].bookName, sizeof(bookList.item[bookCount].bookName),
			bookInfoBuff);
		CHECK_VALUE(err_ret != RET_OK, err_ret);
		++bookCount;
	}

	bookList.last = bookCount;
	fclose(fp);
	ret = SaveToFile("idxList.txt", &idxList);
	CHECK_VALUE(ret != RET_OK, ret);

	/* Step 2 */
	printf("Please input one key word: ");
	scanf_s("%s", buff, MAX_KEY_WORD_LEN + 1);
	int i = 0;
	while (buff[i]) {
		buff[i] = tolower(buff[i]);
		++i;
	}

	HString hStr;
	ret = InitString(&hStr);
	CHECK_VALUE(ret != RET_OK, ret);
	ret = StrAssign(buff, &hStr);
	CHECK_VALUE(ret != RET_OK, ret);
	int retVal;
	i = 0;
	do {
		ret = StrCompare(&hStr, &(idxList.item[i].key), &retVal);
		CHECK_VALUE(ret != RET_OK, ret);
		++i;
	} while ((retVal != 0) && (i < idxList.last));

	if (retVal) {
		printf("Not Found!\n");
	} else {
		Link p = idxList.item[i - 1].bookNumList.head->next;
		while (p) {
			int j;
			for (j = 0; (j < bookList.last) && (p->data != bookList.item[j].bookNum); ++j);
			if (j < bookList.last) {
				printf("%s\n", bookList.item[j].bookName);
			}

			p = p->next;
		}
	}

	/* Step 3 */
	for (int i = 0; i < normalIdx.last; ++i) {
		free(normalIdx.item[i]);
	}

	for (int i = 0; i < idxList.last; ++i) {
		ret = DestroyList(&(idxList.item[i].bookNumList));
		CHECK_VALUE(ret != RET_OK, ret);
		ret = ClearString(&(idxList.item[i].key));
		CHECK_VALUE(ret != RET_OK, ret);
	}

	return 0;
}

3. 运行示例

生成文件 idxList.txt 

9
algorithms
1
34 
analysis
3
34 50 67 
computer
2
5 34 
data
3
5 10 23 
design
1
34 
fundamentals
1
23 
introduction
2
10 50 
numerical
2
50 67 
structures
3
5 10 23 

你可能感兴趣的:(#,数据结构(C语言版),数据结构,算法,c语言,开发语言)