链串详解
本文档将详细介绍链串的基本概念、实现原理及其在 C 语言中的具体应用。通过本指南,读者将了解如何使用链串进行各种字符串操作。
1. 什么是链串?
链串是一种用于存储字符串的数据结构,它使用一组动态分配的节点来保存字符串中的字符序列。链串通常由两部分组成:
- 一个指向链表头结点的指针
s
。 - 一系列的节点,每个节点包含一个字符
data
和指向下一个节点的指针next
。
在本程序中,我们定义了一个名为 LinkStrNode
的结构体来表示链串中的单个节点,并使用一个指针 s
来表示整个链串。
typedef struct StringNode {
char data;
struct StringNode *next;
} LinkStrNode;
2. 基本操作
2.1 生成串 (StrAssign
)
此函数用于将一个 C 语言字符串转换为链串。
void StrAssign(LinkStrNode *&s, const char str[]) {
LinkStrNode *r;
LinkStrNode *p;
s = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建头结点
r = s;
for (int i = 0; str[i] != '\0'; i++) {
p = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
p->data = str[i]; // 设置数据
r->next = p; // 将新节点链接到链表
r = p; // 移动指针到新节点
}
r->next = NULL; // 设置尾节点的 next 为 NULL
}
参数:
s
: 目标链串的头结点指针。str
: 源 C 语言字符串。
功能:
- 为
s
分配一个头结点。 - 为
str
中的每个字符创建一个新的节点,并将其添加到链表中。
2.2 销毁串 (DestroyStr
)
此函数用于释放链串所占用的内存。
void DestroyStr(LinkStrNode *&s) {
LinkStrNode *pre = s;
LinkStrNode *p = s->next;
while (p != NULL) {
free(pre); // 释放前一个节点
pre = p; // 移动 pre 到当前节点
p = pre->next; // 移动 p 到下一个节点
}
free(pre); // 释放最后一个节点
s = NULL; // 清空串指针
}
参数:
s
: 要销毁的链串的头结点指针。
功能:
- 依次释放链串中的每个节点,并清空头结点指针。
2.3 判断是否为空串 (StrEmpty
)
此函数用于检查一个链串是否为空。
bool StrEmpty(LinkStrNode *s){
return s->next == NULL;
}
参数:
s
: 要检查的链串的头结点指针。
返回值:
- 如果链串为空(即头结点的
next
指向NULL
),则返回true
;否则返回false
。
2.4 串的复制 (StrCopy
)
此函数用于将一个链串复制到另一个链串。
void StrCopy(LinkStrNode *&s, LinkStrNode *t) {
LinkStrNode *p = t->next;
LinkStrNode *q;
LinkStrNode *r;
s = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
r = s;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
}
参数:
s
: 目标链串的头结点指针。t
: 源链串的头结点指针。
功能:
- 为
s
分配一个头结点。 - 复制
t
中的所有节点到s
的链表中。
2.5 判断串相等 (StrEqual
)
此函数用于检查两个链串是否相等。
bool StrEqual(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *p = s->next;
LinkStrNode *q = t->next;
while (p != NULL && q != NULL && p->data == q->data) {
p = p->next;
q = q->next;
}
return (p == NULL && q == NULL);
}
参数:
s
: 第一个链串的头结点指针。t
: 第二个链串的头结点指针。
返回值:
- 如果两个链串具有相同的长度且所有字符都相等,则返回
true
;否则返回false
。
2.6 获得串的长度 (StrLength
)
此函数用于获取一个链串的长度。
int StrLength(LinkStrNode *s) {
int i = 0;
LinkStrNode *p = s->next;
while (p != NULL) {
i++;
p = p->next;
}
return i;
}
参数:
s
: 要获取长度的链串的头结点指针。
返回值:
- 返回链串的长度。
2.7 串的连接 (Concat
)
此函数用于将两个链串连接成一个新的链串。
LinkStrNode *Concat(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
r = str;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
p = t->next;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
参数:
s
: 第一个链串的头结点指针。t
: 第二个链串的头结点指针。
返回值:
- 返回一个新的链串,该串包含
s
和t
的所有节点。
2.8 获得子串 (SubStr
)
此函数用于从一个链串中获取指定位置的子串。
LinkStrNode *SubStr(LinkStrNode *s, int i, int j) {
int k;
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || j < 0 || i + j - 1 > StrLength(s)) {
return str;
}
for (k = 1; k < i; k++) { // 移动到起始位置
p = p->next;
}
for (k = 1; k <= j; k++) { // 复制子串
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
参数:
s
: 原始链串的头结点指针。i
: 子串的起始位置(1-indexed)。j
: 子串的长度。
返回值:
- 返回一个新的链串,该串包含
s
从位置i
开始的长度为j
的子串。
2.9 子串的插入 (InsertStr
)
此函数用于在一个链串的指定位置插入另一个链串。
LinkStrNode *InsertStr(LinkStrNode *s, int i, LinkStrNode *t) {
int k;
LinkStrNode *str;
LinkStrNode *p1 = s->next;
LinkStrNode *p2 = t->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) + 1) {
return str;
}
for (k = 1; k < i; k++) { // 移动到插入位置
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p1->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p1 = p1->next; // 移动原串指针
}
while (p2 != NULL) { // 插入子串
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p2->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p2 = p2->next; // 移动原串指针
}
while (p1 != NULL) { // 继续复制剩余部分
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p1->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p1 = p1->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
参数:
s
: 原始链串的头结点指针。i
: 插入位置(1-indexed)。t
: 要插入的链串的头结点指针。
返回值:
- 返回一个新的链串,该串包含
s
和在位置i
插入的t
。
2.10 子串的删除 (DelStr
)
此函数用于从一个链串中删除指定位置的子串。
LinkStrNode *DelStr(LinkStrNode *s, int i, int j) {
int k;
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || j < 0 || i + j - 1 > StrLength(s)) {
return str;
}
for (k = 1; k < i; k++) { // 移动到删除位置前
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
for (k = 0; k < j; k++) { // 跳过要删除的部分
p = p->next; // 移动原串指针
}
while (p != NULL) { // 复制剩余部分
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
参数:
s
: 原始链串的头结点指针。i
: 子串的起始位置(1-indexed)。j
: 子串的长度。
返回值:
- 返回一个新的链串,该串包含
s
除位置i
开始的长度为j
的子串外的所有节点。
2.11 子串的替换 (ReplaceSubStr
)
此函数用于在一个链串的指定位置替换一个子串。
LinkStrNode *ReplaceSubStr(LinkStrNode *s, int i, int j, LinkStrNode *t) {
LinkStrNode *newStr = InsertStr(s, i, t); // 先执行插入操作
LinkStrNode *delStr = DelStr(newStr, i, j); // 再执行删除操作
DestroyStr(newStr); // 销毁中间串
return delStr; // 返回替换后的串
}
参数:
s
: 原始链串的头结点指针。i
: 要替换子串的起始位置(1-indexed)。j
: 要替换子串的长度。t
: 新的子串的头结点指针。
返回值:
- 返回一个新的链串,该串包含
s
除位置i
开始的长度为j
的子串被t
替换外的所有节点。
2.12 串的比较 (StrCompare
)
此函数用于比较两个链串。
int StrCompare(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *p = s->next, *q = t->next;
while (p != NULL && q != NULL) {
if (p->data != q->data) {
return p->data > q->data ? 1 : -1;
}
p = p->next;
q = q->next;
}
if (p == NULL && q == NULL) {
return 0;
} else if (p == NULL) {
return -1;
} else {
return 1;
}
}
参数:
s
: 第一个链串的头结点指针。t
: 第二个链串的头结点指针。
返回值:
- 如果
s
和t
相等,返回 0; - 如果
s
小于t
,返回负数; - 如果
s
大于t
,返回正数。
2.13 打印链串 (PrintStr
)
此函数用于显示一个链串的内容。
void PrintStr(LinkStrNode *s) {
LinkStrNode *p = s->next;
while (p != NULL) {
printf("%c", p->data);
p = p->next;
}
printf("\n");
}
参数:
s
: 要显示的链串的头结点指针。
3. 示例程序
下面是一个示例程序,演示了如何使用上述定义的功能。
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 用于使用 strlen 函数
#define MaxSize 100
// 链串的结构体
typedef struct StringNode {
char data;
struct StringNode *next;
} LinkStrNode;
// 生成串
void StrAssign(LinkStrNode *&s, const char str[]) {
LinkStrNode *r;
LinkStrNode *p;
s = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建头结点
r = s;
for (int i = 0; str[i] != '\0'; i++) {
p = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
p->data = str[i]; // 设置数据
r->next = p; // 将新节点链接到链表
r = p; // 移动指针到新节点
}
r->next = NULL; // 设置尾节点的 next 为 NULL
}
// 销毁串
void DestroyStr(LinkStrNode *&s) {
LinkStrNode *pre = s;
LinkStrNode *p = s->next;
while (p != NULL) {
free(pre); // 释放前一个节点
pre = p; // 移动 pre 到当前节点
p = pre->next; // 移动 p 到下一个节点
}
free(pre); // 释放最后一个节点
s = NULL; // 清空串指针
}
//判断是否为空串
bool StrEmpty(LinkStrNode *s){
return s->next == NULL;
}
// 串的复制
void StrCopy(LinkStrNode *&s, LinkStrNode *t) {
LinkStrNode *p = t->next;
LinkStrNode *q;
LinkStrNode *r;
s = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
r = s;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
}
// 判断串相等
bool StrEqual(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *p = s->next;
LinkStrNode *q = t->next;
while (p != NULL && q != NULL && p->data == q->data) {
p = p->next;
q = q->next;
}
return (p == NULL && q == NULL);
}
// 获得串的长度
int StrLength(LinkStrNode *s) {
int i = 0;
LinkStrNode *p = s->next;
while (p != NULL) {
i++;
p = p->next;
}
return i;
}
// 串的连接
LinkStrNode *Concat(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
r = str;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
p = t->next;
while (p != NULL) {
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
// 获得子串
LinkStrNode *SubStr(LinkStrNode *s, int i, int j) {
int k;
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || j < 0 || i + j - 1 > StrLength(s)) {
return str;
}
for (k = 1; k < i; k++) { // 移动到起始位置
p = p->next;
}
for (k = 1; k <= j; k++) { // 复制子串
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
// 子串的插入
LinkStrNode *InsertStr(LinkStrNode *s, int i, LinkStrNode *t) {
int k;
LinkStrNode *str;
LinkStrNode *p1 = s->next;
LinkStrNode *p2 = t->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) + 1) {
return str;
}
for (k = 1; k < i; k++) { // 移动到插入位置
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p1->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p1 = p1->next; // 移动原串指针
}
while (p2 != NULL) { // 插入子串
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p2->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p2 = p2->next; // 移动原串指针
}
while (p1 != NULL) { // 继续复制剩余部分
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p1->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p1 = p1->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
// 子串的删除
LinkStrNode *DelStr(LinkStrNode *s, int i, int j) {
int k;
LinkStrNode *str;
LinkStrNode *p = s->next;
LinkStrNode *q;
LinkStrNode *r;
str = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 创建新的头结点
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || j < 0 || i + j - 1 > StrLength(s)) {
return str;
}
for (k = 1; k < i; k++) { // 移动到删除位置前
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
for (k = 0; k < j; k++) { // 跳过要删除的部分
p = p->next; // 移动原串指针
}
while (p != NULL) { // 复制剩余部分
q = (LinkStrNode *)malloc(sizeof(LinkStrNode)); // 分配新节点
q->data = p->data; // 设置数据
r->next = q; // 将新节点链接到链表
r = q; // 移动指针到新节点
p = p->next; // 移动原串指针
}
r->next = NULL; // 设置尾节点的 next 为 NULL
return str;
}
// 子串的替换
LinkStrNode *ReplaceSubStr(LinkStrNode *s, int i, int j, LinkStrNode *t) {
LinkStrNode *newStr = InsertStr(s, i, t); // 先执行插入操作
LinkStrNode *delStr = DelStr(newStr, i, j); // 再执行删除操作
DestroyStr(newStr); // 销毁中间串
return delStr; // 返回替换后的串
}
int StrCompare(LinkStrNode *s, LinkStrNode *t) {
LinkStrNode *p = s->next, *q = t->next;
while (p != NULL && q != NULL) {
if (p->data != q->data) {
return p->data > q->data ? 1 : -1;
}
p = p->next;
q = q->next;
}
if (p == NULL && q == NULL) {
return 0;
} else if (p == NULL) {
return -1;
} else {
return 1;
}
}
// 打印链串
void PrintStr(LinkStrNode *s) {
LinkStrNode *p = s->next;
while (p != NULL) {
printf("%c", p->data);
p = p->next;
}
printf("\n");
}
// 主函数
int main() {
LinkStrNode *s, *t;
char input[MaxSize];
// 用户输入第一个串
printf("请输入第一个字符串: ");
scanf("%s", input);
StrAssign(s, input);
// 用户输入第二个串
printf("请输入第二个字符串: ");
scanf("%s", input);
StrAssign(t, input);
// 显示两个串
printf("第一个字符串: ");
PrintStr(s);
printf("第二个字符串: ");
PrintStr(t);
// 检查是否为空串
if (StrEmpty(s)) {
printf("第一个串为空串。\n");
} else {
printf("第一个串不为空串。\n");
}
if (StrEmpty(t)) {
printf("第二个串为空串。\n");
} else {
printf("第二个串不为空串。\n");
}
// 比较两个串
int result = StrCompare(s, t);
if (result == 0) {
printf("两个串相等。\n");
} else if (result < 0) {
printf("第一个串小于第二个串。\n");
} else {
printf("第一个串大于第二个串。\n");
}
// 计算两个串的长度
printf("第一个串的长度为: %d\n", StrLength(s));
printf("第二个串的长度为: %d\n", StrLength(t));
// 连接两个串
LinkStrNode *concatenated = Concat(s, t);
printf("连接后的字符串: ");
PrintStr(concatenated);
// 获取子串
int start, length;
printf("请输入子串起始位置 (1-%d): ", StrLength(s));
scanf("%d", &start);
printf("请输入子串长度: ");
scanf("%d", &length);
LinkStrNode *substring = SubStr(s, start, length);
printf("子串为: ");
PrintStr(substring);
// 插入子串
printf("请输入插入位置 (1-%d): ", StrLength(s) + 1);
scanf("%d", &start);
LinkStrNode *inserted = InsertStr(s, start,t );
printf("插入后的字符串: ");
PrintStr(inserted);
// 删除子串
printf("请输入删除起始位置 (1-%d): ", StrLength(s));
scanf("%d", &start);
printf("请输入删除长度: ");
scanf("%d", &length);
LinkStrNode *deleted = DelStr(s, start, length);
printf("删除后的字符串: ");
PrintStr(deleted);
// 替换子串
printf("请输入替换起始位置 (1-%d): ", StrLength(s));
scanf("%d", &start);
printf("请输入替换长度: ");
scanf("%d", &length);
LinkStrNode *replaced = ReplaceSubStr(s, start, length, t);
printf("替换后的字符串: ");
PrintStr(replaced);
return 0;
}
4. 使用说明
- 编译并运行上述示例程序。
- 根据提示输入两个字符串。
- 观察并理解各个操作的结果。
5. 总结
通过本文档,您可以了解到链串的基本概念和常用操作。使用这些操作,您可以轻松地处理字符串数据,进行各种字符串相关的任务。希望这份指南能够帮助您更好地理解和使用链串。