C语言数据结构学习之 串型结构

一、串的介绍

​ 串型结构就是由若干个类型相同的元素组成的数据结构,末尾有一个结束标志,字符串就是由字符元素组成的串型结构,'\0’是它的结束标志,使用它可以存储单词、文章、汉字等文字信息。

​ 随着计算机和编程语言的发展,字符串在程序中使用的越来越多,字符串就简称串,对它的操作都是对串里面所有字符整体操作,以’\0’为结束标志,如果字符串的末尾没有’\0’,可能会产生乱码、段错误、脏数据等问题。

字符串结构应该具备的功能、算法有:

​ 创建:定义字符串

​ 销毁:释放字符串

​ 清空:删除所有的字符

​ 复制:就是strcpy函数

​ 连接:就是strcat函数

​ 比较:就是strcmp函数

​ 长度:就是strlen函数

​ 查询字串:就是strstr函数

字符串的表示与实现

​ 字符串一般有两实现方式,这两种方式都是使用顺序表,只是内存不同而已:

​ 1、用栈内存存储字符,定长方式,字符的数量一旦超出表的范围,为了防止内存越界要对字符串进行截取,了解一下即可。

​ 2、使用堆内存破碎字符,在操作字符串时,自动扩展堆内存。

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

typedef struct String
{
    
    
	char* ch;
	size_t size;
}String;

// 创建字符串
String* create_string(void)
{
    
    
	String* str = malloc(sizeof(String));
	str->ch = NULL;
	str->size = 0;
	return str;
}

// 计算长度
size_t len_string(String* str)
{
    
    
	return strlen(str->ch);
}

// 复制
void copy_string(String* str,const char* ch)
{
    
    
	size_t size = strlen(ch)+1;
	if(size > str->size)
	{
    
    
		str->ch = realloc(str->ch,size);
		str->size = size;
	}
	
	strcpy(str->ch,ch);
}

// 构建字符串
String* assign_string(const char* ch)
{
    
    
	String* str = create_string();
	copy_string(str,ch);
	return str;
}

// 判断是否是空串
bool empty_string(String* str)
{
    
    
	return NULL == str->ch;
}

// 赋值
void sav_string(String* str1,String* str2)
{
    
    
	if(str1->size < str2->size)
	{
    
    
		str1->ch = realloc(str1->ch,str2->size);
		str1->size = str2->size;
	}

	strcpy(str1->ch,str2->ch);
}

// 比较
int cmp_string(String* str1,String* str2)
{
    
    
	return strcmp(str1->ch,str2->ch);
}

// 连接
void cat_string(String* str1,String* str2)
{
    
    
	size_t size = strlen(str1->ch)+strlen(str2->ch)+1;
	if(size >= str1->size)
	{
    
    
		str1->ch = realloc(str1->ch,size);
		str1->size = size;
	}
	strcat(str1->ch,str2->ch);
}

// 清空字符串
void clear_string(String* str)
{
    
    
	free(str->ch);
	str->size = 0;
}

// 销毁字符串
void destroy_string(String* str)
{
    
    
	free(str->ch);
	free(str);
}

int main(int argc,const char* argv[])
{
    
    
	/*
	String* str1 = assign_string("hehe");
	String* str2 = create_string();
	// 浅拷贝,两个对象的ch指向了同一个字符串,当其它对象被销毁,另一个会受影响
	*str2 = *str1; // str2->ch = str1->ch; str2->len = str1->len;
	destroy_string(str1);
	puts(str2->ch);
	// 深拷贝,如果结构体中有成员是指针类型,且指向了堆内,这种结构变量不能直接赋值(浅拷贝),为了不出问题,我们需要实现深拷贝
	sav_string(str2,str1);
	destroy_string(str1);
	puts(str2->ch);
	*/
	String* str1 = assign_string("hehe");
	String* str2 = assign_string("xixi12rfaspoikjrqw;elifkj;lasejkrf;oawlikeujf;olaeirjtfasldkjf;qlwiejfa;sldkjf;qwlsekjfa;sldkfj;l");
	cat_string(str1,str2);
	puts(str1->ch);

	return 0;
}

封装字符串的意义

​ 1、字符串被封装成数据结构后,使用者不需要关心字符串的空间问题,但这种封装在C语言下没有太大意义,因为C语言语法的原因会使用字符串操作更麻烦。

​ 2、我们在C++语言中重写该数据结构,因为C++的语法会让该数据结构使用起来更方便。

子串查询的算法

​ 假定有两个字符串str1,str2,查询子串就是在字符串str1中查询是否存在str2,如果存在则返回str2首次出现的位置,这个操作叫子串查询。

char *str_str(const char *str1, const char *str2)
{
    
    
	assert(NULL != str1 && NULL != str2);
	for (int i = 0, j; '\0' != str1[i]; i++)
	{
    
    
		for (j = 0; '\0' != str2[j] && str2[j] == str1[i + j]; j++)
			;
		if ('\0' == str2[j])
			return (char *)str1 + i;
	}
	return NULL;
}

char *str_str(const char *str1, const char *str2)
{
    
    
	assert(NULL != str1 && NULL != str2);
	int i = 0, j = 0;
	while ('\0' != str1[i] && '\0' != str2[j])
	{
    
    
		if (str1[i] == str2[j])
		{
    
    
			i++;
			j++;
		}
		else
		{
    
    
			i = i - j + 1;
			j = 0;
		}
	}
	return '\0' == str2[j] ? (char *)str1 + i - j : NULL;
}

char* str_str(const char* str1,const char* str2)
{
    
    
	assert(NULL != str1 && NULL != str2);
	int sum1 = 0 , sum2 = 0 , len = 0;
	while('\0'!=str2[len]&&'\0'!=str1[len])
	{
    
    
		sum1 += str1[len];
		sum2 += str2[len++];
	}

	for(int i=len; '\0' != str1[i]; i++)
	{
    
    
		printf("%d %d\n",sum1,sum2);
		if(sum1 == sum2 && 0 == strncmp(str1+i-len,str2,len))
		{
    
    
			return (char*)str1+i-len;
		}
		else
		{
    
    
			sum1 -= str1[i-len];
			sum1 += str1[i];
		}
	}

	return NULL;
}

猜你喜欢

转载自blog.csdn.net/m0_62480610/article/details/126233885