数据结构--Chapter4(串与数组)

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称为数组的长度。

    一维数组可以看成是一个顺序存储结构的线性表。数组元素是一维数组的数组称为二维数组,数组元素是二维数组的数组称为三维数组,以此类推。二维数组和三维数组都属于多维数组。实际上,多维数组实际上时是用一维数组实现的。

     

一、填空题(每空1分,共20分) 1. 不包含任何字符(长度为0)的 称为空; 由一个或多个空格(仅由空格符)组成的 称为空白。 (对应严题集4.1①,简答题:简述空和空格的区别) 2. 设S=“A;/document/Mary.doc”,则strlen(s)= 20 , “/”的字符定位的位置为 3 。 4. 子的定位运算称为的模式匹配; 被匹配的主 称为目标, 子 称为模式。 5. 设目标T=”abccdcdccbaa”,模式P=“cdcc”,则第 6 次匹配成功。 6. 若n为主长,m为子长,则的古典(朴素)匹配算法最坏的情况下需要比较字符的总次数为 (n-m+1)*m 。 7. 假设有二维数组A6×8,每个元素用相邻的6个字节存储,存储器按字节编址。已知A的起始存储位置(基地址)为1000,则数组A的体积(存储量)为 288 B ;末尾元素A57的第一个字节地址为 1282 ;若按行存储时,元素A14的第一个字节地址为 (8+4)×6+1000=1192 ;若按列存储时,元素A47的第一个字节地址为 (6×7+4)×6+1000)=1276 。 8. 〖00年计算机系考研题〗设数组a[1…60, 1…70]的基地址为2048,每个元素占2个存储单元,若以列序为主序顺序存储,则元素a[32,58]的存储地址为 9188 。 答:考虑0行0列,(58列×61行+32行)×2字节+基址2048=9188?? 9. 三元素组表中的每个结点对应于稀疏矩阵的一个非零元素,它包含有三个数据项,分别表示该元素 的 行下标 、 列下标 和 元素值 。 10.求下列广义表操作的结果: (1) GetHead【((a,b),(c,d))】=== (a, b) ; //头元素不必加括号 (2) GetHead【GetTail【((a,b),(c,d))】】=== (c,d) ; …………………………
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值