4 串与数组
4.1 串
字符串(串),是由n(>=0)个字符组成的优先序列。从逻辑结构来看,串也是一种特殊的线性表,即串可以看成是每个数据元素仅有一个字符组成的线性表。长度n为0的串称为空串,即空串不包含任何字符。包含一个及以上空白字符串的串称为空白串。需要说明的是,空串和空白串的区别。空串不包含任何字符,长度为0;空白串是由一个或多个字符所组成的字符串,其长度是空白字符的个数。
4.1.1 串的抽象数据类型描述
package adt;
public interface IString {
/**
* 将一个已经存在的串置为空串
*/
public void clear();
/**
* 判断当前串是否为空。
*/
public boolean isEmpty();
/**
* 返回串中的字符个数
*/
public int length();
/**
* 读取并返回串中的第index个字符值。其中,0<=index<=length()-1。
*/
public char charAt(int index);
/**
* 返回值为当前串中从序号begin开始,到序号end-1为止的子串。其中,0<=begin<=length()-1,1<=end<=length()
* 。
*/
public IString substring(int begin, int end);
/**
* 在当前串的第offset个字符之前插入串str。其中,0<=offset<=length()。
*/
public IString insert(int offset, IString str);
/**
* 删除当前串中从序号begin开始到序号end-1为止的子串。其中0<=begin<=length()-1;1<=end<=length()。
*/
public IString delete(int begin, int end);
/**
* 把str串连接到当前串的后面
*/
public IString concat(IString str);
/**
* 将当前串与目标串str进行比较,若当前串大于str,则返回一个正整数; 若当前串等于str,则返回0;若当前串小于str,则返回一个负整数
*/
public int compareTo(IString str);
/**
* 在当前串中从begin位置开始搜索与str相等的子串,所搜索成功,则返回str在当前串中的位置;否则返回-1。
*/
public int indexOf(IString str, int begin);
}
4.1.2 顺序串类的定义
串的顺序存储结构与线性表的顺序存储结构类似,可以采用一组地址连续的存储单元来存储串字符序列。
package sequeue;
import adt.IString;
public class SeqString implements IString {
private char[] strvalue;// 字符数组,存放串值
private int curlen;// 当前串的长度
// 构造方法1,构造一个空串
public SeqString() {
strvalue = new char[0];
curlen = 0;
}
// 构造方法2,以字符串常量构造字符串对象
public SeqString(String str) {
char[] tempchararray = str.toCharArray();
strvalue = tempchararray;
curlen = tempchararray.length;
}
// 构造方法3,以字符数组构造串对象
public SeqString(char[] value) {
strvalue = new char[value.length];
for (int i = 0; i < value.length; i++) {// 复制数组
strvalue[i] = value[i];
}
curlen = value.length;
}
@Override
public void clear() {
this.curlen = 0;
}
@Override
public boolean isEmpty() {
return curlen == 0;
}
@Override
public int length() {
return curlen;
}
@Override
public char charAt(int index) {
if ((index < 0) || (index >= curlen)) {
throw new StringIndexOutOfBoundsException(index);
}
return strvalue[index];
}
// 扩充字符串存储空间容量,参数指定容量
public void allocate(int newCapacity) {
char[] temp = strvalue;
strvalue = new char[newCapacity];// 复制数组
for (int i = 0; i < curlen; i++) {
strvalue[i] = temp[i];
}
}
@Override
// 返回串中序号从begin至end-1的子串
public IString substring(int begin, int end) {
return null;
}
@Override
// 在当前串的第offset个字符之前插入串str
public IString insert(int offset, IString str) {
return null;
}
@Override
// 删除当前串中从序号begin开始到序号end-1为止的子串
public IString delete(int begin, int end) {
return null;
}
@Override
// 添加指定串str到当前串尾
public IString concat(IString str) {
return null;
}
@Override
// 将当前串与目标串str进行比较
public int compareTo(IString str) {
return 0;
}
@Override
// 子串定位
public int indexOf(IString str, int begin) {
return 0;
}
}
4.1.3 串的基本操作实现
1. 求子串操作
求子串操作的功能是,返回当前串中序号从begin至end-1的子串。begin的范围是0<=begin<=length()-1;end的范围是1<=end<=length()。思路:
1)检查begin和end参数的合法性
2)若要截取整个串,则返回原串;否则截取从begin至end-1之间的子串
// 返回串中序号从begin至end-1的子串
public IString substring(int begin, int end) {
if (begin < 0) {
throw new StringIndexOutOfBoundsException("起始位置不能小于0");
}
if (end > curlen) {
throw new StringIndexOutOfBoundsException("结束位置不能大于串的当前长度");
}
if (begin > end) {
throw new StringIndexOutOfBoundsException("开始位置不能大于结束位置");
}
if (begin == 0 && end == curlen) {
return this;
} else {
char[] buffer = new char[end - begin];
for (int i = 0; i < buffer.length; i++) {
buffer[i] = this.strvalue[i + begin];
}
return new SeqString(buffer);
}
}
2. 串的插入操作
串的插入操作是指,在当前串中第offset个字符之前插入串str,并返回结果串对象。其中,参数offset的有效范围是0<=offset<=length()。思路:
1)检查插入位置的合法范围。
2)若插入时存储空间不足,则调用allocate(newCount),重新分配存储空间。
3)将strvalue中从offset开始的字符向后移动len个位置(len为待插入串str的长度)。
4)将str串插入到strvalue中从offset开始的位置。
5)改变串的长度
public IString insert(int offset, IString str) {
if ((offset < 0) || (offset > curlen)) {
throw new StringIndexOutOfBoundsException("插入位置不合法");
}
int len = str.length();
int newCount = curlen + len;
if (newCount > strvalue.length) {
allocate(newCount);// 插入存储空间不足,需扩充容量
}
for (int i = curlen - 1; i >= offset; i++) {
strvalue[len + i] = strvalue[i];// 从offset开始向后移动len个字符
}
for (int i = 0; i < len; i++) {
strvalue[offset + i] = str.charAt(i);// 复制串str
}
this.curlen = newCount;//改变串长度
return this;
}
3. 串的删除操作
串的删除操作是指在当前串中删除从start到end-1之间的子串,并返回当前串对象。参数start和end的取值范围分别是0<=begin<=length()-1和1<=end<=length()。思路:
1)检查参数的合法性
2)将strvalue中从end开始到尾串的子串向前移动到从begin开始的位置。
3)当前串长度减去end-begin。
public IString delete(int begin, int end) {
if (begin < 0) {
throw new StringIndexOutOfBoundsException("起始位置不能小于0");
}
if (end > curlen) {
throw new StringIndexOutOfBoundsException("结束位置不能大于串的当前长度");
}
if (begin > end) {
throw new StringIndexOutOfBoundsException("开始位置不能大于结束位置");
}
for (int i = 0; i < end - begin; i++) {
strvalue[begin + i] = strvalue[end + i];// 从end开始至串尾的子串向前移动到从begin开始的位置
}
curlen = curlen - (end - begin);// 当前串的长度将去(end-begin)
return this;
}
4. 串的比较操作
串的比较操作是将当前串与参数str指定的串进行比较,若当前串的值大于str的串的值,则返回一个正整数; 若当前串的值等于str的串值,则返回0;若当前串的值小于str的串值,则返回一个负整数。思路:
1)求出当前串与待比较串的长度,并把较小值赋值到n
2)从下标0到n-1依次取出两个串中对应的字符进行比较,若不等,则返回 第一个不相等的字符的数值差
3)若下标从0到n-1对应的字符均相等,则返回两个串长度的差
public int compareTo(SeqString str) {
int len1 = this.curlen;
int len2 = str.length();// 求出当前串与待比较串的长度
int n = Math.min(len1, len2);// 把较小值赋值到n
char[] s1 = this.strvalue;
char[] s2 = str.strvalue;
int k = 0;
while (k < n) {
char ch1 = s1[k];
char ch2 = s2[k];
if (ch1 != ch2) {
return ch1 - ch2; // 返回第一个不相等字符的数值差
}
k++;
}
return len1 - len2;// 返回两个串长度的差
}
5. 串的模式匹配操作
串的查找定位操作(也称为串的模式匹配操作)是指在当前串(主串)中寻找子串(模式串)的过程。若在主串中找到了一个和模式串相同的子串,则查找成功;若在主串中找不到与模式串相同的子串,则查找失败。当模式匹配成功时,返回模式串的首字符在主串中的为序号;当匹配失败时,返回-1。
两种主要的模式匹配算法是Brute-Force算法和KMP算法。
1)Brute-Force模式匹配算法
Brute-Force算法是一种简单、直观的模式匹配算法。其实现方法是:设s为主串,t为模式串,i为主串当前比较字符的下标,j为模式串当前比较的下标。令i的初值为start,j的初值为0。从主串的第start个字符(i=start)起和模式串的第一个字符(j=0)比较,若相等,则继续比较后续字段(i++,j++);否则从主串的第二个字符起重新开始和模式串比较(i返回到原位置加1,j返回到0),依次类推,知道模式串中的每一个字符依次和主串s的一个连续的字符序列相等,则称匹配成功,返回模式串t的第一个字符在主串s中的位置;否则称匹配失败,返回-1。
public int indexOf(IString t, int start) {
if (this != null && t != null && t.length() > 0
&& this.length() >= t.length()) {
int slen = this.length();
int tlen = t.length();
int i = start;
int j = 0;//j为模式串当前字符的下标
while (i < slen && j < tlen) {
if (this.charAt(i) == t.charAt(j)) {
i++;
j++;//继续比较后续字符
} else {
i = j - i + 1;//主串下标返回原位置加1
j = 0;//模式串下标返回到0
}
}
if (j >= t.length()) {
return i - tlen;// 匹配成功,返回子串序号
} else {
return -1;
}
}
return -1;
}
2)KMP算法
4.2 数组
数组是n(n>=1)个具有相同类型的数据元素a0,a1,... an-1构成的有限序列,并且这些数据元素占用一片地址连续的内存单元。其中,n称为数组的长度。
一维数组可以看成是一个顺序存储结构的线性表。数组元素是一维数组的数组称为二维数组,数组元素是二维数组的数组称为三维数组,以此类推。二维数组和三维数组都属于多维数组。实际上,多维数组实际上时是用一维数组实现的。