mirror of
https://github.com/kangjianwei/Data-Structure.git
synced 2026-02-06 16:31:59 +08:00
749 lines
16 KiB
C
749 lines
16 KiB
C
/*=============================
|
||
* 串的块链分配存储表示(块链串)
|
||
==============================*/
|
||
|
||
#include "LString.h" //**▲04 串**//
|
||
|
||
/*
|
||
* 初始化
|
||
*
|
||
* 构造一个值为chars的串T。
|
||
*
|
||
*【注】
|
||
* 该操作属于最小操作子集
|
||
*/
|
||
Status StrAssign(LString* T, const char* chars) {
|
||
int len; // chars的长度
|
||
int i, count; // 遍历块链串和字符串chars的游标
|
||
int m; // 总块数
|
||
int n; // 如果存在未填满的块,存储该块中的有效元素数量
|
||
Chunk* r;
|
||
|
||
// 求chars的长度
|
||
len = (int) strlen(chars);
|
||
|
||
// 没有有效元素
|
||
if(len == 0) {
|
||
(*T).head = NULL;
|
||
(*T).tail = NULL;
|
||
(*T).curlen = 0;
|
||
|
||
return OK;
|
||
}
|
||
|
||
m = len / CHUNKSIZE; // 先计算需要填满的块
|
||
n = len % CHUNKSIZE; // 如果存在未填满的块,计算该块中填充的有效元素数量
|
||
if(n != 0) {
|
||
m++; // 计算总块数
|
||
}
|
||
|
||
// 1.创建块链串的结构
|
||
for(i = 1; i <= m; i++) {
|
||
// 创建新块
|
||
r = (Chunk*) malloc(sizeof(Chunk));
|
||
if(r == NULL) {
|
||
exit(OVERFLOW);
|
||
}
|
||
r->next = NULL;
|
||
|
||
// 第一个块
|
||
if(i == 1) {
|
||
(*T).head = (*T).tail = r;
|
||
|
||
// 联接后面的块
|
||
} else {
|
||
(*T).tail->next = r;
|
||
(*T).tail = r;
|
||
}
|
||
}
|
||
|
||
r = (*T).head;
|
||
i = 0; // 块链串T的游标
|
||
count = 0; // 统计遍历过的元素,顺便作为chars的游标
|
||
|
||
// 2.为块链串填充内容
|
||
while(count < len) {
|
||
r->ch[i] = chars[count];
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
if(i == 0) {
|
||
r = r->next; // 指向下一个块
|
||
}
|
||
|
||
count++;
|
||
}
|
||
|
||
// 3.如果存在未填满的块
|
||
if(n != 0) {
|
||
// 填充非串值内容(补上'\0')
|
||
while(i < CHUNKSIZE) {
|
||
(*T).tail->ch[i] = '\0';
|
||
i++;
|
||
}
|
||
}
|
||
|
||
// 4.记录长度信息
|
||
(*T).curlen = len;
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 销毁
|
||
*
|
||
*【注】
|
||
* 块链串的结构可以销毁,但不是在销毁操作中
|
||
*/
|
||
Status DestroyString(LString* S) {
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 清空
|
||
*
|
||
* 将串S清空。
|
||
*/
|
||
Status ClearString(LString* S) {
|
||
Chunk* p, * q;
|
||
|
||
p = (*S).head;
|
||
|
||
// 释放所有串占用的空间
|
||
while(p != NULL) {
|
||
q = p->next;
|
||
free(p);
|
||
p = q;
|
||
}
|
||
|
||
(*S).head = NULL;
|
||
(*S).tail = NULL;
|
||
(*S).curlen = 0;
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 判空
|
||
*
|
||
* 判断串S中是否包含有效数据。
|
||
*
|
||
* 返回值:
|
||
* TRUE : 串S为空
|
||
* FALSE: 串S不为空
|
||
*/
|
||
Status StrEmpty(LString S) {
|
||
return S.curlen == 0 ? TRUE : FALSE;
|
||
}
|
||
|
||
/*
|
||
* 计数
|
||
*
|
||
* 返回串S中元素的个数。
|
||
*
|
||
*【注】
|
||
* 该操作属于最小操作子集
|
||
*/
|
||
int StrLength(LString S) {
|
||
return S.curlen;
|
||
}
|
||
|
||
/*
|
||
* 求子串
|
||
*
|
||
* 用Sub返回S[pos, pos+len-1]。
|
||
* 返回值指示是否截取成功。
|
||
*
|
||
*【注】
|
||
* 该操作属于最小操作子集
|
||
*/
|
||
Status SubString(LString* Sub, LString S, int pos, int len) {
|
||
int m; // Sub的总块数
|
||
int n; // 如果Sub存在未填满的块,存储该块中的有效元素数量
|
||
int i, j, count;
|
||
Chunk* r, * p;
|
||
|
||
if(pos < 1 || pos > S.curlen || len < 0 || pos + len - 1 > S.curlen) {
|
||
return ERROR;
|
||
}
|
||
|
||
// 如果是截取0个字符,不需要分配空间
|
||
if(len == 0) {
|
||
(*Sub).head = NULL;
|
||
(*Sub).tail = NULL;
|
||
(*Sub).curlen = 0;
|
||
|
||
return OK;
|
||
}
|
||
|
||
m = len / CHUNKSIZE; // 先计算需要填满的块
|
||
n = len % CHUNKSIZE; // 如果Sub存在未填满的块,计算该块中填充的有效元素数量
|
||
if(n != 0) {
|
||
m++; // 计算Sub的总块数
|
||
}
|
||
|
||
// 1.创建Sub的结构
|
||
for(i = 1; i <= m; i++) {
|
||
// 创建新块
|
||
r = (Chunk*) malloc(sizeof(Chunk));
|
||
if(r == NULL) {
|
||
exit(OVERFLOW);
|
||
}
|
||
r->next = NULL;
|
||
|
||
// 第一个块
|
||
if(i == 1) {
|
||
(*Sub).head = (*Sub).tail = r;
|
||
|
||
// 联接后面的块
|
||
} else {
|
||
(*Sub).tail->next = r;
|
||
(*Sub).tail = r;
|
||
}
|
||
}
|
||
|
||
// 查找S中第pos个元素所在的块,并用指针p指向它
|
||
for(count = 1, p = S.head; pos > count * CHUNKSIZE; count++, p = p->next) {
|
||
// 空循环
|
||
}
|
||
|
||
r = (*Sub).head; // 指向Sub的块
|
||
|
||
i = 0; // 块链串Sub的游标
|
||
j = (pos - 1) % CHUNKSIZE; // 块链串S 的游标
|
||
count = 0; // 统计遍历过的元素
|
||
|
||
// 2.为块链串Sub填充内容
|
||
while(count < len) {
|
||
r->ch[i] = p->ch[j];
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
if(i == 0) {
|
||
r = r->next; // 指向Sub中下一个块
|
||
}
|
||
|
||
j = (j + 1) % CHUNKSIZE;
|
||
if(j == 0) {
|
||
p = p->next; // 指向S中下一个块
|
||
}
|
||
|
||
count++;
|
||
}
|
||
|
||
// 3.如果存在未填满的块
|
||
if(n != 0) {
|
||
// 填充非串值内容(补上'\0')
|
||
while(i < CHUNKSIZE) {
|
||
(*Sub).tail->ch[i] = '\0';
|
||
i++;
|
||
}
|
||
}
|
||
|
||
// 4.记录长度信息
|
||
(*Sub).curlen = len;
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 查找
|
||
*
|
||
* 从pos处开始搜索模式串T在主串S中首次出现的位置,如果不存在,则返回0。
|
||
* 如果查找成功,返回匹配的位置。
|
||
*
|
||
*【注】
|
||
* 1.此实现需要依赖串的最小操作子集
|
||
* 2.该实现比较低效
|
||
*/
|
||
int Index(LString S, LString T, int pos) {
|
||
int i, s, t;
|
||
LString sub;
|
||
|
||
if(pos < 1 || pos > S.curlen || StrEmpty(T)) {
|
||
return 0;
|
||
}
|
||
|
||
s = S.curlen; //主串S长度
|
||
t = T.curlen; //模式串T长度
|
||
i = pos;
|
||
|
||
while(i + t - 1 <= s) {
|
||
// 获取S[i, i+m-1]
|
||
SubString(&sub, S, i, t);
|
||
|
||
// 如果子串与模式串不匹配,则需要继续推进
|
||
if(StrCompare(sub, T) != 0) {
|
||
++i;
|
||
} else {
|
||
return i;
|
||
}
|
||
}
|
||
|
||
return 0; //找不到匹配的则返回0
|
||
}
|
||
|
||
/*
|
||
* 插入
|
||
*
|
||
* 将串T插入到主串S的pos位置处。
|
||
*/
|
||
Status StrInsert(LString* S, int pos, LString T) {
|
||
Chunk* pre, * p; // pre指向目标块的前驱,p指向目标块
|
||
Chunk* h, * t; // 指向新增的块链的头部和尾部
|
||
Chunk* r;
|
||
Chunk* s;
|
||
int i, j, count;
|
||
|
||
if(pos < 1 || pos > (*S).curlen + 1) {
|
||
return ERROR;
|
||
}
|
||
|
||
// 如果待插入的串为空,则提前返回
|
||
if(StrEmpty(T)) {
|
||
return OK;
|
||
}
|
||
|
||
// 记录待插入块的起始块和终止块
|
||
h = t = NULL;
|
||
|
||
// 复制T中的块(只复制结构)
|
||
for(r = T.head; r != NULL; r = r->next) {
|
||
s = (Chunk*) malloc(sizeof(Chunk));
|
||
if(s == NULL) {
|
||
exit(OVERFLOW);
|
||
}
|
||
s->next = NULL;
|
||
|
||
if(r == T.head) {
|
||
h = t = s;
|
||
} else {
|
||
t->next = s;
|
||
t = s;
|
||
}
|
||
}
|
||
|
||
// 查找S中第pos个元素所在的块(注:称为目标块),并用指针pre指向它的前驱
|
||
if(pos >= 1 && pos <= CHUNKSIZE) {
|
||
pre = NULL; // 说明第pos个元素所在块为head
|
||
p = (*S).head;
|
||
} else {
|
||
for(count = 1, pre = (*S).head; count < (pos - 1) / CHUNKSIZE; count++, pre = pre->next) {
|
||
// 空循环
|
||
}
|
||
p = pre->next;
|
||
}
|
||
|
||
/*
|
||
* 接下来,将h到t范围的块插入到pos所在的块之前
|
||
*/
|
||
|
||
if(pre == NULL) {
|
||
t->next = (*S).head;
|
||
(*S).head = h;
|
||
} else {
|
||
pre->next = h;
|
||
t->next = p;
|
||
}
|
||
|
||
if(pre == (*S).tail) {
|
||
(*S).tail = t;
|
||
}
|
||
|
||
/*
|
||
* 移动/复制元素
|
||
*/
|
||
|
||
j = 0;
|
||
|
||
// 如果插入到了某个块的“中间”
|
||
if((pos - 1) % CHUNKSIZE != 0) {
|
||
// 移动目标块中pos位置之前的元素
|
||
for(i = 1; i <= (pos - 1) % CHUNKSIZE; i++) {
|
||
h->ch[j++] = p->ch[i - 1];
|
||
p->ch[i - 1] = '\0';
|
||
}
|
||
}
|
||
|
||
r = T.head;
|
||
i = 0;
|
||
|
||
// 复制T中的元素到S中
|
||
for(count = 1; count <= T.curlen; count++) {
|
||
h->ch[j] = r->ch[i];
|
||
|
||
j = (j + 1) % CHUNKSIZE;
|
||
i = (i + 1) % CHUNKSIZE;
|
||
|
||
if(j == 0) {
|
||
h = h->next;
|
||
}
|
||
|
||
if(i == 0) {
|
||
r = r->next;
|
||
}
|
||
}
|
||
|
||
// 如果T中最后一个块中包含'\0'
|
||
if(T.curlen % CHUNKSIZE != 0) {
|
||
r = p; // 指向目标块
|
||
i = (pos - 1) % CHUNKSIZE;
|
||
|
||
// 移动目标块中pos位置及其之后的元素
|
||
for(count = pos; count <= (*S).curlen; count++) {
|
||
h->ch[j] = r->ch[i];
|
||
r->ch[i] = '\0';
|
||
|
||
j = (j + 1) % CHUNKSIZE;
|
||
i = (i + 1) % CHUNKSIZE;
|
||
|
||
if(j == 0) {
|
||
h = h->next;
|
||
}
|
||
|
||
if(i == 0) {
|
||
r = r->next;
|
||
}
|
||
}
|
||
}
|
||
|
||
(*S).curlen += T.curlen;
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 删除
|
||
*
|
||
* 删除S[pos, pos+len-1]。
|
||
*/
|
||
Status StrDelete(LString* S, int pos, int len) {
|
||
Chunk* pre; // 指向元素S[pos]所在的块的前驱
|
||
Chunk* p; // 指向元素S[pos]所在的块
|
||
Chunk* q; // 指向元素S[pos+len]所在的块
|
||
Chunk* r;
|
||
int i, j, count, total;
|
||
|
||
if(pos < 1 || pos > (*S).curlen || len < 0 || pos + len - 1 > (*S).curlen) {
|
||
return ERROR;
|
||
}
|
||
|
||
// 如果待删除的长度为0,则提前返回
|
||
if(len == 0) {
|
||
return OK;
|
||
}
|
||
|
||
// 查找S[pos]所在的块,并用指针pre指向它的前驱
|
||
if(pos >= 1 && pos <= CHUNKSIZE) {
|
||
pre = NULL; // 说明第pos个元素所在块为head
|
||
p = (*S).head;
|
||
} else {
|
||
for(count = 1, pre = (*S).head; count < (pos - 1) / CHUNKSIZE; count++, pre = pre->next) {
|
||
// 空循环
|
||
}
|
||
p = pre->next;
|
||
}
|
||
|
||
// 查找S[pos+len]所在的块
|
||
for(count = (pos - 1) / CHUNKSIZE, q = p; count < (pos + len - 1) / CHUNKSIZE; count++, q = q->next) {
|
||
// 空循环
|
||
}
|
||
|
||
// 计算可能需要移动的元素个数
|
||
total = (*S).curlen - (pos + len) + 1;
|
||
|
||
// 把最终的长度先定下来
|
||
(*S).curlen -= len;
|
||
|
||
i = (pos - 1) % CHUNKSIZE;
|
||
j = (pos + len - 1) % CHUNKSIZE;
|
||
|
||
if(p != q) {
|
||
// 删除p与q之间的块
|
||
r = p->next;
|
||
while(r != q) {
|
||
p->next = r->next;
|
||
free(r);
|
||
r = p->next;
|
||
}
|
||
|
||
if(q == NULL) {
|
||
(*S).tail = p;
|
||
}
|
||
|
||
// 如果需要删除p指向的整个块
|
||
if((pos - 1) % CHUNKSIZE == 0) {
|
||
// p已经指向块链的头部
|
||
if(pre == NULL) {
|
||
(*S).head = q;
|
||
} else {
|
||
pre->next = q;
|
||
}
|
||
|
||
free(p);
|
||
p = q;
|
||
|
||
if(q == NULL) {
|
||
(*S).tail = pre;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 已经就位
|
||
if(p == q && i == j) {
|
||
return OK;
|
||
}
|
||
|
||
for(count = 1; count <= total; count++) {
|
||
// 移动元素
|
||
p->ch[i] = q->ch[j];
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
j = (j + 1) % CHUNKSIZE;
|
||
|
||
if(i == 0) {
|
||
pre = p;
|
||
p = p->next;
|
||
}
|
||
|
||
if(j == 0) {
|
||
q = q->next;
|
||
}
|
||
}
|
||
|
||
// 恰好填满了最后的块
|
||
if(i == 0) {
|
||
(*S).tail = pre;
|
||
r = p;
|
||
} else {
|
||
(*S).tail = p;
|
||
|
||
// 剩余部分填充非串值字符
|
||
while(i < CHUNKSIZE) {
|
||
p->ch[i++] = '\0';
|
||
}
|
||
|
||
r = p->next;
|
||
}
|
||
|
||
// 释放多余的空间
|
||
while(r != NULL) {
|
||
(*S).tail->next = r->next;
|
||
free(r);
|
||
r = (*S).tail->next;
|
||
}
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 比较
|
||
*
|
||
* 比较串S和串T,返回比较结果。
|
||
*
|
||
*【注】
|
||
* 该操作属于最小操作子集
|
||
*/
|
||
int StrCompare(LString S, LString T) {
|
||
int i;
|
||
Chunk* s = S.head;
|
||
Chunk* t = T.head;
|
||
|
||
while(s != NULL && t != NULL) {
|
||
for(i = 0; i < CHUNKSIZE; i++) {
|
||
if(s->ch[i] != t->ch[i]) {
|
||
return s->ch[i] - t->ch[i];
|
||
}
|
||
}
|
||
|
||
s = s->next;
|
||
t = t->next;
|
||
}
|
||
|
||
if(s != NULL) {
|
||
return 1;
|
||
} else if(t != NULL) {
|
||
return -1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 复制
|
||
*
|
||
* 将串S复制到串T。
|
||
*/
|
||
Status StrCopy(LString* T, LString S) {
|
||
int i;
|
||
Chunk* s, * p;
|
||
|
||
for(p = S.head; p; p = p->next) {
|
||
s = (Chunk*) malloc(sizeof(Chunk));
|
||
if(s == NULL) {
|
||
exit(OVERFLOW);
|
||
}
|
||
s->next = NULL;
|
||
|
||
if(p == S.head) {
|
||
(*T).head = (*T).tail = s;
|
||
} else {
|
||
(*T).tail->next = s;
|
||
(*T).tail = s;
|
||
}
|
||
|
||
for(i = 0; i < CHUNKSIZE; i++) {
|
||
(*s).ch[i] = (*p).ch[i];
|
||
}
|
||
}
|
||
|
||
(*T).curlen = S.curlen;
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 替换
|
||
*
|
||
* 用V替换主串S中出现的所有与T相等的且不重叠的子串。
|
||
*
|
||
*【注】
|
||
* 1.该操作依赖最小操作子集
|
||
* 2.该实现比较低效
|
||
*/
|
||
Status Replace(LString* S, LString T, LString V) {
|
||
int i;
|
||
|
||
if(StrEmpty(*S) || StrEmpty(T)) {
|
||
return ERROR;
|
||
}
|
||
|
||
// 在主串S中寻找模式串T第一次出现的位置
|
||
i = Index(*S, T, 1);
|
||
|
||
// 如果存在匹配的字符串
|
||
while(i != 0) {
|
||
StrDelete(S, i, StrLength(T)); // 从S中删除T
|
||
StrInsert(S, i, V); // 向S中插入V
|
||
|
||
i += StrLength(V); // i切换到下一个位置
|
||
|
||
i = Index(*S, T, i); // 查找下一个匹配的字符串
|
||
}
|
||
|
||
return OK;
|
||
}
|
||
|
||
/*
|
||
* 串联接
|
||
*
|
||
* 联接S1和S2,并存储到T中返回。如果联接后的长度溢出,则只保留未溢出的部分。
|
||
* 返回值表示联接后的串是否完整。
|
||
*
|
||
*【注】
|
||
* 该操作属于最小操作子集
|
||
*/
|
||
Status Concat(LString* T, LString S1, LString S2) {
|
||
Chunk* p; // 用来遍历S1和S2
|
||
Chunk* r; // 遍历T
|
||
Chunk* s;
|
||
int i, j, count;
|
||
|
||
// 计算长度信息
|
||
(*T).curlen = S1.curlen + S2.curlen;
|
||
|
||
// 计算需要创建多少个块
|
||
count = (*T).curlen / CHUNKSIZE + ((*T).curlen % CHUNKSIZE == 0 ? 0 : 1);
|
||
|
||
// 复制S1的结构
|
||
for(i = 1; i <= count; i++) {
|
||
s = (Chunk*) malloc(sizeof(Chunk));
|
||
if(s == NULL) {
|
||
exit(OVERFLOW);
|
||
}
|
||
s->next = NULL;
|
||
|
||
if(i == 1) {
|
||
(*T).head = (*T).tail = s;
|
||
} else {
|
||
(*T).tail->next = s;
|
||
(*T).tail = s;
|
||
}
|
||
}
|
||
|
||
i = 0;
|
||
r = (*T).head;
|
||
|
||
// 复制S1中的数据
|
||
j = 0;
|
||
p = S1.head;
|
||
for(count = 0; count < S1.curlen; count++) {
|
||
r->ch[i] = p->ch[j];
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
j = (j + 1) % CHUNKSIZE;
|
||
|
||
if(i == 0) {
|
||
r = r->next;
|
||
}
|
||
|
||
if(j == 0) {
|
||
p = p->next;
|
||
}
|
||
}
|
||
|
||
// 复制S2中的数据
|
||
j = 0;
|
||
p = S2.head;
|
||
for(count = 0; count < S2.curlen; count++) {
|
||
r->ch[i] = p->ch[j];
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
j = (j + 1) % CHUNKSIZE;
|
||
|
||
if(i == 0) {
|
||
r = r->next;
|
||
}
|
||
|
||
if(j == 0) {
|
||
p = p->next;
|
||
}
|
||
}
|
||
|
||
// 多余的空间填充非串值字符
|
||
if(i != 0) {
|
||
while(i < CHUNKSIZE) {
|
||
r->ch[i] = '\0';
|
||
i++;
|
||
}
|
||
}
|
||
|
||
return OK;
|
||
}
|
||
|
||
// 测试函数,打印字符串
|
||
void StrPrint(LString S) {
|
||
int i = 0;
|
||
Chunk* p = S.head;
|
||
|
||
if(S.curlen == 0 || S.head == NULL || S.tail == NULL) {
|
||
printf("\n");
|
||
return;
|
||
}
|
||
|
||
while(p != NULL) {
|
||
// 遇到非串值符号时结束遍历
|
||
if(p->ch[i] == '\0') {
|
||
break;
|
||
}
|
||
|
||
printf("%c", p->ch[i]);
|
||
|
||
i = (i + 1) % CHUNKSIZE;
|
||
|
||
if(i == 0) {
|
||
p = p->next;
|
||
}
|
||
}
|
||
|
||
printf("\n");
|
||
}
|