数据结构——串

news2025/1/23 17:28:40

串又称字符串,是由零个或多个字符组成的有限序列,是一种特殊的线性表。由串中若干个连续字符组成的子序列称为子串。

利用字符数组或字符指针表示串:

char str1[] = { 'a','b','c','d','\0' };
char str2[] = "abcdef";
char* str3 = str1;

上述三种表示串的方法其实就是顺序表的简化形式。由于在上述表达方法中,字符串的最后都有一个结束符“/0”,从而不需要指定顺序表的长度。

串的另一种表示方法是采用双向链表表示,称之为链串。存储密度为有效空间与所占用总空间的比值,若每个结点只存放一个字符,那么字符串的存储密度只有1/(2*4+1)=0.11(next,pre指针所占用空间都为4个字节),空间利用率低。
为提高空间效率,可以在每一个结点中存放多个字符。

在头文件<string.h>中有针对字符数组和字符指针所表示字符串的相关操作。例如,求字符串的长度(strlen)、字符串赋值(strcpy)、字符串比较(strcmp)、字符串连接(strcat)等。

链串的组织形式与一般的链表类似。主要的区别在于,链串中的一个节点可以存储多个字符。通常将链串中每个节点所存储的字符个数称为节点大小。

链串节点大小的选择与顺序串的格式选择类似。节点大小越大,则存储密度越大。但存储密度越大,一些操作(如插入、删除、替换等)有所不便,且可能引起大量字符移动,因此它适合于在串基本保持静态使用方式时采用。节点大小越小(如节点大小为1时),运算处理越方便,但存储密度下降。为简便起见,这里规定链串节点大小均为1。                                                                     

//str = (chainString)malloc(sizeof(csNode));

不采用该方式进行分配空间,会出现黄色警告

采用如下方式:

str = new csNode;

 代码如下所示:

#include<iostream>
using namespace std;
typedef char datatype;

typedef struct csNode {
	datatype data;
	csNode* next;
	csNode() :next(NULL) {
		data = '\0';
	};
}*chainString;

void cs_insert(chainString p, datatype ch) {
	chainString q = new csNode;
	if (q == NULL) {
		cout << "插入结点失败" << endl;
		return;
	}
	q->data = ch;
	q->next = p->next;
	p->next = q;
}

/*生成串*/
void createChainString(chainString h, datatype cstr[], int n)
{
	if (h == NULL) h = new csNode;
	for (int i = n - 1; i >= 0; i--)
		cs_insert(h, cstr[i]);
}

//串的第一个数据结点删除
void cs_delete(chainString p) {
	if (p == NULL) return;
	chainString q = p->next;
	if (q == NULL)
		return;
	p->next = q->next;
	delete q;
	q = NULL;
}

/*摧毁串*/
void destroyString(chainString& h)
{
	while (h->next != NULL) {
		cs_delete(h);
	}
	delete h;
	h = NULL;
}

/*串的复制,t赋值*/
void StrCopy(chainString& s, chainString t)
{
	if (s == NULL) s = new csNode;
	chainString p = t->next;
	while (p != NULL)
	{
		cs_insert(s, p->data);
		p = p->next;
	}
}

/*判断串相等*/
bool StrEqual(chainString s, chainString t)
{
	chainString p = s->next, q = t->next;
	while (p != NULL && q != NULL && p->data == q->data)
	{
		p = p->next;
		q = q->next;
	}
	if (p == NULL || q == NULL)		//表明此时已经扫描到串尾
		return true;
	else
		return false;
}

/*求串长*/
int StrLength(chainString s)
{
	int i = 0;
	chainString p = s->next;
	while (p != NULL)
	{
		i++;
		p = p->next;
	}
	return i;
}

/*串的连接,但不改变原本的s和t*/
chainString ConCat(chainString s, chainString t)
{
	chainString str, p, q, r;
	str = new csNode;
	r = str;
	//先将s存放到str中
	p = s->next;
	while (p != NULL)
	{
		q = new csNode;
		q->data = p->data;

		r->next = q;	r = q; //通过r指针的不断移动,构建str的后面部分,str作为一个不变的头,进行遍历

		p = p->next;
	}
	//再循环利用变量将t也存入str中,即接在s之后
	p = t->next;
	while (p != NULL)
	{
		q = new csNode;
		q->data = p->data;

		r->next = q;	r = q;

		p = p->next;
	}
	r->next = NULL;
	return str;
}

/*求子串*/
chainString SubStr(chainString s, int i, int j)
{
	chainString str, p, q, r;
	str = new csNode;
	str->next = NULL;		//若不将str置为空串,则输入放入会出现异常
	r = str;

	if (i <= 0 || i > StrLength(s) || j<0 || j>StrLength(s) || j - i + 1 > StrLength(s))
		return str;

	//利用指向p先行遍历到位置i
	p = s->next;
	for (int k = 1; k < i; ++k)
		p = p->next;

	//在从位置i开始遍历到位置j取出子串
	for (int k = i; k <= j; ++k)
	{
		q = new csNode;
		q->data = p->data;

		r->next = q;	r = q;

		p = p->next;
	}
	r->next = NULL;
	return str;
}

/*子串的插入*/
chainString InsStr(chainString s, int i, chainString t)
{
	int k = 1;
	chainString str, p, p1, q, r;
	str = new csNode;
	str->next = NULL;
	r = str;

	//判断下标i是否越界
	if (i <= 0 || i > StrLength(s) + 1)
		return str;

	//利用指针p,先将前0~i-1个字符存入str
	p = s->next;
	for (int k = 1; k < i; ++k)
	{
		q = new csNode;
		q->data = p->data;
		r->next = q;	r = q;
		p = p->next;
	}

	//利用指针p1,将串t中的结点顺势放入str中
	p1 = t->next;
	while (p1 != NULL)
	{
		q = new csNode;
		q->data = p1->data;
		r->next = q;	r = q;
		p1 = p1->next;
	}

	//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
	while (p != NULL)
	{
		q = new csNode;
		q->data = p->data;
		r->next = q;	r = q;
		p = p->next;
	}
	r->next = NULL;
	return str;
}

/*子串的删除*/
chainString DelStr(chainString s, int i, int j)
{
	int k = 1;
	chainString str, p, q, r;
	str = new csNode;
	str->next = NULL;
	r = str;

	if (i <= 0 || i > StrLength(s) || j - i + 1 > StrLength(s))
		return str;

	//利用指针p,先将前0~i-1个字符存入str
	p = s->next;
	for (int k = 1; k < i; ++k)
	{
		q = new csNode;
		q->data = p->data;
		r->next = q;	r = q;
		p = p->next;
	}

	//再利用指针p,直接遍历过j个位置
	for (k = i; k <= j; ++k)
		p = p->next;

	//此时的指针p,已然移动到j+1的位置,开始将后面的剩余结点继续存入串str中
	while (p != NULL)
	{
		q = new csNode;
		q->data = p->data;
		r->next = q;	r = q;
		p = p->next;
	}
	r->next = NULL;
	return str;
}

/*子串的替换*/
chainString RepStr(chainString s, int i, int j, chainString t)
{
	int k = 1;
	chainString str, p, p1, q, r;
	str = new csNode;
	str->next = NULL;
	r = str;
	//判断下标i是否越界
	if (i <= 0 || i > StrLength(s) + 1 || j<0 || j - i + 1>StrLength(s))
		return str;

	//利用指针p,先将前0~i-1个字符存入str
	p = s->next;
	for (int k = 1; k < i; ++k)
	{
		q = new csNode;
		q->data = p->data;	 q->next = NULL;
		r->next = q;	r = q;
		p = p->next;
	}

	for (k = i; k <= j; ++k)
		p = p->next;		//直接删除原本串s中从第i位开始长度为j的那一子串

	//利用指针p1,将串t中的结点顺势放入str中
	p1 = t->next;
	while (p1 != NULL)
	{
		q = new csNode;
		q->data = p1->data;	 q->next = NULL;
		r->next = q;	r = q;
		p1 = p1->next;
	}

	//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
	while (p != NULL)
	{
		q = new csNode;
		q->data = p->data;	 q->next = NULL;
		r->next = q;	r = q;
		p = p->next;
	}
	r->next = NULL;
	return str;
}

/*输出串*/
void DispStr(chainString s)
{
	chainString p = s->next;
	while (p != NULL)
	{
		cout << p->data << endl;
		p = p->next;
	}
	cout << endl;
}

void test1()
{
	printf("*测试串的生成*\n");
	chainString s1 = new csNode;
	datatype ch1[12] = "Hello World";
	createChainString(s1, ch1, 12);
	printf("串s初始化为:");
	DispStr(s1);

	printf("\n*测试串的复制*\n");
	chainString s2 = new csNode;
	datatype ch2[6] = "Frank";
	createChainString(s2, ch2, 6);

	StrCopy(s1, s2);
	printf("复制后的串s1为:");
	DispStr(s1);

	printf("\n*测试串是否相等*\n");
	chainString s3 = new csNode, s4 = new csNode, s5 = new csNode;
	datatype ch3[6] = "apple";
	datatype ch4[6] = "apple";
	datatype ch5[7] = "banana";
	createChainString(s3, ch3, 6);
	createChainString(s4, ch4, 6);
	createChainString(s5, ch5, 7);
	if (StrEqual(s3, s4))
		printf("s3与s4两串相等\n");
	else
		printf("s3与s4两串不相等\n");

	if (StrEqual(s3, s5))
		printf("s3与s5两串相等\n");
	else
		printf("s3与s5两串不相等\n");

	printf("\n*测试串的长度*\n");
	int len = StrLength(s5);
	printf("显示一下串s5:");
	DispStr(s5);
	printf("串s5的长度为:%d\n", len);

	printf("\n*测试串的连接*\n");
	chainString s6 = new csNode;
	chainString s7 = new csNode;
	chainString s8 = new csNode;
	datatype ch6[3] = "so";
	datatype ch7[6] = " good";
	createChainString(s6, ch6, 3);
	createChainString(s7, ch7, 6);
	s8 = ConCat(s6, s7);
	printf("串s6与串s7连接之后为:");
	DispStr(s8);
}

void test2()
{
	printf("*测试求子串*\n");
	chainString s1 = new csNode, s2 = new csNode;
	datatype ch1[10] = "wonderful";
	createChainString(s1, ch1, 10);
	printf("初始化的串s1为:");
	DispStr(s1);
	printf("串s1从第2个字符开始的连续4个字符的子串为:");
	s2 = SubStr(s1, 2, 4);
	DispStr(s2);

	printf("\n*测试子串的插入*\n");
	chainString s3 = new csNode, s4 = new csNode;
	datatype ch3[10] = "ok";
	createChainString(s3, ch3, 10);

	printf("在串s1的第3个位置插入串s3之后为:");
	s4 = InsStr(s1, 3, s3);
	DispStr(s4);

	printf("\n*测试子串的删除*\n");
	chainString s5 = new csNode, s6 = new csNode;
	datatype ch5[10] = "fantistic";
	createChainString(s5, ch5, 10);

	printf("将串s5的第5个字符开始的长度为2的子串删除后为:");
	s6 = DelStr(s5, 5, 2);
	DispStr(s6);

	printf("\n*测试子串的替换*\n");
	chainString s7 = new csNode;
	chainString s8 = new csNode;
	chainString s9 = new csNode;
	datatype ch7[15] = "accountability";
	datatype ch8[3] = "le";
	createChainString(s7, ch7, 15);
	createChainString(s8, ch8, 3);

	printf("将串s7从第10个字符开始的长度为2的子串替换成s8后为:");
	s9 = RepStr(s7, 10, 5, s8);
	DispStr(s9);
}

int main()
{
	test1();
	test2();
}

 参考:

数据结构 | 串的存储结构之链串_Fire_Cloud_1的博客-CSDN博客https://blog.csdn.net/Fire_Cloud_1/article/details/127414613?spm=1001.2014.3001.5501C++: 形参的*& 与 *的理解_福桐的博客-CSDN博客_c++ 形参&https://blog.csdn.net/weixin_40597170/article/details/95788150?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%BD%A2%E5%8F%82%20&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-95788150.142%5Ev70%5Ejs_top,201%5Ev4%5Eadd_ask&spm=1018.2226.3001.4187

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151351.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java设计模式之单例模式

这一篇&#xff0c;我们来介绍下设计模式最简单的一个模式&#xff0c;单例模式。 二、释义以及实战 2.1 单例模式的定义 单例模式&#xff0c;英文&#xff1a;Singleton Pattern,英文解释&#xff1a;Ensure a class has only instance,and provide a global point of acce…

黑马2022新版SSM框架教程(SpringMVC_day02)

SpringMVC_day02 文章目录SpringMVC_day021&#xff0c;SSM整合1.1 流程分析1.2 整合配置步骤1&#xff1a;创建Maven的web项目步骤2:添加依赖步骤3:创建项目包结构步骤4:创建SpringConfig配置类步骤5:创建JdbcConfig配置类步骤6:创建MybatisConfig配置类步骤7:创建jdbc.proper…

Vue(十二)

1. TodoList案例自定义事件 //App.vue <template><div id"root"><div class"todo-container"><div class"todo-wrap"><!-- addTodo添加自定义事件 --><MyHeader addTodo"addTodo"/><MyList …

Spring AOP详解

1.什么是 Spring AOP&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;⾯向切⾯编程&#xff0c;它是⼀种思想&#xff0c;它是对某⼀类事情的 集中处理。⽐如⽤户登录权限的效验&#xff0c;没学 AOP 之前&#xff0c;我们所有需要判断⽤户登…

YACC移进规约冲突案例分析(二)output中状态机转移步骤详解

案例 calc.y %union {int ival;const char *sval; } %token <ival> NUM %nterm <ival> exp %token <sval> STR %nterm <sval> useless %left - %left * %% exp:exp exp | exp - exp | exp * exp | exp / exp | NUM ; useless: STR; %%编译 $ biso…

恭喜龙蜥获得中国开源云联盟2022年度中国“最佳开源实践案例”和“杰出开源贡献者”奖项

近日&#xff0c;由工信部中国电子技术标准化研究院主办的 2022 木兰峰会在北京圆满举办&#xff0c;峰会上正式公布了中国开源云联盟(China Open Source Cloud League&#xff0c;简称“COSCL”) 2022 年度评选名单&#xff0c;龙蜥社区荣获中国“最佳开源实践案例”和“杰出开…

仪器设备使用

NI DcpowerSwitchDigitalDMMFgenScope名称直流电源&#xff08;SMU&#xff09;继电器PPMU数字万用表信号发生器示波器版本PXI-4147PXI-2567PXI-6571PXI-4070PXI-4463PXI-5160 1.Scope 示波器是一种电子测量仪器&#xff0c;可以在无干扰的情况下监控输入信号&#xff0c;随后…

Go结构体(struct)

文章目录Struct定义struct构造struct实例struct的值和指针在与函数共用时&#xff1a;匿名字段和嵌套struct嵌套struct的名称冲突问题Struct 是一个值类型的 定义struct type identifier struct {field1 type1field2 type2… } // 或者 type T struct { a, b int }理论上&am…

JAVA多线程初阶(1)

目录JAVA多线程(1)1.Thread类创建与使用1.1 继承Thread类1.2 实现并发关于sleep()1.3 Runnable创建线程1.4 匿名内部类创建线程1.5 lamda表达式创建线程2.多线程提高效率3.Thread类属性和方法3.1 Thread(String name)3.2 isDaemon()3.3 isAlive()3.3 线程的重要方法3.4 中断线程…

数据结构:图

文章目录图内存中存储图数据结构邻接矩阵存储方法用邻接矩阵&#xff08;Adjacency Matrix&#xff09;来表示一个图的缺点&#xff1a;浪费空间优点邻接表存储方法&#xff08;Adjacency List&#xff09;广度优先算法Breadth-First-Search&#xff08;BFS&#xff09;深度优先…

Android——GT库-日志工具

GT库在创造出来初期&#xff0c;里面的日志工具就一直存在的&#xff0c;经历了很久的迭代变更&#xff0c;当目前的最新版本&#xff0c;日志工具已经创造出更高级的调试日志方式了&#xff0c;接下来咋们来看看GT库中的日志工具具体使用方法吧。 使用GT库里的&#xff0c;当然…

web表单设计器的优点体现在哪?

在数字化管理越来越规范的当下&#xff0c;拥有一款优质高效的低代码开发平台&#xff0c;确实能给企业提质增效带来更大的帮助。很多客户朋友会问道&#xff1a;web表单设计器都有哪些特点&#xff1f;为什么能在企业的现代化办公管理中起到巨大的作用&#xff1f;今天&#x…

Linux终端远程工具xshell,xftp,mobasterm

目录 软件介绍 1.xshell 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第5步&#xff1a; 2.xftp 第一步&#xff1a; 第二部&#xff1a; 第三步&#xff1a; 3.mobasterm 全能终端神器——MobaXterm 第一步&#xff1a; 第二步&a…

C1083无法打开包括文件: “atlbase.h”: No such file or directory

在打开别人的项目的过程中遇到了“atlbase.h”无法打开的问题&#xff0c;在此记录一下。1.下载ATL生成工具与缓解只下载ATL生成工具后面还会报错&#xff0c;直接下载下载ATL生成工具与缓解一步到位。下载的入口在&#xff1a;工具--->获取工具与功能。需要注意的是&#x…

Guitar Pro2023Win/Mac中文吉他/贝斯打谱识谱软件

Guitar Pro 是一款曲谱阅读器。以 GTP 结尾的曲谱文件都必须用 Guitar Pro 才能打开。Guitar Pro 凭借着其便利的制谱和读曲谱环境&#xff0c;在各大谱库论坛里都占据着一席之地&#xff0c;喜欢吉他的朋友一定略有耳闻。早几年该作者将它移植到了移动平台&#xff0c;现在你也…

7-2国王游戏

题目&#xff1a; 恰逢 H 国国庆&#xff0c;国王邀请 n 位大臣来玩一个有奖游戏。 首先&#xff0c;他让每个大臣在左、右手上面分别写下一个整数&#xff0c;国王自己也在左、右手上各写一个整数。 然后&#xff0c;让这 n 位大臣排成一排&#xff0c;国王站在队伍的最前面。…

应用层——Web和HTTP

目录 1. HTTP概况 1.1 Web页面简介 1.2 URL-统一资源定位器 1.3 HTTP协议 2. HTTP连接的两种类型 2.1 HTTP非持久性连接(Non-persistent HTTP) 2.2 HTTP持久性连接(Persistent HTTP) 2.2.1 无流水(pipelining)的持久性连接 2.2.2 带有流水机制的持久性连接 3. HT…

一站式开发平台赋能办公全场景

近几年&#xff0c;数字化办公迎来了新的机遇&#xff0c;根据亿欧智库《2022中国数字化办公市场研究报告》推算&#xff0c;数字化办公2021年的市场规模达到973.89亿元&#xff0c;至2025年将达到1768.16亿元&#xff0c;整体增速保持平稳&#xff0c;2018-2025年的CAGR为15.8…

Mybatis 框架搭建封装JDBC,实现sql语句

目录 1、maven新建一个工程​编辑 2、添加POM.XML配置文件 3、创建实例包 4、创建一个环境资源根目录 5、配置环境文件 6、创建接口&#xff0c;添加方法 7、编写sql语句 8、创建测试类 8.1 、定义工厂模式 8.2 、定义会话 8.3、定义对象 8.5、获取Builder建造工厂 …

LAB3 EIGRP1实验

1 实验拓扑&#xff1a; 2 实验要求&#xff1a; 1>.R1-R3环回口0:192.168.100.x/32。 2>.R1上采用手动汇总的命令&#xff0c;汇总4条环回口成一条。 3>.R1上下发一条默认路由。 4>.实现R1到R2的环回口路由非等价负载。 5>.as 90都使用eigrp认证。 6>…