目录
前言
这篇博客主要串的块链存储表示。
1.串的块链存储表示
串的块链存储表表示指的是采用链表方式存储传值。链表中的每一个结点既可以表示一个字符,也可以存放多个串值。串的块链存储表除了头指针外还可以设置一个尾指针指示链表中的最后一个结点,并给出串的长度。使用链表定义的串存储结构称为块链结构。
串的块链存储表示定义如下:
// = = = = = 串的块链存储表示 = = = = =
#define CHUNKSIZE 80 // 可由用户定义的块大小
// 块链表节点
typedef struct Chunk {
char ch[CHUNKSIZE]; // 数据域,用于存储字符串的一部分
struct Chunk* next; // 指针域,指向下一个块
} Chunk, *LinkList;
// 串结构
typedef struct {
Chunk* head; // 串的头指针,指向串的第一个块
Chunk* tail; // 串的尾指针,指向串的最后一个块
int curlen; // 串的当前长度,即串中字符的数量
} LString;
2.串的块链存储表表示和实现
1. strAssign
// 将C风格字符串赋值给LString串
void strAssign(LString* S, const char* str) {
if (!S || !str) return; // 检查传入指针是否为空
int len = (unsigned int)strlen(str); // 获取字符串长度
S->curlen = len; // 更新串的长度
S->head = S->tail = NULL; // 初始化头尾指针为空
// 分配第一个块
Chunk* p = (Chunk*)malloc(sizeof(Chunk)); // 分配块内存
if (!p) { // 检查内存分配是否成功
fprintf(stderr, "Memory allocation failed\n"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
S->head = S->tail = p; // 将头尾指针指向第一个块
// 处理字符串内容
int i, j;
for (i = 0; i < len; i += CHUNKSIZE) { // 每次处理一个块的数据
for (j = 0; j < CHUNKSIZE && str[i + j] != '\0'; j++) { // 将字符串内容复制到块中
p->ch[j] = str[i + j];
}
p->next = (i + CHUNKSIZE < len) ? (Chunk*)malloc(sizeof(Chunk)) : NULL; // 分配下一个块
if (!p->next && i + CHUNKSIZE < len) { // 检查内存分配是否成功
fprintf(stderr, "Memory allocation failed\n"); // 打印错误消息
exit(EXIT_FAILURE); // 退出程序
}
p = p->next; // 移动到下一个块
}
}
2. strCopy
// 复制一个LString串到另一个LString串
void strCopy(LString* dest, const LString* src) {
if (!dest || !src) return; // 空指针检查
dest->curlen = src->curlen; // 复制长度
Chunk* srcPtr = src->head; // 源串头指针
Chunk* destPtr = NULL; // 目标串当前指针
// 遍历源串的每个块
while (srcPtr) {
// 分配新块
Chunk* newChunk = (Chunk*)malloc(sizeof(Chunk));
if (!newChunk) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
// 复制数据
memcpy(newChunk->ch, srcPtr->ch, CHUNKSIZE);
newChunk->next = NULL;
// 如果目标串为空,设置头指针
if (!dest->head) {
dest->head = dest->tail = newChunk;
} else {
dest->tail->next = newChunk; // 否则连接到尾部
dest->tail = newChunk; // 更新尾指针
}
srcPtr = srcPtr->next; // 移动到源串的下一个块
}
}
3. strEmpty
// 检查LString串是否为空
int strEmpty(const LString* S) {
if (!S) return -1; // 空指针检查
return (S->curlen == 0) ? 1 : 0; // 返回是否为空串的标志
}
4. strCompare
// 比较两个LString串的大小
int strCompare(const LString* S1, const LString* S2) {
if (!S1 || !S2) return -1; // 空指针检查
Chunk* p1 = S1->head; // 指向S1的第一个块
Chunk* p2 = S2->head; // 指向S2的第一个块
while (p1 && p2) {
for (int i = 0; i < CHUNKSIZE; i++) {
if (p1->ch[i] < p2->ch[i]) return -1; // S1小于S2
else if (p1->ch[i] > p2->ch[i]) return 1; // S1大于S2
else if (p1->ch[i] == '\0' && p2->ch[i] == '\0') return 0; // 字符串相等
}
p1 = p1->next; // 移动到S1的下一个块
p2 = p2->next; // 移动到S2的下一个块
}
// 遍历完其中一个串后,如果另一个串还有剩余,则较长的串大
if (!p1 && p2) return -1; // S1小于S2
else if (p1 && !p2) return 1; // S1大于S2
return 0; // 两个串相等
}
5. strLength
// 获取LString串的长度
int strLength(const LString* S) {
if (!S) return -1; // 空指针检查
return S->curlen; // 返回串的长度
}
6. clearString
// 清空LString串
void clearString(LString* S) {
if (!S) return; // 空指针检查
Chunk* p = S->head; // 从头指针开始清空
while (p) {
Chunk* temp = p; // 保存当前块的指针
p = p->next; // 移动到下一个块
free(temp); // 释放当前块的内存
}
S->head = S->tail = NULL; // 将头尾指针置为空
S->curlen = 0; // 将串的长度置为0
}
7. concat
// 连接两个LString串
void concat(LString* result, const LString* S1, const LString* S2) {
if (!result || !S1 || !S2) return; // 空指针检查
// 复制S1到result
strCopy(result, S1);
// 获取S1的尾部块,用于连接S2
Chunk* tail = result->tail;
// 复制S2到result尾部
strCopy(result, S2);
// 连接S1的尾部块和S2的头部块
tail->next = S2->head;
// 更新result的长度
result->curlen = S1->curlen + S2->curlen;
}
8. subString
// 获取LString串的子串
void subString(LString* sub, const LString* S, int pos, int len) {
if (!sub || !S || pos < 1 || pos > S->curlen || len < 0 || pos + len - 1 > S->curlen) return; // 参数合法性检查
clearString(sub); // 清空子串
Chunk* p = S->head; // 指向源串的头部
int startChunkIndex = (pos - 1) / CHUNKSIZE; // 起始块的索引
int startPosInChunk = (pos - 1) % CHUNKSIZE; // 起始块内的起始位置
int copiedLen = 0; // 已复制的长度
// 移动到起始块
for (int i = 0; i < startChunkIndex; i++) {
p = p->next;
}
// 复制子串内容
while (p && copiedLen < len) {
int remainingLenInChunk = CHUNKSIZE - startPosInChunk; // 当前块内剩余的长度
int copyLenInChunk = (remainingLenInChunk < len - copiedLen) ? remainingLenInChunk : len - copiedLen; // 当前块内需要复制的长度
memcpy(sub->tail->ch + copiedLen, p->ch + startPosInChunk, copyLenInChunk); // 复制内容到子串的尾部
copiedLen += copyLenInChunk; // 更新已复制的长度
startPosInChunk = 0; // 起始块内的起始位置置为0
p = p->next; // 移动到下一个块
}
// 更新子串的长度
sub->curlen = len;
}
9. index
// 在LString串中查找字符第一次出现的位置
int index(const LString* S, char ch) {
if (!S) return -1; // 空指针检查
Chunk* p = S->head; // 从头部开始查找
int pos = 1; // 记录当前位置
// 遍历串的每个字符
while (p) {
for (int i = 0; i < CHUNKSIZE; i++) {
if (p->ch[i] == ch) return pos; // 如果找到目标字符,返回位置
else if (p->ch[i] == '\0') return -1; // 如果遇到字符串结束符,表示查找结束,返回-1
pos++; // 更新当前位置
}
p = p->next; // 移动到下一个块
}
return -1; // 如果没有找到目标字符,返回-1
}
10.replace
// 将LString串中从指定位置开始的一段子串替换为另一个LString串
void replace(LString* S, int pos, int len, const LString* T) {
if (!S || !T || pos < 1 || pos > S->curlen || len < 0) return; // 参数合法性检查
// 定位起始块
Chunk* p = S->head;
int startChunkIndex = (pos - 1) / CHUNKSIZE;
for (int i = 0; i < startChunkIndex; i++) {
p = p->next;
}
// 定位起始块内的起始位置
int startPosInChunk = (pos - 1) % CHUNKSIZE;
// 定位结束块
int endPos = pos + len - 1;
Chunk* endChunk = S->head;
int endChunkIndex = (endPos - 1) / CHUNKSIZE;
for (int i = 0; i < endChunkIndex; i++) {
endChunk = endChunk->next;
}
// 定位结束块内的结束位置
int endPosInChunk = (endPos - 1) % CHUNKSIZE;
// 计算需要插入的长度
int insertLen = T->curlen;
// 计算原串中需要移动的字符数
int moveLen = S->curlen - len - (pos - 1) + 1;
// 计算新串的长度
int newLen = S->curlen + insertLen - len;
// 创建新的块链表
Chunk* newHead = NULL;
Chunk* newTail = NULL;
Chunk* newPtr = NULL;
// 复制原串中替换位置之前的部分
int copiedLen = 0;
while (copiedLen < pos - 1) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
copiedLen += CHUNKSIZE;
p = p->next;
}
// 复制插入的串
Chunk* insertPtr = T->head;
while (insertPtr) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
memcpy(newPtr->ch, insertPtr->ch, CHUNKSIZE);
insertPtr = insertPtr->next;
}
// 复制原串中替换位置之后的部分
copiedLen = 0;
while (copiedLen < moveLen) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
copiedLen += CHUNKSIZE;
p = p->next;
}
// 释放原串中被替换的部分的内存
while (S->head) {
Chunk* temp = S->head;
S->head = S->head->next;
free(temp);
}
// 更新原串的头尾指针和长度
S->head = newHead;
S->tail = newTail;
S->curlen = newLen;
}
11.strInsert
// 将LString串中从指定位置开始插入另一个LString串
void strInsert(LString* S, int pos, const LString* T) {
if (!S || !T || pos < 1 || pos > S->curlen + 1) return; // 参数合法性检查
// 如果插入位置是1,直接将T连接在S之前
if (pos == 1) {
LString temp;
concat(&temp, T, S);
strCopy(S, &temp);
return;
}
// 定位插入位置的前一个块
Chunk* prev = S->head;
int prevChunkIndex = (pos - 2) / CHUNKSIZE;
for (int i = 0; i < prevChunkIndex; i++) {
prev = prev->next;
}
// 定位插入位置的前一个块内的结束位置
int prevPosInChunk = (pos - 2) % CHUNKSIZE;
// 定位插入位置的后一个块
Chunk* next = prev->next;
// 定位插入位置的后一个块内的起始位置
int nextPosInChunk = (pos - 1) % CHUNKSIZE;
// 计算插入串的长度
int insertLen = T->curlen;
// 计算插入位置后面的字符数
int moveLen = S->curlen - (pos - 1) + 1;
// 计算新串的长度
int newLen = S->curlen + insertLen;
// 创建新的块链表
Chunk* newHead = NULL;
Chunk* newTail = NULL;
Chunk* newPtr = NULL;
// 复制插入位置之前的部分
Chunk* p = S->head;
int copiedLen = 0;
while (copiedLen < (pos - 1)) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
if (copiedLen < prevPosInChunk) {
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
} else {
memcpy(newPtr->ch, p->ch + prevPosInChunk, CHUNKSIZE - prevPosInChunk);
p = p->next;
if (p) {
memcpy(newPtr->ch + CHUNKSIZE - prevPosInChunk, p->ch, prevPosInChunk);
}
}
copiedLen += CHUNKSIZE - prevPosInChunk;
prevPosInChunk = 0;
}
// 复制插入串
Chunk* insertPtr = T->head;
while (insertPtr) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
memcpy(newPtr->ch, insertPtr->ch, CHUNKSIZE);
insertPtr = insertPtr->next;
}
// 复制插入位置之后的部分
copiedLen = 0;
while (copiedLen < moveLen) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
if (copiedLen < nextPosInChunk) {
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
} else {
memcpy(newPtr->ch, p->ch + nextPosInChunk, CHUNKSIZE - nextPosInChunk);
p = p->next;
if (p) {
memcpy(newPtr->ch + CHUNKSIZE - nextPosInChunk, p->ch, nextPosInChunk);
}
}
copiedLen += CHUNKSIZE - nextPosInChunk;
nextPosInChunk = 0;
}
// 释放原串的内存
clearString(S);
// 更新新串的头尾指针和长度
S->head = newHead;
S->tail = newTail;
S->curlen = newLen;
}
12.strDelete
// 从LString串中删除指定位置开始的指定长度的子串
void strDelete(LString* S, int pos, int len) {
if (!S || pos < 1 || pos > S->curlen || len < 0) return; // 参数合法性检查
// 定位起始块
Chunk* p = S->head;
int startChunkIndex = (pos - 1) / CHUNKSIZE;
for (int i = 0; i < startChunkIndex; i++) {
p = p->next;
}
// 定位起始块内的起始位置
int startPosInChunk = (pos - 1) % CHUNKSIZE;
// 定位结束块
int endPos = pos + len - 1;
Chunk* endChunk = S->head;
int endChunkIndex = (endPos - 1) / CHUNKSIZE;
for (int i = 0; i < endChunkIndex; i++) {
endChunk = endChunk->next;
}
// 定位结束块内的结束位置
int endPosInChunk = (endPos - 1) % CHUNKSIZE;
// 计算需要删除的字符数
int deleteLen = len;
// 计算原串中需要移动的字符数
int moveLen = S->curlen - len - (pos - 1) + 1;
// 计算新串的长度
int newLen = S->curlen - len;
// 创建新的块链表
Chunk* newHead = NULL;
Chunk* newTail = NULL;
Chunk* newPtr = NULL;
// 复制原串中删除位置之前的部分
int copiedLen = 0;
while (copiedLen < pos - 1) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
copiedLen += CHUNKSIZE;
p = p->next;
}
// 复制原串中删除位置之后的部分
copiedLen = 0;
while (copiedLen < moveLen) {
if (!newHead) {
newPtr = (Chunk*)malloc(sizeof(Chunk));
newHead = newTail = newPtr;
} else {
newPtr->next = (Chunk*)malloc(sizeof(Chunk));
newPtr = newPtr->next;
newTail = newPtr;
}
if (copiedLen < startPosInChunk) {
memcpy(newPtr->ch, p->ch, CHUNKSIZE);
} else {
memcpy(newPtr->ch, p->ch + endPosInChunk + 1, CHUNKSIZE - endPosInChunk - 1);
p = p->next;
if (p) {
memcpy(newPtr->ch + CHUNKSIZE - endPosInChunk - 1, p->ch, endPosInChunk + 1);
}
}
copiedLen += CHUNKSIZE - endPosInChunk - 1;
endPosInChunk = -1;
}
// 释放原串中被删除的部分的内存
while (S->head) {
Chunk* temp = S->head;
S->head = S->head->next;
free(temp);
}
// 更新原串的头尾指针和长度
S->head = newHead;
S->tail = newTail;
S->curlen = newLen;
}
13.destroyString
// 销毁LString串
void strDestroy(LString* S) {
if (!S) return; // 参数合法性检查
// 释放串中的每个块所占的内存
Chunk* p = S->head;
while (p) {
Chunk* temp = p;
p = p->next;
free(temp);
}
// 重置串的头尾指针和长度
S->head = NULL;
S->tail = NULL;
S->curlen = 0;
}