数据结构与算法学习笔记五--串(C++)

目录

前言

一、定义

二、串的表示和实现

1.定长顺序存储表示

1.定义

2.串拼接

3.求子串

4.完整代码

2.堆分配存储表示

1.定义

2.求串长

3.串比较

4.清空s串,释放空间

5.串拼接

6.求子串

7.完整代码

3.串的块链存储表示

1.定义

2.生成串

3.生成串

4.完整代码


前言

        这篇文章主要记录下串的相关知识。

一、定义

        串即字符串,是由零个或者多个字符组成的有限序列,是数据元素为单个字符的特殊线性表。

        串长:串中字符的个数(n>=0)。n=0的时候称为空串。

        空格(白)串:由一个或者多个空格符组成的串。

        子串:串S中任意个连续的字符序列叫做S的子串;S叫做主串。

        子串位置:子串的第一个字符在主串中的序号。

        字符位置:字符在串中的序号。

        串相等:串长度相等,且对应位置上字符相等。

二、串的表示和实现

        串有三种机内表示方法,分别为定长顺序存储表示、对分配存储表示、串的块链存储表示。

        在了解串的几种表示之前,我们先复习下C++中的字符数组,以便您更好的理解代码,当然如果您已经非常熟悉这块的知识,可以直接看下面的实现。

        字符数组是用来存放字符数据的数组。

        字符数组的声明方式为:

char 数组名[常量表达式];

        例如:

char c[11] = {'I','','a','m','','C','h','i','n','a'}

        如果我们使用字符数组表示字符串的时候,需要在最后加一个'\0'表示字符串结束的标志。

1.定长顺序存储表示

        类似于线性表的顺序存储结构,用一组地址连续的存储单元存储串值的字符序列。在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区,则可用定长数组如下描述之。

1.定义

// ----- 串的定长顺序存储表示 -----

#define Maxstrlen 255

typedef unsigned char SString[Maxstrlen + 1];

        定义好串之后,我们可以实现串的常用操作,例如拼接,求子串等操作

2.串拼接

       拼接串的时候,我们要考虑下两个字符串的长度和是否超过我们定义的串的长度,超过之后我们要做截取处理。

//字符串拼接
void concat(String &result, String s1, String s2) {if (length(s1)+length(s2) <= Maxstrlen) {//不需要截断int i = 0;while (s1[i] != '\0') {result[i] = s1[i];i++;}int j = 0;while (s2[j] != '\0') {result[i] = s2[j];i++;j++;}result[i] = '\0';}else if( length(s1) < Maxstrlen ){//截断S2//不需要截断int i = 0;while (s1[i] != '\0') {result[i] = s1[i];i++;}int j = 0;for (; i+j <= Maxstrlen; j++) {result[i+j] = s2[j];cout<<"j="<<j<<endl;}result[Maxstrlen] = '\0';}else{//不需要截断for (int i = 0; i< Maxstrlen; i++) {result[i] = s1[i];}result[Maxstrlen] = '\0';}
}

3.求子串

//求子串
bool substring(String &sub, String str, int pos, int len) {int strLen = length(str);if (pos < 0 || pos > strLen || len < 0 || len > strLen - pos + 1) {return false;}for (int i = 0; i < len; ++i) {sub[i] = str[pos + i];}sub[len] = '\0';return true;
}

4.完整代码

#include <iostream>
using namespace std;#define Maxstrlen 8
typedef unsigned char String[Maxstrlen + 1];void initString(String &str) {str[0] = '\0';
}int length(String str) {int len = 0;while (str[len] != '\0') {len++;}return len;
}
//字符串拼接
void concat(String &result, String s1, String s2) {if (length(s1)+length(s2) <= Maxstrlen) {//不需要截断int i = 0;while (s1[i] != '\0') {result[i] = s1[i];i++;}int j = 0;while (s2[j] != '\0') {result[i] = s2[j];i++;j++;}result[i] = '\0';}else if( length(s1) < Maxstrlen ){//截断S2//不需要截断int i = 0;while (s1[i] != '\0') {result[i] = s1[i];i++;}int j = 0;for (; i+j <= Maxstrlen; j++) {result[i+j] = s2[j];cout<<"j="<<j<<endl;}result[Maxstrlen] = '\0';}else{//不需要截断for (int i = 0; i< Maxstrlen; i++) {result[i] = s1[i];}result[Maxstrlen] = '\0';}
}
//求子串
bool substring(String &sub, String str, int pos, int len) {int strLen = length(str);if (pos < 0 || pos > strLen || len < 0 || len > strLen - pos + 1) {return false;}for (int i = 0; i < len; ++i) {sub[i] = str[pos + i];}sub[len] = '\0';return true;
}
//插入
bool insertString(String &str, int pos, String insertStr) {int strLen = length(str);int insertLen = length(insertStr);if (pos < 0 || pos > strLen || strLen + insertLen > Maxstrlen) {return false;}for (int i = strLen; i >= pos; --i) {str[i + insertLen] = str[i];}for (int i = 0; i < insertLen; ++i) {str[pos + i] = insertStr[i];}str[strLen + insertLen] = '\0';return true;
}
//删除
bool deleteString(String &str, int pos, int len) {int strLen = length(str);if (pos < 0 || pos > strLen || len < 0 || len > strLen - pos + 1) {return false;}for (int i = pos + len; i <= strLen; ++i) {str[i - len] = str[i];}return true;
}int main() {String str, sub, result;initString(str);// 测试初始化和长度计算cout << "字符串str长度: " << length(str) << endl;// 测试串的拼接String s1 = "Hello", s2 = "Worlda";concat(result, s1, s2);cout << "拼接之后的字符串: " << result << endl;// 测试子串的提取int pos = 2, len = 3;if (substring(sub, result, pos, len)) {cout << "从下标2开始的子字符串 " << pos << " 长度 " << len << ": " << sub << endl;} else {cout << "非法位置" << endl;}// 测试插入操作String insertStr = " C++";if (insertString(result, pos, insertStr)) {cout << "插入之后的字符串: " << result << endl;} else {cout << "插入失败." << endl;}// 测试删除操作len = 5;if (deleteString(result, pos, len)) {cout << "字符串删除: " << result << endl;} else {cout << "字符串删除失败." << endl;}return 0;
}

2.堆分配存储表示

        在上述的使用定长存储表示串的时候,我们会发现再串的拼接、插入、置换等操作中有可能出现长度超过我们设定的最大长度。为了克服上述的弊端,我们可以不限定串长的最大长度,也就是动态分配串值的存储空间。

1.定义

        这种存储表示的特点是,仍以一组地址连续的存储单元存放串值字符序列,但它们的存储空间是在程序执行过程中动态分配而得。我们可以使用堆内存给串分配存储空间,调用C语言的malloc()和free()来管理。

typedef struct {char* ch;       // 若是非空串,则按照串长分配存储区,否则ch为NULLint length;     // 串长度
} HString;

2.求串长

        返回串的长度即可。

//求表长
int length(HString &str){return str.length;
}

3.串比较

// 字符串比较
int compare(const HString& s1, const HString& s2) {int i = 0;while (s1.ch[i] != '\0' && s2.ch[i] != '\0') {if (s1.ch[i] != s2.ch[i]) {return s1.ch[i] - s2.ch[i];}i++;}return s1.ch[i] - s2.ch[i];
}

4.清空s串,释放空间

        删除s占用的内存空间,串长置空。

// 释放内存
void destroyString(HString& str) {if (str.ch) {delete[] str.ch;}str.length = 0;
}

5.串拼接

// 字符串拼接
bool concat(HString& s1, const HString& s2) {// 计算拼接后的长度int newLength = s1.length + s2.length;char* newStr = new char[newLength + 1];// 将s1的字符复制到新串中for (int i = 0; i < s1.length; ++i) {newStr[i] = s1.ch[i];}// 将s2的字符复制到新串中for (int i = 0; i < s2.length; ++i) {newStr[s1.length + i] = s2.ch[i];}newStr[newLength] = '\0';// 释放原来的内存空间delete[] s1.ch;// 更新s1的指针和长度s1.ch = newStr;s1.length = newLength;return true;
}

6.求子串

// 截取子串
void subString(HString& str, int pos, int len) {// 判断参数合法性if (pos < 1 || pos > str.length || len < 0 || pos + len - 1 > str.length) {cout << "截取位置或长度非法" << endl;return;}// 释放原有的内存空间delete[] str.ch;// 更新指针和长度str.ch = new char[len + 1];str.length = len;// 复制子串到新的内存空间for (int i = 0; i < len; ++i) {str.ch[i] = str.ch[pos - 1 + i];}str.ch[len] = '\0';
}

7.完整代码

#include <iostream>using namespace std;typedef struct {char* ch;       // 若是非空串,则按照串长分配存储区,否则ch为NULLint length;     // 串长度
} HString;// 初始化
void initString(HString& str, const char* initial) {if (initial) {int len = 0;const char* p = initial;while (*p != '\0') {len++;p++;}str.length = len;str.ch = new char[len + 1];for (int i = 0; i < len; ++i) {str.ch[i] = initial[i];}str.ch[len] = '\0';} else {str.length = 0;str.ch = nullptr;}
}
// 为字符串赋值为常量字符串chars
void assignStr(HString& str, const char* chars) {if (str.ch) {delete[] str.ch; // 释放原有的空间}int len = 0;const char* p = chars;while (*p != '\0') {len++;p++;}str.length = len;str.ch = new char[len + 1];for (int i = 0; i < len; ++i) {str.ch[i] = chars[i];}str.ch[len] = '\0';
}//求表长
int length(HString &str){return str.length;
}
// 字符串比较
int compare(const HString& s1, const HString& s2) {int i = 0;while (s1.ch[i] != '\0' && s2.ch[i] != '\0') {if (s1.ch[i] != s2.ch[i]) {return s1.ch[i] - s2.ch[i];}i++;}return s1.ch[i] - s2.ch[i];
}// 字符串拼接
bool concat(HString& s1, const HString& s2) {// 计算拼接后的长度int newLength = s1.length + s2.length;char* newStr = new char[newLength + 1];// 将s1的字符复制到新串中for (int i = 0; i < s1.length; ++i) {newStr[i] = s1.ch[i];}// 将s2的字符复制到新串中for (int i = 0; i < s2.length; ++i) {newStr[s1.length + i] = s2.ch[i];}newStr[newLength] = '\0';// 释放原来的内存空间delete[] s1.ch;// 更新s1的指针和长度s1.ch = newStr;s1.length = newLength;return true;
}
// 截取子串
void subString(HString& str, int pos, int len) {// 判断参数合法性if (pos < 1 || pos > str.length || len < 0 || pos + len - 1 > str.length) {cout << "截取位置或长度非法" << endl;return;}// 释放原有的内存空间delete[] str.ch;// 更新指针和长度str.ch = new char[len + 1];str.length = len;// 复制子串到新的内存空间for (int i = 0; i < len; ++i) {str.ch[i] = str.ch[pos - 1 + i];}str.ch[len] = '\0';
}// 释放内存
void destroyString(HString& str) {if (str.ch) {delete[] str.ch;}str.length = 0;
}
// 插入
bool insertString(HString& s, int pos, const HString& T) {// 插入位置非法if (pos < 1 || pos > s.length + 1) {cout << "插入位置非法" << endl;return false;}// 计算插入后新串的长度int newLength = s.length + T.length;char* newStr = new char[newLength + 1];// 将s的前pos-1个字符复制到新串中for (int i = 0; i < pos - 1; ++i) {newStr[i] = s.ch[i];}// 将串T复制到新串中for (int i = 0; i < T.length; ++i) {newStr[pos - 1 + i] = T.ch[i];}// 将s中pos位置后的字符复制到新串中for (int i = pos - 1; i < s.length; ++i) {newStr[T.length + i] = s.ch[i];}newStr[newLength] = '\0';// 释放原来的内存空间delete[] s.ch;// 更新s的指针和长度s.ch = newStr;s.length = newLength;return true;
}// 打印字符串
void printString(const HString& str) {cout << str.ch << endl;
}int main() {HString s, T;initString(s, "Hello ");initString(T, "world!");cout << "插入前:";printString(s);insertString(s, 6, T); // 在位置6插入字符串Tcout << "插入后:";printString(s);// 测试赋值函数char chars[] = "Welcome";assignStr(s, chars);cout << "赋值后:";printString(s);// 测试字符串比较HString s1, s2;initString(s1, "abc");initString(s2, "abd");cout << "字符串比较结果:" << compare(s1, s2) << endl;// 测试字符串拼接concat(s1, s2);cout << "字符串拼接结果:";printString(s1);// 测试子串截取subString(s1, 2, 2);cout << "截取子串结果:";printString(s1);// 释放内存destroyString(s);destroyString(T);destroyString(s1);destroyString(s2);return 0;
}

3.串的块链存储表示

        串的块链存储表示可以通过链表来实现,每个节点表示一个字符,串中的字符按顺序连接在一起。

1.定义

// 定义节点结构体
struct CharNode {char data;          // 数据域,存储字符CharNode* next;     // 指针域,指向下一个节点
};// 定义串结构体
struct HString {CharNode* head;     // 头指针,指向串的第一个节点int length;         // 串的长度
};

2.生成串

        生成串的时候,串的头结点置空,长度为0。

// 初始化串
void initString(HString& str) {str.head = nullptr;str.length = 0;
}

3.生成串

// 生成串
void assignStr(HString& str, const char* chars) {initString(str); // 清空原有串const char* p = chars;CharNode* prev = nullptr; // 前一个节点指针while (*p != '\0') {CharNode* newNode = new CharNode; // 创建新节点newNode->data = *p;newNode->next = nullptr;if (!str.head) {str.head = newNode; // 如果头指针为空,将新节点设置为头节点} else {prev->next = newNode; // 否则将新节点链接到上一个节点}prev = newNode; // 更新前一个节点指针str.length++; // 更新串长度p++;}
}

4.完整代码

#include <iostream>using namespace std;// 定义节点结构体
struct CharNode {char data;          // 数据域,存储字符CharNode* next;     // 指针域,指向下一个节点
};// 定义串结构体
struct HString {CharNode* head;     // 头指针,指向串的第一个节点int length;         // 串的长度
};// 初始化串
void initString(HString& str) {str.head = nullptr;str.length = 0;
}// 生成串
void assignStr(HString& str, const char* chars) {initString(str); // 清空原有串const char* p = chars;CharNode* prev = nullptr; // 前一个节点指针while (*p != '\0') {CharNode* newNode = new CharNode; // 创建新节点newNode->data = *p;newNode->next = nullptr;if (!str.head) {str.head = newNode; // 如果头指针为空,将新节点设置为头节点} else {prev->next = newNode; // 否则将新节点链接到上一个节点}prev = newNode; // 更新前一个节点指针str.length++; // 更新串长度p++;}
}// 打印串
void printString(const HString& str) {CharNode* p = str.head;while (p) {cout << p->data;p = p->next;}cout << endl;
}// 释放内存
void destroyString(HString& str) {CharNode* p = str.head;while (p) {CharNode* temp = p;p = p->next;delete temp; // 释放节点内存}str.head = nullptr;str.length = 0;
}int main() {HString str;assignStr(str, "Hello, world!");cout << "打印串: ";printString(str);destroyString(str);return 0;
}

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

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

相关文章

revit\navisworks各种安装问题

You have entered a nonvalid serial number &#xff0c;怎么都不给你一个机会输出序列号&#xff0c;怎么办&#xff1f; step1: C:\Program Files (x86)\Common Files\Autodesk Shared\AdskLicensing目录下找到uninstall.exe&#xff0c;右键管理员模式运行&#xff0c;会…

新媒体运营-----短视频运营-----PR视频剪辑----文本与图形

新媒体运营-----短视频运营-----PR视频剪辑-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/138079659 文章目录 1. 文本样式2. 文字与图形样式的配合3. 图形模版4. AE创建动画模版导出到PR5. PS图稿导出至PR创建运动图形6. PR图…

mysql报错 之 报错:Duplicate entry 字段 for key ‘表名.idx_字段’

一、问题操作 Mysql 进行insert 操作&#xff0c;报错&#xff1a;Duplicate entry 字段 for key ‘表名.idx_字段’ 原因解析&#xff1a;idx 是做的索引键&#xff0c;是具有唯一性 二、问题原因&#xff08;三种情况&#xff0c;当前我遇到的情况是第一种&#xff09; 当…

文件包含漏洞基础

php 中的文件包含函数&#xff1a; incude &#xff1a; require incude_once require_once 为了减少重复性代码的编写&#xff1b; 任意后缀的文件当中只要存在 php 代码就会被当作 php 执行&#xff1b; 本质&#xff1a;由于包含的文件不可控&#xff0c;导致文件包含…

07节-51单片机-矩阵键盘

文章目录 1矩阵键盘原理2.扫描的概念3.弱上拉4.实战-实现矩阵键盘对应按钮按下显示对应值4.1配置代码模板 5.键盘锁 1矩阵键盘原理 在键盘中按键数量较多时&#xff0c;为了减少I/O口的占用&#xff0c;通常将按键排列成矩阵形式 采用逐行或逐列的“扫描”&#xff0c;就可以读…

深浅拷贝及其现代写法

#include<iostream> using namespace std; class Person { public://默认构造Person(){cout << "Person()" << endl;}//有参构造函数Person(int age,int height){m_age age;m_height new int(height);cout << "Person(int age, int h…

【ZYNQ】zynq启动模式及程序固化

一、前言 由于zynq含有arm cpu ,其启动模式由ps主导&#xff0c;与纯逻辑的fpga不相同&#xff0c;此处做一个记录。 二、zynq启动模式 关于zynq的启动模式详细内容可以参考官方文档&#xff1a;ug585-Zynq 7000 SoC Technical Reference Manual&#xff0c;第六章。 2.1 启…

12(第十一章,数据仓库和商务智能)

目录 概述 目标和原则 基本概念 商务智能 数据仓库 数据仓库建设方法 数据仓库架构组件 加载处理方式 1、历史数据 2、批量变更数据捕获&#xff08;CDC&#xff09; 3、准实时和实时数据加载 活动 运营分析应用 方法 数据仓库构建 架构演进 数据处理过程 数…

Nacos分布式配置中心和服务注册中心

分布式配置中心 Nacos Spring Cloud 快速开始 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&qu…

第一届长城杯半决赛wp和AWD笔记

目录 AWD 渗透 cfs 单节点1 AWD笔记 AWD工具 文件比较工具 Web漏洞扫描工具 waf工具 代码审计工具 批量网站备份文件泄露扫描工具 cms通杀漏洞的利用 通杀脚本和批量提交flag脚本 防御流程 攻击流程 注意 AWD 解题思路] 首先就是fscan快速扫描对应C段&#xf…

【重学C语言】十、指针入门

【重学C语言】十、指针入门 地址和存储单元内存四区地址存储单元存储区首地址地址和存储单元的关系怎么获得地址指针定义指针初始化指针使用指针修改指针指向的值`类型 *` 的含义取地址符和解引用符取地址符(&)解引用符(*)

pipeline流水线语法

Pipeline 常用语法_pipline语法-CSDN博客 jenkins-pipeline语法详解 最新最全 &#xff08;声明式Pipeline和脚本式&#xff09;_pipeline脚本脚本式-CSDN博客

递归基础-汉诺塔(仅用于复习和交流)

观察上图发现&#xff0c;从N2开始图形都按一定的规律步骤进行位置移动&#xff0c;这个观点是否认同&#xff1f; 那再看N2时&#xff0c;为了方便大家理解&#xff0c;将两个板定义为板1&#xff08;小&#xff09;&#xff0c;板2&#xff08;大&#xff09;&#xff0c;最上…

leaftjs+turfjs+idw纯前端实现等值面绘图

最近有个绘制等值面图的需求。我们一般的实现路径是&#xff1a; 1.后台绘图&#xff0c;用surfer绘制好&#xff0c;给前端调用叠加到地图。 2.后台用python绘图&#xff0c;绘制好给前端调用&#xff0c;叠加到地图。 3.后台进行插值计算、地图裁剪、最终生成geojson文件或…

【Webgl_glslThreejs】搬运分享shader_飘落心形

来源网站 https://www.shadertoy.com/view/4sccWr效果预览 代码演示 将shadertory上的代码转成了threejs可以直接用的代码&#xff0c;引入文件的material&#xff0c;并在创建mesh或已有物体上使用material即可&#xff0c;使用时请注意uv对齐。 import { DoubleSide, Shad…

Facebook的魅力魔法:探访数字社交的奇妙世界

1. 社交媒体的演变与Facebook的角色 在数字化时代&#xff0c;社交媒体已经成为我们日常生活中不可或缺的一部分。而在众多的社交媒体平台中&#xff0c;Facebook 以其深厚的历史和广泛的影响力&#xff0c;成为了全球数亿用户沟通、分享和互动的主要场所。从其初创之时起&…

微软发布!提示工程进化为位置工程,有效提升RAG与上下文学习

别再光顾着优化提示工程啦&#xff01;微软最近推出位置工程研究思路&#xff0c;只需调整token的索引位置&#xff0c;而不修改文本本身&#xff0c;就能显著提高任务性能。 提示工程通过添加、替换或删除段落和句子改变提示&#xff0c;调整语义信息&#xff0c;激发LLMs的推…

javaWeb项目-校园志愿者管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、SpringBoot框架 …

docker容器搭建chatglm2-6b

服务器环境&#xff1a; 显卡驱动&#xff1a;Driver Version: 530.30.02 CUDA版本&#xff1a;CUDA Version: 12.1 显卡&#xff1a;NVIDIA GeForce RTX 3090共4张 注意&#xff1a;最好把显卡驱动升级到530&#xff0c;CUDA版本之前使用11.7有问题。 docker-compose.yml…

运动学与动力学基础知识导读

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言一、运动学基础--坐标转换&#xff08;1&#xff09;Eigen的使用及常用的位姿变换实现&#xff08;2&#xff09;小车底盘运动…