动态存储分配(堆分配)的顺序串与顺序表在操作上的异同

动态存储分配(堆分配)的顺序串与顺序表在操作上的异同


串(string)(或字符串)是由零个或多个字符组成的有限序列,一般记为
                     s=‘a1a2...an’  (n>=0)

串的数据元素固定为字符型的线性表,因此串的逻辑结构和线性表极为相似,只不过对串的操作常常是以”串的整体”或“子串”作为操作对象,而线性表的操作大多以“单个数据元素”为操作对象。


静态存储分配的顺序串(定长)

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

#define MAXSTRLEN 255    //用户可以在255以内定义最大串长
typedef unsigned char SSstring[MAXSTRLEN + 1];   //0号单元存放串的长度

串的实际长度在这预定义长度范围内随意,超过预定义长度的串值则被舍去,称之为“截断”。

在这种存储结构表示如何实现串的操作,下面以串连接和求子串为例讨论。

串连接 Concat(&T,S1,S2)
假设S1、S2和T都是SSstring型的串变量,且串T是由串S1联结串S2得到的,则只要进行串的复制操作即可,只是需要对超长部分实施“截断”操作。基于串S1和S2长度的不同情况,串T值的产生可能有如下3种情况:(1)S1[0]+S2[0]<=MAXSRETRLEN,得到的串T是正确的结果;(2)S1[0]< MAXSRETRLEN而S1[0]+S2[0]>MAXSRETRLEN,则将串S2的一部分截断,得到的串T只包含S2的一个子串;(1)S1[0]=MAXSRETRLEN,得到的串T并非联接结果,而和串S1相等。

求子串 SubString(&Sub,S,pos。len)
求子串的过程即为复制字符序列的过程,将串S中从第pos个字符开始长度为len的字符序列复制到串Sub中。

Status SubString(SString &Sub, SString S, int pos, int len){
    //用Sub返回串S的第pos个字符起长度为len的子串
    //其中,1<=pos<=StrLength(S)且0<=len<=StrLength(S)-pos+1
    if(pos<1||pos>S[0]||len<0||len>[0]-pos+1)
        return ERROR;
    Sub[1...len] = S[pos..pos+len-1];   //循环语句一个一个赋值
    Sub[0] = len; return OK;    //S串的长度置为len
}//Substring

综上两个操作可知,在顺序存储结构中,实现串操作的原操作为“字符序列的复制”,操作时间复杂度基于字符序列的长度。串连接的特点是,如果在操作中出现串值序列的长度超过MAXSTRLEN时,约定用截尾法处理,这种情况不仅在串联接时发生,在串的其它操作中,如插入、置换等也可能发生。克服这个弊病唯有不限定串长的最大长度,即动态分配串值的存储空间。


动态存储分配的顺序串(堆分配存储表示)

由于多数情况下串的操作是以串的整体或子串的形式参与,则在应用程序中,参与运算的串变量之间的长度相差较大,因此为串变量设定固定大小空间的数组不尽合理,需要根据具体情况来决定串空间的大小。

动态存储分配的顺序串完全可以用动态存储分配的顺序表SqList来表示,这样的顺序表的有关操作都可以用来处理顺序串的相关操作,如初始化、求串长等。利用malloc()函数为每个新产生的串分配一块实际串所需的存储空间,若分配成功,则返回一个指向起始地址的指针,作为串的地址。这样,当进行串的插入、连接等操作时,再根据实际需要的增补空间,以满足插入和连接等操作的需要。所以在描述动态顺序串时,当前数组的容量和增补空间不再作为结构的一部分,其结构的描述如下:

typedef struct{
    char *str;   //存放空串的首地址
    int length;  //存放串的当前长度
}DSqString       //动态顺序串类型

由于动态存储分配的顺序串既具有顺序存储结构的优点(随机存取,操作简单),同时串值空间的大小是在程序执行时动态分配而得,这对串的插入、连接、置换等操作非常有利,因此在串处理的应用程序中也常被选用。

这种存储结构表示的串操作仍是基于“字符序列的复制”进行的。
1.串赋值操作
串赋值操作就是把一个字符串常量赋值给顺序串S。成功赋值返回true,否则返回false。其主要操作是:①判断顺序串是否非空,若是,则释放其空间(尽管这样做也不影响操作的执行结果,但适时地进行空闲空间回收是一个好的编程习惯);②求串常量的长度,若等于0,就将顺序串S置空,否则,以此长度为标准为顺序串S申请空间;③把串常量chars的值复制到串S中去,同时顺序串S的长度被赋值为串常量的长度。

bool StrAssign_Sq(DSqString &S, char *chars){
    //将字符串chars赋值给顺序串S
    int i,j;
    char *c;
    for(i=0,c=chars;*c;i++,c++);  //求chars的长度
    if(!i){S.str=NULL;S.length=0;}  //S置为空串
    else{
        if(!(S.str=(char *)malloc(i*sizeof(char))))  //给串S申请空间
            return false;
        for(j=0;j<i;j++)
            S.str[j]=chars[j];   //将数组中的字符赋值到串S中
        S。length=i;    //串长置为i
    }
    return true;
}//StrAssign_Sq

又例如串复制操作StrCopy_Sq(&T, S)的算法实现是,若串T已存在,则先释放T所占的空间,当S不空时,首先为T分配大小和S长度相等的存储空间,然后将串S复制到串T中;又如串插入操作(连接)操作StrConcat_Sq(&S,T),首先判断T的长度是否为非0,若是,则为S分配长度为T.length的增补空间,然后将T中的所有字符复制在S的后面。


关于动态顺序串的主要操作与在顺序表中进行的相关操作的算法思想基本上是一致的,不同之处在于:
①因为动态顺序串的存储空间分配是以串的实际长度为标准的,所以在有些操作(如插入、连接等)中首先需要根据实际情况增补存储空间;
②在顺序串的操作中经常是针对串的整体(如串赋值)或串的一部分子串而进行的,这样的操作相对于顺序表要复杂一些。以串插入操作为例,首先需要增补存储空间以便能够存放插入后的串,其次是要把从插入位置pos到串末尾的每一个字符串向后移动T.length个位置(而不是顺序表的一个位置),最后再把子串中的每一个字符(而不是顺序表的一个字符)复制到主串的相应位置。所以他的时间开销相比顺序表的插入要多一些,其它操作类似。


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值