串的基本概念
串 ( 字符串 ) :是零个或多个字符组成的有限序列。 记作: S=”
a1
a2
a3
a4
…” ,其中 S 是串名,
ai
(1 ≦ i ≦ n) 是 单个,可以是字母、数字或其它字符。
串值:双引号括起来的字符序列是串值。
串长:串中所包含的字符个数称为该串的长度。
空串 ( 空的字符串 ) :长度为零的串称为空串,它不 包含任何字符。
空格串 ( 空白串 ) :构成串的所有字符都是空格的串 称为空白串。
子串 (substring) :串中任意个连续字符组成的子序 列称为该串的子串,包含子串的串相应地称为主串。
子串的序号:将子串在主串中首次出现时的该子串 的首字符对应在主串中的序号,称为子串在主串中的序
号(或位置)。
串相等:如果两个串的串值相等 ( 相同 ) ,称这两 个串相等。换言之,只有当两个串的长度相等,且各 个对应位置的字符都相同时才相等。
串的基本操作
1.串赋值
2.串拷贝
3.串比较
4.串长度
5.清空串
6.返回第pos个字符起长度为len的字串
7.若存在和T相等的字串,返回其字串第一次出现的位置
8.替换字串
9.在串中的第pos个字符之前插入字串
10.删除第pos个字符起长度为len的字串
11.销毁串
12. 两个串相接
串的表示和实现
◆ 定长顺序存储表示:将串定义成字符数组,利用 串名可以直接访问串值。用这种表示方式,串的存 储空间在编译时确定,其大小不能改变。
特点:
1.操作时间的复杂度基本上基于字符序列的长度
2.如果在操作中出现串值序列的长度超过上界MAXSTRLEN时,约定用截尾法处理.
定长顺序存储结构定义为:
#define MAX_STRLEN 256
typedef struct
{
char str[MAX_STRLEN] ;
int length;
} StringType ;
◆ 堆分配存储方式:仍然用一组地址连续的存储单 元来依次存储串中的字符序列,但串的存储空间是 在程序运行时根据串的实际长度动态分配的。
实现方法:系统提供一个空间足够大且地址连续的存储空间 ( 称为“堆” ) 供串使用。可使用 C 语言的动态存储分配函数 malloc() 和 free() 来管理。
串的堆式存储结构的类型定义
typedef struct
{
char *ch; /* 若非空,按长度分配,否则为 NULL */
int length;
} HString ;
◆ 块链存储方式:是一种链式存储结构表示。
串的链式存储结构和线性表的串的链式存储结构类似,采用单链表来存储串,结点的构成是:
data 域:存放字符, data 域可存放的字符个数
称为结点的大小;
next 域:存放指向下一结点的指针。
若每个结点仅存放一个字符,则结点的指针域就非常多,造成系统空间浪费,为节省存储空间,考虑串结构的特殊性,使每个结点存放若干个字符,这种
结构称为块链结构。如图 4-1 是块大小为 3 的串的块链式存储结构示意图。
结点的大小直接影响着串的处理效率.
串的存储密度:
显然,存储密度小时(例如结点大小为1),运算处理非常方便,但是存储占用量大.而存储密度大时,存储占用量相对较小,但如果在串处理过程中需进行过多的内外存交换时,会影响处理的总效率.
堆分配存储方式C语言实现
接口定义头文件
#define true 1
#define false 0
#define S_g_T 1
#define S_e_T 0
#define S_s_T -1
/* 串的堆分配存储表示 */
typedef struct h_string{
char *str;
/* 串长度 */
int length;
}h_string;
/* 生成一个值等于str的字符串常量
* 生成成功返回true
* 否则返回false
*/
int string_assign(h_string *S, char *ch);
/* 复制串S并返回 */
h_string string_copy(h_string S);
/* 判断串S是否为空
* 若为空则返回true
* 否则返回false
*/
int string_empty(h_string S);
/* 比较串S和T
* 若S>T返回S_g_T
* 若S=T返回S_e_T
* 若S<T返回S_s_T
*/
int string_compare(h_string S, h_string T);
/* 返回自字符串的长度 */
int string_length(h_string S);
/* 将S清空 */
void string_clear(h_string *S);
/* S1和S2连接,并由T返回连接的新串
* 若连接成功,返回true
* 否则返回false
*/
int string_contact(h_string *T, h_string S1, h_string S2);
/* 用sub返回串S第pos个字符起长度为len的子串
* 成功返回true
* 否则返回false
*/
int string_substring(h_string S, h_string *sub, int pos, int len);
/* 若主串S中存在和串T值相等的字串,
* 返回它在主串S中第pos个字符之后第一次出现的位置
* 否则返回-1
*/
int string_index(h_string S, h_string T, int pos);
/* 用V替换主串S中出现所有的与T串值相等的不重叠的字串 */
h_string string_replace(h_string *S, h_string V, h_string T);
/* 在串S的第pos个字符之前插入串T */
h_string string_strinsert(h_string S, h_string T, int pos);
/* 从串S中删除第pos个字符起长度为len的字串 */
h_string string_strdelete(h_string S, int pos, int len);
/* 销毁串S */
void string_destroy(h_string *S);
/* 遍历打印字符 */
void string_traverse(h_string S);
接口实现文件
#include<stdio.h>
#include<stdlib.h>
#include"string.h"
int string_assign(h_string *S, char *ch)
{
/* 释放S的原有空间 */
if(S -> str)
free(S -> str);
/* 求串ch的长度 */
int count = 0;
char *c;
for(count = 0, c = ch; *c; count++, c++);
printf("count = %d\n", count);
/* 若*ch为空 */
if(!count)
{
S -> length = 0;
S -> str = NULL;
return true;
}
else
{
/* 申请内存空间 */
if(!(S -> str = (char *)malloc(sizeof(char)*count)))
return false;
S -> length = count;
/* 赋值 */
int i = 0;
for(i = 0; i < count; i++)
{
S -> str[i] = ch[i];
}
return true;
}
}
h_string string_copy(h_string S)
{
/* 创建新串 */
int len = S.length;
h_string T;
T.str = (char *)malloc(sizeof(char) * len);
T.length = len;
/* 复制 */
int i = 0;
for (i = 0; i < len; i++)
{
T.str[i] = S.str[i];
}
return T;
}
int string_empty(h_string S)
{
if(S.length == 0)
return false;
else
return true;
}
int string_length(h_string S)
{
return S.length;
}
int string_compare(h_string S, h_string T)
{
/* 相同索引号从小到大的字符比较
* 相同则继续比较,直至最后一个字符
* 否则直接返回两字符的差值
*/
int i = 0;
for (i = 0;i < S.length && i < T.length; i++)
{
int value = S.str[i] - T.str[i];
if(value > 0)
return 1;
if(value < 0)
return -1;
}
/* 若两个字符串前面的字符都一致
* 则比较长度
* 长度一样说明相等
* 长度短的字符串小
*/
int value = S.length - T.length;
if(value == 0)
return 0;
if(value > 0)
return 1;
if(value < 0)
return -1;
}
void string_clear(h_string *S)
{
if(S -> str)
{
free(S -> str);
S -> str = NULL;
}
S -> length = 0;
}
int string_contact(h_string *T, h_string S1, h_string S2)
{
/* 释放旧字符串(可直接调用string_empty()函数) */
if(T -> str)
{
free(T -> str);
T -> str = NULL;
}
/* 新字符串长度 */
T -> length = S1.length + S2.length;
T -> str = (char *)malloc(sizeof(char)*(T -> length));
if(!T -> str)
return false;
/* 赋值 */
int i = 0;
for(i = 0; i < T -> length; i++)
{
if(i >= S1.length)
{
T -> str[i] = S2.str[i - S1.length];
}
else
{
T -> str[i] = S1.str[i];
}
}
return true;
}
int string_substring(h_string S, h_string *T, int pos, int len)
{
/* pos,len与S.length间的关系
* 1 <= pos <= S.length;
* pos + len - 1 <= S.length
* len >= 0
*/
/* 判断子串起始索引号和长度是否满足要求 */
if(pos < 1 || pos > S.length || len > S.length + pos - 1 || len < 0)
return false;
/* 清空旧串 */
if(T -> str)
free(T -> str);
/* 空子串 */
if (!len)
{
T -> str = NULL;
T -> length = 0;
return false;
}
/* 申请内存空间 */
T -> str = (char *)malloc(sizeof(char) * len);
T -> length = len;
/* 申请内存空间失败 */
if(!(T -> str))
return false;
/* 赋值 */
int i = 0;
for (i = 0; i < len; i++)
{
T -> str[i] = S.str[pos - 1 + i];
}
return true;
}
int string_index(h_string S, h_string T, int pos)
{
/* 若T为空串 */
if(!(T.length))
return -1;
/* 比较 */
/* 串S的起始位置 */
int i = pos -1;
/* 串T的起始位置 */
int j = 0;
while(i < S.length && j < T.length)
{
if(S.str[i] == T.str[j])
{
i += 1;
j += 1;
}
else
{
i = i -j + 1;
j = 0;
}
}
if (j >= T.length)
return (i - T.length);
return -1;
}
h_string string_replace(h_string *S, h_string V, h_string T)
{
/* 替换之后,下一轮替换的起始位置,初始化为1 */
int pos = 1;
while(pos <= S -> length)
{
int index = string_index(*S, T, pos);
/* S中存在与T串值相等的子串 */
if (index >= 0)
{
/* V.length = T.length */
if (V.length == T.length)
{
int i = 0;
for (i = 0; i < V.length; i++)
{
S -> str[index + i] = V.str[i];
}
pos = index + V.length;
continue;
}
/* V.length > T.length */
if (V.length > T.length)
{
/* 复制字符串S,保存在临时字符串temp中 */
h_string temp = string_copy(*S);
/* 增加申请内存 */
S -> str = (char *)malloc(sizeof(char) * S -> length + V.length - T.length);
S -> length = S -> length + V.length - T.length;
int i = 0;
int j = 0;
for (i = 0; i < index; i++)
{
S -> str[i] = temp.str[i];
}
for (i = 0; i < V.length; i++)
{
S -> str[index + i] = V.str[i];
}
for(i = index + V.length, j = index + T.length; j < temp.length; i++, j++)
{
S -> str[i] = temp.str[j];
printf("temp.length =%d j=%d\n", temp.length, j);
}
pos = index + V.length;
printf("部分\n");
string_traverse(*S);
continue;
}
/* V.length < T.length */
if(V.length < T.length)
{
/* 复制字符串S,保存在临时字符串temp中 */
h_string temp = string_copy(*S);
/* 释放旧字符串 */
free(S -> str);
S -> str = NULL;
/* 新申请内存 */
int len = temp.length + V.length - T.length;
S -> str = (char *)malloc(sizeof(char) * len);
S -> length = len;
int i = 0;
int j = 0;
for (i = 0; i < index; i++)
{
S -> str[i] = temp.str[i];
}
for (i = 0; i < V.length; i++)
{
S -> str[index + i] = V.str[i];
}
for(i = index + V.length, j = index + T.length; j < temp.length; i++, j++)
{
S -> str[i] = temp.str[j];
}
pos = index + V.length;
continue;
}
}
else
return *S;
}
}
h_string string_strinsert(h_string S, h_string T, int pos)
{
/* 插入字符串为空 */
if(!T.length)
return S;
/* 存储字符串S的临时字符串 */
h_string temp = S;
/* 增加申请内存 */
int len = S.length + T.length;
S.length = len;
S.str = (char *)malloc(sizeof(char) * len);
/* 插入字符串 */
int i = 0;
int j = 0;
for (i = 0; i < pos - 1; i++)
{
S.str[i] = temp.str[i];
}
for (i = 0; i < T.length; i++)
{
S.str[pos - 1 + i] = T.str[i];
}
for (i = pos -1 + T.length, j = pos - 1; j < temp.length; i++, j++)
{
S.str[i] = temp.str[j];
printf("S.str[%d]=%c j=%d\n", i, S.str[i], j);
}
return S;
}
h_string string_strdelete(h_string S, int pos, int len)
{
/* pos,len与S.length间的关系
* 1 <= pos <= S.length;
* pos + len - 1 <= S.length
* len > 0
*/
/* 判断子串起始索引号和长度是否满足要求 */
if(pos < 1 || pos > S.length || len > S.length + pos - 1 || len <= 0)
return S;
/* S删除子串之后的长度 */
int length = S.length - len;
if (!length)
{
free(S.str);
S.str = NULL;
S.length = 0;
return S;
}
else
{
h_string temp;
temp.str = (char *)malloc(sizeof(char) * length);
temp.length = length;
int i = 0;
int j = 0;
for (i = 0; i < pos - 1; i++)
{
temp.str[i] = S.str[i];
}
for (i = pos - 1, j = pos -1 + len; j < S.length; i++, j++)
{
temp.str[i] = S.str[j];
}
return temp;
}
}
void string_destroy(h_string *S)
{
free(S -> str);
S -> str = NULL;
S -> length = 0;
}
void string_traverse(h_string S)
{
if(!(S.length))
printf("字符串为空\n");
else
{
int i = 0;
for(i = 0; i < S.length; i++)
{
printf("%c",S.str[i]);
}
printf("\n");
}
}
其中的int string_index()函数的采用的是暴力比较法,该算法最好情况的时间复杂度是O(n+m),最差情况时的时间复杂度为O(n*m),可以采用KMP算法改进,使得其时间复杂度为O(n+m),详细情况见下篇博文.
测试文件
int main()
{
char *ch = "jiang";
h_string S;
S.length = 7;
S.str = (char *)malloc(sizeof(char) * S.length);
/* 赋值 */
string_assign(&S, ch);
string_traverse(S);
/* 复制 */
h_string T = string_copy(S);
string_traverse(T);
/* 判空 */
int value = string_empty(S);
printf("字符串判空=%d\n",value);
/* 字符串比较 */
char *str = "linyijiang";
string_assign(&T, str);
value = string_compare(S, T);
printf("字符串比较=%d\n",value);
/* 字符串长度 */
value = string_length(T);
printf("字符串长度=%d\n", value);
/* 清空字符串 */
//string_clear(&S);
//printf("字符串清空:");
//string_traverse(S);\
/* 字符串连接 */
h_string ST ;
ST.str = NULL;
string_contact(&ST, S, T);
string_traverse(ST);
/* 字串位置 */
string_traverse(T);
string_traverse(S);
value = string_index(T, S, 1);
printf("index=%d\n", value);
/* 返回长度为len的字符串 */
printf("返回长度为len的字符串\n");
string_traverse(T);
string_traverse(S);
value = string_substring(T, &S, 2, 9);
string_traverse(S);
/* 替换 */
//string_traverse(T);
//h_string V;
//V.str = "brother";
//V.length = 7;
//S.str = "i";
//S.length = 1;
//S = string_replace(&T, V, S);
//string_traverse(S);
/* 插入字符串 */
//printf("插入字符串\n");
//string_traverse(T);
//S = string_strinsert(T, V, 2);
//string_traverse(S);
/* 删除 */
//printf("删除\n");
//string_traverse(T);
//S = string_strdelete(T, 2, 6);
//string_traverse(S);
}