数据结构之串
来源:互联网 发布:网络赛车赌博是骗局吗 编辑:程序博客网 时间:2024/05/16 19:02
基本概念
串(string)是由零个或多个字符组成的有限序列,又名叫字符串。形如s="a,b,c.."。ai(1 ≤ i ≤ n)可以是字母、数字或其他字符,i就是该字符在串中位置。串中的字符数目n称为串的长度,定义中谈到“有限”是指长度n是一个有限的数值。两个字符的串称为空串(null string),它的长度为零,可以直接用双引号“”表示。所谓序列,说明串的相邻字符之间具有前驱后继的关系。
空格串,是只包含空格的串。注意它和空串的区别,空格串是有内容有长度的,而且可以不止一个空格。
子串与主串,串中任意个数的连续字符组成的子序列称为该串的字串,相应地,包含子串的串称为主串。
子串在主串中的位置,就是子串第一个字符在主串中的序号。
串的存储结构
从串的存储方式来区分,主要分为堆式存储结构、链式存储结构和顺序存储结构。
顺序存储
串的静态存储结构即串的顺序存储结构,在大多数的计算机系统中,一个字占用多个字节,而一个字符只占用一个字节,所以为了节省空间,就采用紧缩格式存储。
类型定义:
typedef struct { char data[maxsize]; int len; }stype;
下面是串的顺序结构的常用操作:
package bind;class SqString {final int MaxSize = 100;char data[];int length;public SqString() {data = new char[MaxSize];length = 0;} // 字符串赋值 char[]public void StrAssign(char cstr[]){int i;for (i = 0; cstr[i] != '/0'; i++)data[i] = cstr[i];length = i;} // 将串s复制给当前串SqStingpublic void StrCopy(SqString s) {int i;for (i = 0; i < s.length; i++)data[i] = s.data[i];length = i;} // 判断串是否相等public boolean StrEqual(SqString s) {boolean b = true;if (s.length != length)b = false;elsefor (int i = 0; i < length; i++)if (s.data[i] != data[i]) {b = false;break;}return b;} // 求串长public int StrLength() {return length;} // 当前串与串s连接public void Concat(SqString s) {SqString str = new SqString();int i;str.length = length + s.length;for (i = 0; i < length; i++)str.data[i] = data[i];for (i = 0; i < s.length; i++)str.data[length + i] = s.data[i];this.StrCopy(str);} // 返回串从第i开始之后j个字符组成的子串public SqString SubStr(int i, int j) {SqString s = new SqString();if (i <= 0 || i > length || j < 0 || i + j - 1 > length)return s;for (int k = i - 1; k < i + j - 1; k++) {s.data[s.length++] = data[k];}return s;} // 在当前串在第i位插入S2public void InsStr(int i, SqString s2) {int j;SqString str = new SqString();if (i <= 0 || i > s2.length + 1) {System.out.println("Error:out of index! ");return;}for (j = 0; j < i - 1; j++)str.data[str.length++] = data[j];for (j = 0; j < s2.length; j++)str.data[str.length++] = s2.data[j];for (j = i - 1; j < length; j++)str.data[str.length++] = data[j];this.StrCopy(str);}// 删除第i位开始长度为j的子串public void DelStr(int i, int j) {int k;SqString s = new SqString();if (i <= 0 || i > length || i + j > length + 1) {System.out.println("Error: out of index!");return;}for (k = 0; k < i - 1; k++)s.data[s.length++] = data[k];for (k = i + j - 1; k < length; k++)s.data[s.length++] = data[k];this.StrCopy(s);}// 用串t代替当前串中第i位开始的j个字符构成的子串public void RepStr(int i, int j, SqString t) {this.DelStr(i, j);this.InsStr(i, t);}public void DispStr() {int i;if (length > 0) {for (i = 0; i < length; i++)System.out.print(data[i] + " ");System.out.println();}}}public class MySqString {public static void main(String args[]){char cstr1[] ={ 'a', 'b', 'c', 'd', 'e', 'f', 'g', '/0' };char cstr2[] ={ '1', '2', '3', '4', '5', '6', '7', '/0' };SqString s1 = new SqString();s1.StrAssign(cstr1);System.out.print("s1:");s1.DispStr();SqString s2 = new SqString();s2.StrAssign(cstr2);System.out.print("s2:");s2.DispStr();SqString s3 = new SqString();s3.StrCopy(s1);System.out.print("s3=s1/ns3:");s3.DispStr();System.out.println("s3.length=" + s3.length);s3.Concat(s2);System.out.print("s3=s3+s2:");s3.DispStr();System.out.println("s3.length=" + s3.length);SqString s4 = new SqString();System.out.println("返回串3从第1开始之后2个字符组成的子串");s4 = s3.SubStr(1, 2);s4.DispStr();System.out.println("在当前串在第i位插入S2");s4.InsStr(2, s2);s4.DispStr();System.out.println("删除第2位开始长度为1的子串");s4.DelStr(2, 1);s4.DispStr();System.out.println("用串s2代替当前串中第2位开始的3个字符构成的子串");s4.RepStr(2, 3, s2);s4.DispStr();}}}
堆式存储
串的堆式存储结构类似于线性表的顺序存储结构,以一组地址连续的存储单元存放串值字符序列,其存储空间是在程序执行过程中动态分配的,而不是静态分配的。在C和C++语言中 ,提供一个称之为“堆”的共享空间,可以在程序运行过程中,系统利用函数malloc( )和free( )动态地申请或释放一块连续空间。由于在C和C++语言中可以用指针对数组进行访问和操作,在串的存储和操作上也可以充分利用上述特性。(这种在c和c++用的比较多,不做讲解)
堆式存储结构定义:
Typedef struct { char *ch; //指针域,指向存放串值的存储空间基址 int length; // 整型域:存放串长 }HString;
链式存储
这种比较简单,如我们常见的链表。
表示:
//串的链式存储表示typedef struct{ char ch LStrNode *next;}LStrNode,*LString;链式存储简单操作
#include <stdio.h> #include <stdlib.h> #include <string.h> /*字符结点*/ struct Node{ char c; struct Node * next; }; typedef struct Node * PNode; /*函数声明*/ PNode createNullLinkedString(void); int isNullLinkedString(PNode pstr); int initLinkedString(PNode pstr); int insertBeforeElement(PNode pstr); int dtempteElement(PNode pstr); void printString(PNode pstr); /*----------创建一个空的字符串---------------*/ PNode createNullLinkedString(){ PNode pstr=(PNode)malloc(sizeof(struct Node)); if(pstr != NULL){ pstr->next=NULL; pstr->c='-'; printf("创建成功!\n"); } else{ pstr=NULL; printf("创建失败!\n"); } return pstr; } /*-----------判断串是否为空----------------*/ int isNullLinkedString(PNode pstr){ if(pstr->next == NULL){ printf("空串!\n"); return 1; } else{ printf("非空串!\n"); return 0; } } /*----------初始化一个空字符串-------------*/ int initLinkedString(PNode pstr){ PNode cur = pstr; if(isNullLinkedString(pstr)){ while(1){ char c; printf("输入字符(@结束)>>>"); scanf("%c",&c); getchar(); if(c =='@'){ break; } // 申请要插入的结点 PNode temp = (PNode)malloc(sizeof(Node)); temp->next = NULL; temp->c = c; if(pstr->next == NULL){ pstr->next = temp; cur = pstr; }else{ cur->next->next = temp; cur = cur->next; } } printf("初始化完毕!\n"); printString(pstr); return 1; }else{ printf("初始化失败!\n"); return 0; } } /*-----------在指定字符之前插入字符---------------*/ int insertBeforeElement(PNode pstr,char c,char s){ PNode cur = pstr; while(cur->next != NULL){ // 找到指定字符 if(cur->next->c == c) { PNode temp = (PNode)malloc(sizeof(PNode)); temp->c = s; temp->next = cur->next; cur->next = temp; printf("插入成功!\n"); printString(pstr); return 1; } cur = cur->next; } printf("未找到指定字符,插入失败\n"); return 0; } /*---------删除指定元素----------*/ int deleteElement(PNode pstr,char c){ PNode cur = pstr; while(cur->next != NULL){ // 找到指定字符 if(cur->next->c == c){ cur->next = cur->next->next; printf("删除成功!\n"); printString(pstr); return 1; } cur = cur->next; } printf("删除失败!\n"); return 0; } /*----------打印当前字符串----------*/ void printString(PNode pstr){ PNode cur = pstr; printf("["); while(cur->next != NULL){ printf(" %c ",cur->next->c); cur = cur->next; } printf("]"); } /*-------主控-------*/ int main(void){ PNode pstr= NULL; printf("\n--------字符串的基本操作---------\n"); char c,s,ch; int input; while(1){ printf("\n 1_创建空串\n 2_判断当前是否为空\n 3_初始化空串\n"); printf(" 4_在指定字符之前插入字符\n 5_删除指定字符\n 6_打印串\n"); printf("\n请选择>>>"); scanf("%d",&input); scanf("%c",&ch); switch(input){ case 1 : pstr = createNullLinkedString(); break; case 2 : isNullLinkedString(pstr); break; case 3 : initLinkedString(pstr); break; case 4 : printf("请指定字符>>>"); scanf("%c",&c); getchar(); printf("请输入要插入的字符>>>"); scanf("%c",&s); insertBeforeElement(pstr,c,s); break; case 5 : printf("请指定字符>>>"); scanf("%c",&s); deleteElement(pstr,s); break; case 6 : printString(pstr); break; default: printf("输入错误,请重新输入"); break; } } return 0; }
串的比较
对于两个串不相等时,如何判定它们的大小呢。我们这样定义:
给定两个串:s = “a1a2…….an”,t = “b1,b2……bm”,当满足以下条件之一时,s < t。
存在某个k < min(m,n),使得ai = bi(i = 1, 2 , ….., k -1),ak < bk
假如当s = “happen” ,t = “happy”,因为两串的前4个字符均相同,而两串的第5个字母(k值),字母e的ASCII码时101,而字母y的ASCII码时121,显然e < y,所以s < t。
n < m,且ai = bi (i = 1, 2, …… , n)
例如当 s = “hap”,t = “happy”,就有s < t,因为t比s多处了两个字母。
给定两个串:s = “a1a2…….an”,t = “b1,b2……bm”,当满足以下条件之一时,s < t。
存在某个k < min(m,n),使得ai = bi(i = 1, 2 , ….., k -1),ak < bk
假如当s = “happen” ,t = “happy”,因为两串的前4个字符均相同,而两串的第5个字母(k值),字母e的ASCII码时101,而字母y的ASCII码时121,显然e < y,所以s < t。
n < m,且ai = bi (i = 1, 2, …… , n)
例如当 s = “hap”,t = “happy”,就有s < t,因为t比s多处了两个字母。
串的抽象数据类型
串的逻辑结构和线性表很相似,不同之处在于串针对的是字符集,也就是串中的元素都是字符。因此,对于串的基本操作与线性表所有很大差别的。线性表更关注的是单个元素的操作,但串中更多的是查找子串的位置、得到指定位置的子串、替换子串等操作。
例如串的add
Data 串中元素仅由一个字符组成,相邻元素具有前驱和后继关系。Operation StrAssign(T,*chars):生成一个其值等于字符串常量chars的串T。 strCopy(T,S):串S存在,由串S复制得串T ClearString(S):串S存在,将串清空 StringEmpty(S):若串S为空,返回true,否则false StrLength(S):返回串S的元素个数,即串的长度 StrCompare(S,T):若S>T,返回值大于0;若S=T,返回0,若S<T,返回值0 Concat(T,S1,S2):用串T返回S1和S2连接而成的新串 SubString(Sub,S,pos,len):串S存在,1≤pos≤StrLength(S) 且0≤len≤StrLength(S)-pos+1,用Sub 返回值S的第pos个字符起长度为len的子串 Index(S,T,pos):串S和串T存在,T是非空串1≤ pos ≤StrLength(S) 若主串S中存在和串T相同的子串,则返回它在主串S中 第pos个字符后第一个出现的位置,否则返回0 Replace(S,T,V):串S、T和V存在,T是非空串。用V替换主串中出现的 所有与T相等的不重叠的子串 StrInset(S,pos,T):串S和T存在,1 ≤ pos ≤StrLength(S) + 1 在串S的第pos个字符之前插入串T StrDelete(S,pos,len):串S存在,1≤pos≤StrLengthS-len+1 从串S中删除第pos个字符起长度为len的子串。串的查找
int Index(String S,String T,int pos){ int n,m,i; String sub; if(pos > 0) { n = StrLength(S);//得到主串S的长度 m = StrLength(T); i = pos; while(i <= n-m+1) { SubString(sub,S,i,m);//取主串第i个位置,m长度的子串 if(StrCompare(sub,T) != 0)//如果两串不相等 i++; else //如果相等 return i; //返回i值 } } return 0;//若无子串与T相等,返回0}
串的模式匹配
子串的定位操作通常称为串的模式匹配。应该算是串中最重要的操作之一。假设我们要从下面的主串S = “goodgoogle”中,找到T=”google”这个子串的位置。最简单的朴素想法就是,对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串作大循环,每个字符开头做T长度的小循环,知道匹配完成为止。前面我们已经用串的其他操作实现了模式匹配的算法Index。现在考虑不用其他操作,而是只用基本的数组来实现同样的算法。
int Index(String S,String T,int pos){ int i = 0; int j = 0; int SLen = 0; int TLen = 0; while(s[i] != '\0')//计算S长度 { SLen++; } while(T[i] != '\0')//子串T的长度 { TLen++; } int i = pos; while(i <= SLen - TLen && j <= TLen) { if(S[i] == T[i]) { i++; j++; } else { i = i - j + 1;//匹配失败回到开始的地方的下一个位置 j = 0;//子串回到原来位置 } } if(j > TLen) { return i - TLen; } else return 0;}注:分析一下,最好的情况时什么?那就是一开始就成功,此时时间复杂度为O(1)。 稍差一点,如果像”abcdedgood”中查找”good”。那么时间复杂度就是O(n + m),n为主串长度,m为要匹配的子串长度。根据等概原则,平均是(n + m) / 2次查找,时间复杂度为O(n+m)。那么最坏的情况是什么呢?就是每次不成功的匹配都发生在串T的最后一个字符,比如主串S = 0000000000000000000000000000000000001,而要匹配的子串为T = 0000001。前者是有49个“0”和1个“1”,后者是9个“0”和1个“1”。这样等于前40个位置要循环40 * 10次。知道第41个位置,这里也要匹配10次。
哦,mygod,最坏时间复杂度为O[(n-m+1)*m]。
全是KMP模式匹配算法也是串的操作之一,有兴趣的可以看看之前这个算法的讲解kmp算法
6 3
- 数据结构之串
- 数据结构基础之串
- 数据结构基础之串
- 数据结构之串
- 数据结构复习之串
- 数据结构之串
- 数据结构之------串
- 数据结构之串
- 数据结构之“串”
- 数据结构之串
- 数据结构之串笔记
- 数据结构之顺序串(整理严蔚敏数据结构)
- 数据结构与算法之----串
- 数据结构之——串
- 数据结构之串的应用
- 数据结构之最大子串
- 数据结构实验之串一
- 数据结构实验之串三
- jQuery 移动 DIV 代码
- fastjson序列化hibernate代理和延迟加载对象出现no session异常的解决办法
- 我的百度实习生面试(一)
- spring mvc Service 获取request
- 谈谈Angular指令bindToController的使用(1.4版本后支持)
- 数据结构之串
- HDU_1241 Oil Deposits(dfs)
- AccessibilityService从入门到出轨
- TABLES参数已经过时 TABLES parameters are obsolete! 问题的解决
- java web 通过request获取客户端IP
- 2016.12.10数据库连接发生的一系列错误
- ubuntu12.04设置中文输入法
- opencv3.1 cmake mingw qt
- 帕累托分布图--python学习笔记18