串概述
串也是一种特殊的线性表。与线性表相比,串的数据元素及其逻辑关系和线性表完全相同,不同点在于:
- 线性表的数据元素可以是任意类型,串只能是由字符序列组成。
- 线性表一次性操作一个数据元素。而串一次操作若干个数据元素。
串和字符是两个不同的概念。例如:字符’a’只需要存储字符’a’即可。但是串"a",不仅需要存储字符’a’,还需要存放’a’的长度。
串的抽象数据类型
- 数据集合
串的数据集合可以表示为字符序列:s0,s1,s2,s3…,sn-1。每个数据类型均为字符型。
- 操作集合
- 取字符charAt(int index)
- 求长度length()
- 比较compareTo(<?> anotherString)
- 取子串substring(int beginIndex,int endIndex)
- 连接concat(<?> str)
- 插入子串(<?> str,int pos)
- 删除字串delete(int beginIndex,int endIndex)
- 输出串值myPrint():
- 查找字串index(<?> subStr,int start)
- 两种功能的串类
- 任何对串的操作不可改变原串的值。即Java中的String类
- 任何对串的操作都可改变原串的值。即Java中的StringBuffer类
串的存储结构
- 顺序存储结构
串的顺序存储结构,就是用由字符型数组存放串的所有字符。
- 链式存储结构
串的链式存储结构就是把串值分别存储在构成链表的各个节点的数据属性上。
自定义的两种串
采用顺序存储结构设计串类。根据串的特点,设计两种类:可变长度的MyStringBuffer类和不可变长度的MyString类。
- MyString
public class MyString {
/*
* 1. value:存放字符数组
* 2. count:字符个数
*/
private char[] value;
private int count;
//不可变长串类,在每次改变元素时,都需要重新获取内存空间,存放数组
static void arrayCopy(char[] src,int srcPos,char[] dst,int dstPos,int length) {
/*
* 1. src:需要存放的串对象
* 2. srcPos:需要存放的串对象,从哪个位置开始
* 3. dst:本类串对象
* 4. dstPos:本类串对象起始下标
* 5. length:新串的长度
*/
if(src.length-srcPos<length || dst.length - dstPos<length) {
//需要存放的串长度不能大于总长度
throw new StringIndexOutOfBoundsException(length);
}
for(int i = 0;i<length;i++) {
dst[dstPos++] = src[srcPos++];
}
}
//默认构造函数,创建空串对象
public MyString() {
value = new char[0];
count = 0;
}
//构造函数,可以将一个字符数组从指定位置到长度存放入串
public MyString(char[] value,int offset,int count) {
/*
* 1. value:需要存放的字符数组
* 2. offset:从哪个位置开始存
* 3. count:需要存几个字符
*/
if(offset<0) {
//位置不能小于0
throw new StringIndexOutOfBoundsException(offset);
}
if(count<0) {
//存放字符的个数不能小于0
throw new StringIndexOutOfBoundsException(count);
}
if(offset>value.length - count) {
//起始位置加上个数不能大于总长度
throw new StringIndexOutOfBoundsException(offset + count);
}
//提供一个合适的空间存放
this.value = new char[count];
//更新字符个数
this.count = count;
arrayCopy(value,offset,this.value,0,count);
}
//构造函数,将一个字符数组完全存放
public MyString(char[] value) {
this.count = value.length;
this.value = new char[count];
arrayCopy(value,0,this.value,0,count);
}
//构造函数,将自一个字符串转换为字符数组,方便操作
public MyString(String str) {
//可以直接使用类似 MyString str = new MyString("zhangsan");
//转换成字符数组
char[] chararray = str.toCharArray();
//指向转换成字符数组的对象
this.value = chararray;
this.count = chararray.length;
}
//取字符
public char charAt(int index) {
//位置不能大于总长度,也不能小于0
if(index<0 || index>=count) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
//取串的长度
public int length() {
return count;
}
//比较两个串谁大谁小
public int compareTo(MyString anotherString) {
/*
* 1. 当前对象大于anotherString返回正整数
* 2.当前对象等于anotherString返回0
* 3.当前对象小于anotherString返回负整数
*/
int len1 = count;
int len2 = anotherString.count;
//取最短的那个长度
int n = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
//从位置零开始比较
int k = 0;
while(k<n) {
char c1 = v1[k];
char c2 = v2[k];
if(c1!=c2) {
return c1-c2;
}
k++;
}
//如果上面都没有返回值,那就表示前面的值都相同,谁长谁就大
return len1-len2;
}
//取指定位置的子串
public MyString substring(int beginIndex,int endIndex) {
//起始位置不能小于零
if(beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
//终点位置不能大于等于长度
if(endIndex > count) {
throw new StringIndexOutOfBoundsException(endIndex);
}
//起始位置不能大于终点位置
if(beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
}
//取全部字串或者部分字串
return ((beginIndex == 0)&&(endIndex == count)) ?
this:new MyString(value,beginIndex,endIndex-beginIndex);
}
//取指定长度的字串
public MyString substring(int beginIndex) {
return substring(beginIndex,count);
}
//返回字符数组
public char[] toArray() {
char[] buf = new char[count];
arrayCopy(value,0,buf,0,count);
return buf;
}
//连接字符串
public MyString concat(MyString str) {
int anotherLen = str.length();
char[] strarray = str.toArray();
if(anotherLen == 0) {
return this;
}
//新建一个可以容纳原字符串和需要连接的字符串
char buf[] = new char[count+anotherLen];
//将原字符串的元素赋值到新字符串对象里
arrayCopy(value,0,buf,0,count);
//将需要连接的字符串strarray,赋值到新的字符串里。
arrayCopy(strarray,0,buf,count,anotherLen);
//返回连接的字符串
return new MyString(buf);
}
//插入字符串
public MyString insert(MyString str,int pos) {
/*
* 1 str:需要插入的字符串
* 2 pos:从哪个位置开始插入
*/
//插入的位置不能小于零,不能大于总长度
if(pos < 0 || pos > count) {
throw new StringIndexOutOfBoundsException(pos);
}
//插入位置不等于零的情况下
if(pos != 0 ) {
MyString str1 = this.substring(0,pos);//取出pos位置前的子串
MyString str2 = this.substring(pos);//取出包含pos位置的剩余子串
MyString res1 = str1.concat(str);//连接str1和str
MyString res2 = res1.concat(str2);
return res2;
}else {
//等于零,则用插入的字符串连接主串
return str.concat(this);
}
}
//删除子串
public MyString delete(int beginIndex,int endIndex) {
//起始位不能小于零
if(beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
//起始位置不能大于总长度
if(beginIndex > count) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
//起始位置不能大于终点位置
if(beginIndex > endIndex) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
//删除全部字符的情况下
if(beginIndex == 0 && endIndex == count) {
return new MyString();//返回空字符串
}else {
MyString str1 = this.substring(0,beginIndex);//取起始位置之前的字符
MyString str2 = this.substring(endIndex);//取终点位置及其之后的字符
return str1.concat(str2);//返回删除后的字符串
}
}
//将字符序列输出
public void myPrint() {
for(int i = 0;i<count;i++) {
System.out.print(value[i]);
}
System.out.println();
}
}
- MyStringBuffer
public class MyStringBuffer {
private char[] value;
private int count;
static void arrayCopy(char[] src,int srcPos,char[] dst,int dstPos,int length) {
/*
* 1. src:需要存放的串对象
* 2. srcPos:需要存放的串对象,从哪个位置开始
* 3. dst:本类串对象
* 4. dstPos:本类串对象起始下标
* 5. length:新串的长度
*/
if(src.length-srcPos<length || dst.length - dstPos<length) {
//需要存放的串长度不能大于总长度
throw new StringIndexOutOfBoundsException(length);
}
for(int i = 0;i<length;i++) {
dst[dstPos++] = src[srcPos++];
}
}
//重新申请内存空间
private void expandCapacity(int newCapacity) {
char[] newValue = new char[newCapacity];
arrayCopy(value,0,newValue,0,count);
value = newValue;
}
//构造函数
public MyStringBuffer(String str) {
char[] chararray = str.toCharArray();
value = chararray;
count = chararray.length;
}
//连接字符串
public MyStringBuffer concat(MyStringBuffer str) {
int anotherLen = str.length();
if(anotherLen == 0) {
return this;
}
expandCapacity(count + anotherLen);
arrayCopy(str.toArray(),0,this.toArray(),0,this.length());
count = count + anotherLen;
return this;
}
//返回字符数组
public char[] toArray() {
return value;
}
//返回字符串长度
public int length() {
return count;
}
//输出字符序列
public void myPrint() {
for(int i = 0;i<count;i++) {
System.out.println(value[i]);
}
}
//其他方法取字串等与MyString类似
}
串的模式匹配算法
串的模式匹配算法就是串的查找操作。当模式匹配成功时,返回子串的第一个字符在主串首次出现的位置,失败则返回-1。
Brute-Force算法
暴力匹配算法,一个一个找。首先匹配主串第一个字符和模式串第一个字符是否相等,若相等则继续匹配第二个,若不相等,重新拿主串的第二个字符与模式串的第一个字符匹配。以此类推,直到最后一个字符。
//匹配子串
public int indexOf(MyString substr) {
/*
* 1. i:主串的位置
* 2. j:子串的位置
* 3. v:返回的值,是否匹配
*/
int i = 0,j = 0,v;
//位置不能超过主串的长度,也不能超过子串的长度
while(i<this.length() && j<substr.length()) {
//如果相等,则继续比较下一个
if(this.charAt(i) == substr.charAt(j)) {
i++;
j++;
}else {
//如果不相等,主串返回到下一个位置,子串返回到0
i = i-j+1;
j = 0;
}
}
//如果子串的位置等于子串的长度,则表示匹配成功
if(j == substr.length()) {
//获取子串第一个字符在主串首次出现的位置
v = i-substr.length();
}else {
v = -1;
}
return v;
}
@ syl 2021/08/21 19:26 周六 晴 回宿舍煮碗面看电影