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

news2024/11/20 18:26:11

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 <stdio.h>
#include <stdlib.h>

/* 分配由指向的值为 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 <stdlib.h>
#include <string.h>
#include <stdio.h>

/* 初始化(产生空串)字符串 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* 初始化操作,置索引表 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1058982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32复习笔记(六):STM32远程升级BootLoader相关

目录 Preface&#xff1a; &#xff08;一&#xff09;STM32上电启动流程 &#xff08;二&#xff09;BootLoader相关 &#xff08;三&#xff09;Clion配置 Preface&#xff1a; 有关STM32的BootLoader主要还是参考了许多大佬的文章&#xff0c;这里只是简单地列举一下&am…

postgresql新特性之Merge

postgresql新特性之Merge 创建测试表测试案例 创建测试表 create table cps.public.test(id integer primary key,balance numeric,status varchar(1));测试案例 官网介绍 merge into test t using ( select 1 id,0 balance,Y status) s on(t.id s.id) -- 当匹配上了,statu…

STM32复习笔记(二):GPIO

目录 &#xff08;一&#xff09;Demo流程 &#xff08;二&#xff09;工程配置 &#xff08;三&#xff09;代码部分 &#xff08;四&#xff09;外部中断&#xff08;EXTI&#xff09; &#xff08;一&#xff09;Demo流程 首先&#xff0c;板子上有4个按键&#xff0c;…

网络技术在学校是学习网络协议吗

大家好&#xff0c;我是网络工程师成长日记实验室的郑老师&#xff0c;您现在正在查看的是网络工程师成长日记专栏&#xff0c;记录网络工程师日常生活的点点滴滴 一个同学说他现在也在学这个计算机专业&#xff0c;他以后他现在目前规划是把软考和华为的HIP1套。我自己本身也做…

AI时代给我们公司模式带来的改变

最近和几个朋友聊天&#xff0c;都说公司卷的更厉害了&#xff0c;老板趁着经济形势不好压榨的更厉害了&#xff01;但确实没办法&#xff0c;我们部分老板还是喜欢以人为本&#xff08;成本&#xff01;&#xff09;&#xff0c;更喜欢雇更多的人&#xff0c;看着他们低效而痛…

【Java】权限修饰符

目录 权限修饰符 权限修饰符-示例代码 权限修饰符 Java有四种访问权限&#xff0c;其中三种有访问权限修饰符&#xff0c;分别为 private&#xff0c;public 和 protected&#xff0c;还有一种不带任何修饰符&#xff1a; private&#xff1a;Java语言中对访问权限限制的最窄…

【C语言经典100例题-68】有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

方法一 将原数组拆成两部分&#xff0c;前面n-m个数和后面m个数。首先将前面n-m个数逆序&#xff0c;然后将后面的m个数逆序。最后将整个数组逆序即可。 #include <stdio.h>void reverse(int arr[], int start, int end) {for (int i start, j end; i < (start en…

快排三种递归及其优化,非递归和三路划分

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 快排简介&#xff1a; 快排的三种递归实现&#xff1a; Hoare&#xff1a; 挖坑&#xff1a; 双指针&#xff1a; 小区间优化&#xff1a; 三数取中优化&#xff1a; 快排非递归实现&#xff1a; 快排的三路划…

嵌入式Linux应用开发-驱动大全-第一章同步与互斥④

嵌入式Linux应用开发-驱动大全-第一章同步与互斥④ 第一章 同步与互斥④1.5 自旋锁spinlock的实现1.5.1 自旋锁的内核结构体1.5.2 spinlock在UP系统中的实现1.5.3 spinlock在SMP系统中的实现 1.6 信号量semaphore的实现1.6.1 semaphore的内核结构体1.6.2 down函数的实现1.6.3 u…

Android etc1tool之png图片转换pkm 和 zipalign简介

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、etc1tool2.1、用法 三、zipalign3.1 使用 四…

day49 ARM

.text .globl _start _start:mov r1,#1mov r2,#0mov r3,#100 fun2:cmp r2,r3bcc fun1 stop:b stop fun1: ADD r2,r2,r1add r4,r4,r2b fun2 .end

华为云云耀云服务器L实例评测|部署个人在线电子书库 calibre

华为云云耀云服务器L实例评测&#xff5c;部署个人在线电子书库 calibre 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 应用场景1.3 支持镜像 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 calibre3.1 calibre 介绍3.2 Docker 环境搭建3.3 c…

javaScript-事件循环-微任务-宏任务

为什么引入事件循环?如何理解&#xff1f; js是单线程的语言&#xff0c;需要把异步任务交给宿主浏览器执行&#xff0c;仿制js引擎堵塞 以下面的代码为例 异步的代码交给浏览器之后 进入队列中等待被调用&#xff1a; <!DOCTYPE html> <html lang"en"…

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③

嵌入式Linux应用开发-驱动大全-第一章同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占…

CharacterEncodingFilter的用法

CharacterEncoding是SpringMVC提供的一个一个过滤器,用于设置请求和响应的字符编码,解决乱码问题,他本身是一个过滤器 那么在SpringBoot中,CharacterEncoding就有一个很好的秒用 setEncoding("UTF-8")设置编码 setForceEncoding(true) 设置请求和响应编码 还需要在配…

树的存储结构以及树,二叉树,森林之间的转换

目录 1.双亲表示法 2.孩子链表 3.孩子兄弟表示法 4.树与二叉树的转换 &#xff08;1&#xff09;树转换为二叉树 &#xff08;2&#xff09;二叉树转换成树 5.二叉树与森林的转化 &#xff08;1&#xff09;森林转换为二叉树 以下树为例 1.双亲表示法 双亲表示法定义了…

javaee之通用mapper

通用mapper可以帮我们写sql语句 我们需要引入依赖是 通用mapper的核心依赖 它本身就依赖一个jpa的依赖&#xff0c;通用mapper的整体依赖就包含了通用mapper的核心依赖 下面说一下通用mapper里面的常见注解 KeySql的用法 tk.mybatis.mapper.common.Mapper 这个是通用Mapper的一…

c#设计模式-行为型模式 之 模板方法模式

&#x1f680;简介 模板方法模式定义了一个操作中的算法的骨架&#xff0c;而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下&#xff0c;重新定义算法中的某些步骤。通常用于应对在开发中设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定…

用 Pycharm 远程连接 Linux 服务器——超详细

用 Pycharm 远程连接 Linux 服务器——超详细 一、介绍二、要求三、服务器配置四、Pycharm远程连接Linux服务器 实战 一、介绍 本人是做NLP的&#xff0c;pycharm写的项目&#xff0c;数据集很大&#xff0c;在自己电脑上运行很慢&#xff0c;但是放到服务器上跑就很快。下面详…

FileZila 实现wind10与Linux系统文件互传

【FileZila】实现windows与Linux系统文件互传