串的数据结构表——顺序串与链式串

  串的结构类似与线性表,只不过串的数据元素是一个字符,即是由零个或多个字符组成的有限序列。

一、串的顺序存储

  串的顺序存储结构也就是顺序存储,即串中的字符被依次的存在一组连续的存储单元中,可以类比线性表的顺序存储,可以写出其数据结构如下:

typedef struct st
{
	char *ch;		//串存放的起始地址,串中第i个字符存储在ch[i-1]中
	int length;		//串的长度
	int strsize;	//分配的存储空间的大小,如果不足,在通过realloc()分配增加空间
}string;

  假设现在的字符串是“friend”,其结构可以用如图所示:

  

  现在我们拿到这个数据结构第一步是要初始化一个串,即首先在内存中开辟一段连续的存储空间(大小可以假先预定MAXSIZE个存储空间),初始化其长度length为0,同时制定其串的最大容量是MAXSZIE,如下:

//串的初始化操作
string CreateNullString()
{
	string s;
	s.length=0;
	s.ch=(char*)malloc(MAXSIZE *sizeof(char));
	s.strsize=MAXSIZE;
	return s;
}

 初始化完成之后,我们可以类比顺序表,对串完成一系列操作,如串拷贝、取子串、串赋值等,

 程序示例如下:

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100

typedef struct st
{
	char *ch;		//串存放的起始地址,串中第i个字符存储在ch[i-1]中
	int length;		//串的长度
	int strsize;	//分配的存储空间的大小,如果不足,在通过realloc()分配增加空间
}string;

//串的初始化操作
string CreateNullString()
{
	string s;
	s.length=0;
	s.ch=(char*)malloc(MAXSIZE *sizeof(char));
	s.strsize=MAXSIZE;
	return s;
}

//判断空串
int isEmpty(string s)
{
	if(s.length==0)
		return 1;
	else
		return 0;
}

//赋值操作
void StringAssign(string *s1,char s2[])
{
	int i=0;
	while(s2[i]!=‘\0‘)	// ‘\0‘ 是字符串的结束符,任何字符串之后都会自动加上‘\0‘
		i++;			//计算s2的长度
	if(i>s1->strsize)
	{
		//所赋值的字符数组超过字符串的默认容量,则增加存储空间
		s1->ch=(char*)malloc(i*sizeof(char));
		s1->strsize=i;
	}
	s1->length=i;
	for(i=0;i<s1->length;i++)
		s1->ch[i]=s2[i];	//从第一个字符开始逐个字符赋值
}

//串拷贝操作
void StringCopy(string *s1,string s2)
{
	if(s1->strsize<s2.length)
	{
		s1->ch=(char*)realloc(s1->ch,s2.length*sizeof(char));
		s1->strsize=s2.length;
	}
	s1->length=s2.length;
	int i;
	for(i=0;i<s1->length;i++)
		s1->ch[i]=s2.ch[i];
}

//求串的长度
int StringLength(string s)
{
	return s.length;
}

//串的连接操作
void concat(string *s,string s1,string s2)
{
	if(s->strsize<s1.length+s2.length)
	{
		s->ch=(char*)realloc(s->ch,(s1.length+s2.length)*sizeof(char));
		s->strsize=s1.length+s2.length;
	}
	s->length=s1.length+s2.length;		//两串连接
	int i;
	for(i=0;i<s1.length;i++)			//将s1复制到s中
		s->ch[i]=s1.ch[i];
	for(;i<s->length;i++)
		s->ch[i]=s2.ch[i-s1.length];	//将s2复制到s中去
}

//取子串操作
int substr(string s,int i,int len,string *t)
{
	/*
		i表示从字符串s的第i个位置开始截取(索引从1开始)
		len表示截取字符串的长度
	*/
	if(i<=0 || i>s.length  || len<0 || len>s.length-i+1)	//参数不合法
		return 0;
	if(t->length<len)	//存储空间不够,继续分配存储空间
	{
		t->ch=(char*)realloc(t->ch,len*sizeof(char));
		t->strsize=len;
	}
	t->length=len;
	int k;
	for(k=0;k<t->length;k++)
		t->ch[k]=s.ch[i-1+k];
	return 1;
}

//插入操作
int insertString(string *s,int i,string t)
{
	//在字符串s的第i个位置插入字符串t
	if(i<=0 || i>s->length+1)
		return 0;
	if(s->strsize<s->length+t.length)	//空间不足
	{
		s->ch=(char*)realloc(s->ch,(s->length+t.length)*sizeof(char));
		s->strsize=s->length+t.length;
	}
	int k;
	for(k=s->length-1;k>=i-1;k--)	//将s中的后i个字符后移到后面
		s->ch[k+t.length]=s->ch[k];
	s->length=s->length+t.length;
	for(k=0;k<t.length;k++)				//将t的值赋值给s
		s->ch[k+i-1]=t.ch[k];
	return 1;
}

//删除操作
int deleteString(string *s,int i,int len)
{
	//从s的第i个字符开始删除len个字符
	if(i<=0 || i>s->length  || len<0 || len>s->length-i+1)	//参数不合法
		return 0;
	int k;
	for(k=i+len-1;k<s->length;k++)	//从s的i+len-1个位置开始将其后的所有字符前移
		s->ch[k-len]=s->ch[k];
	s->length-=len;
	return 1;
}

//输出操作
void print(string s)
{
	int i;
	for(i=0;i<s.length;i++)
		printf("%c",s.ch[i]);
	printf("\n");
}

void main()
{
	string s1=CreateNullString();
	string s2=CreateNullString();
	string s3=CreateNullString();
	char ch[MAXSIZE];
	printf("请输入主串:\n");
	gets(ch);
	StringAssign(&s1,ch);
	printf("主串 s1 为:");
	print(s1);
	StringCopy(&s2,s1);
	printf("拷贝串操作结果如下,结果如下 s2 :");
	print(s2);
	printf("删除操作(1——s1.length-3 全删):");
	deleteString(&s2,1,s1.length-3);
	print(s2);
	printf("插入操作,插入到s2的第2个位置上,请输入插入的字符串:");
	gets(ch);
	StringAssign(&s3,ch);
	insertString(&s2,2,s3);
	print(s2);
	printf("取子串操作(取s1的子串【2-4】):");
	substr(s1,2,3,&s3);
	print(s3);
	printf("串连接操作【将s1与s3合并】:");
	concat(&s1,s1,s2);
	print(s1);
}

  结果如下:


二、串的链式存储

  串的链式存储结构称为串的链式存储,即串中的每一个节点包括两个部分:数据域和指针域,其中数据域用来存放字符,指针域用来存放指向下一个节点的指针。

  由此可得串的链式数据结构如下:

typedef struct node
{
	char ch;			//字符域
	struct node *next;  //指针域,存放下一个结点的地址
}node,*linkstr;

 如下所示:

 

其初始化操作主要是开辟头结点,同时让它的下一个指针指向为NULL:

//初始化操作
linkstr CreateNullString()
{
	linkstr s;
	s=(linkstr)malloc(sizeof(node));
	if(s!=NULL)
		s->next=NULL;
	return s;
}

由此,完整的案例如下:

#include<stdio.h>
#include<stdlib.h>

//链式串

typedef struct node
{
	char ch;			//字符域
	struct node *next;  //指针域,存放下一个结点的地址
}node,*linkstr;

//初始化操作
linkstr CreateNullString()
{
	linkstr s;
	s=(linkstr)malloc(sizeof(node));
	if(s!=NULL)
		s->next=NULL;
	return s;
}

//判断空串
int iEmpty(linkstr s)
{
	if(s->next==NULL)
		return 1;
	else
		return 0;
}

//赋值操作
void Stringassign(linkstr s,char t[])
{
	linkstr p,q,r;
	r=s;		//r始终表示的是尾结点(最后一个非空节点,而不是最后一个NULL节点)。
	q=s->next;
	int i;
	for(i=0;t[i]!=‘\0‘;i++)
		if(q!=NULL)
		{
			q->ch=t[i];
			r=q;
			q=q->next;
		}
		else
		{
			//(初始化时只给头结点分配了存储空间或者其他情况),如果需要继续添加数据(其他节点没分配空间)需要继续分配
			p=(linkstr)malloc(sizeof(node));
			//添加节点
			p->ch=t[i];
			r->next=p;
			r=p;
		}
		r->next=NULL;
		//将s中原来多余的空间释放掉
		while(q!=NULL)
		{
			p=p->next;
			free(q);
			q=p;
		}
}

//串拷贝操作
void assign(linkstr s,linkstr t)
{
	//将t串的值赋值给s串
	linkstr p,q,r,u;
	p=t->next;
	q=s->next;
	r=s;
	while(p!=NULL)
	{
		//串s原先分配了空间
		if(q!=NULL)
		{
			q->ch=p->ch;
			r=q;
			q=q->next;
		}
		//若串s原先的空间不够用
		else
		{
			u=(linkstr)malloc(sizeof(node));
			u->ch=p->ch;
			r->next=u;
			r=u;
		}

		//p节点后移
		p=p->next;
	}

	//同理,若q的长度过长,可以释放多余的空间
	while(q!=NULL)
	{
		p=p->next;
		free(q);
		q=p;
	}
	r->next=NULL;
}

//求串的长度
int length(linkstr s)
{
	linkstr p;
	int n=0;
	p=s->next;
	while(p!=NULL)
	{
		n++;
		p=p->next;
	}
	return n;
}

//串的连接操作
void contact(linkstr s,linkstr s1,linkstr s2)
{
	linkstr p,q,r,t;
	r=s;
	p=s1->next;
	q=s->next;
	while(p!=NULL)
	{
		if(q!=NULL)
		{
			q->ch=p->ch;
			q=q->next;
			r=q;
		}
		else
		{
			//串s原来没有分配存储空间,需要申请空间
			t=(linkstr)malloc(sizeof(node));
			t->ch=p->ch;
			r->next=t;
			r=t;
		}
		p=p->next;
	}
	p=s2->next;
	while(p!=NULL)
	{
		if(q!=NULL)
		{
			q->ch=p->ch;
			q=q->next;
			r=q;
		}
		else
		{
			//串s原来没有分配存储空间,需要申请空间
			t=(linkstr)malloc(sizeof(node));
			t->ch=p->ch;
			r->next=t;
			r=t;
		}
		p=p->next;
	}

	//将串s的多余的空间清除掉(这个情况只可能发生在while的if循环中)
	while(q!=NULL)
	{
		p=q->next;
		free(q);
		q=p;
	}
	r->next=NULL;	

}

//截取子串
int substr(linkstr s,int i,int len,linkstr t)
{
	linkstr p,q,r,u;
	if(i<=0 || i>length(s) || i+len-1>length(s) )
		return 0;
	//指针指向s的第i-1个位置
	int j,k;
	for(j=0,p=s;j<i;j++)
		p=p->next;

	for(k=0,r=t,q=t->next;k<len;k++)
	{
		if(q!=NULL)
		{
			q->ch=p->ch;
			r=q;
			q=q->next;
		}
		else
		{
			u=(linkstr)malloc(sizeof(node));
			u->ch=p->ch;
			r->next=u;
			r=u;
		}
		p=p->next;
	}

	while(q!=NULL)
	{
		p=q->next;
		free(q);
		q=p;
	}
	r->next=NULL;
	return 1;
}

//插入操作
int insert(linkstr s,int i,linkstr t)
{
	linkstr p,q,r;
	if(i<=0 || i>length(s)+1)
		return 0;
	//指向第i-1个位置
	int j;
	for(j=0,p=s;j<i-1;j++)
		p=p->next;
	q=t->next;
	while(q!=NULL)
	{
		r=(linkstr)malloc(sizeof(node));
		r->ch=q->ch;
		r->next=p->next;
		p->next=r;
		q=q->next;
		p=r;

	}
	return 1;
}

//删除操作
int deleteString(linkstr s,int i,int len){
	linkstr p,q,r;
	if(i<=0 || i>length(s) || i+len-1>length(s) )
		return 0;
	int j;
	for(j=0,p=s;j<i-1;j++)
		p=p->next;
	for(j=0;j<len;j++)
	{
		q=p->next;
		p->next=q->next;
		free(q);
	}
	return 1;

}

//打印输出
void print(linkstr s)
{
	 linkstr p=s->next;
	 while(p!=NULL)
	 {
		printf("%c",p->ch);
		p=p->next;
	 }
	 printf("\n");
}

void main()
{
	linkstr s1;
	linkstr s2;
	linkstr s3;
	s1=CreateNullString();
	s2=CreateNullString();
	s3=CreateNullString();
	char str[100];
	printf("请输入字符串:\n");
	gets(str);
	Stringassign(s1,str);
	printf("串s1:");
	print(s1);
	printf("串s1的长度为:%d\n",length(s1));
	assign(s2,s1);
	printf("串s2:");
	print(s2);
	printf("串s2删除操作(第三个位置的三个字符删除 :");
	deleteString(s2,3,3);
	print(s2);
	printf("串连接操作(s3=s1+s2 ):");
	contact(s3,s1,s2);
	print(s3);
	printf("插入操作(从s1的第6个位置插入s3):");
	insert(s1,6,s3);
	print(s1);
	printf("测试截取子串的功能s2(截取s3的第四个位置长度为4的字符串:");
	substr(s3,4,4,s2);
	print(s2);
	printf("测试字Contact的清除过多存储空间的功能:(将两个较短的字符[两个s2]合并写到s1上去:");
	contact(s1,s2,s2);
	print(s1);
}

  结果如图:

时间: 2024-10-18 06:29:03

串的数据结构表——顺序串与链式串的相关文章

Java实现线性表-顺序表示和链式表示

顺序表示和链式表示的比较: 1.读写方式:顺序表可以顺序存取,也可以随机存取:链表只能从表头顺序存取元素: 2.逻辑结构与物理结构:顺序存储时,逻辑上相邻的元素其对应的物理存储位置也相邻:链式存储时,逻辑上相邻的元素,其物理存储位置则不一定相邻: 3.查找.插入和删除操作: 按值查找,当线性表在无序的情况下,两者的时间复杂度均为o(n):而当顺序表有序时,可采用折半查找,此时时间复杂度为o(log n): 按位查找,顺序表支持随机访问,时间复杂度为o(1):而链表的平均时间复杂度为o(n). 顺

数据结构Java实现05----栈:顺序栈和链式堆栈

数据结构Java实现05----栈:顺序栈和链式堆栈 一.堆栈的基本概念: 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作. 先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底.堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈. 备注:栈本身就是一个线性表,所以我们之前讨论过线性表的顺序存储和链式存储,对于栈来说,同样适

数据结构(四)——基于链式存储结构的线性表

数据结构(四)--基于链式存储结构的线性表 一.基于链式存储结构的线性表 1.链式存储的定义 链式存储为了表示数据元素与其直接后继元素间的逻辑关系,数据元素除了存储本身的信息外,还需要存储直接后继的信息.相连的数据元素之间在存储空间中不要求连续.链式存储的逻辑结构基于链式存储结构的线性表中,每个结点都包含数据域和指针域.数据域用于存储数据元素本身,指针域用于存储相邻结点的地址. 2.链表的定义 链表是基于链式存储结构的线性表链表的基本元素:头结点:链表中的辅助结点,包含指向第一个数据元素的指针数

数据结构导论 四 线性表的顺序存储VS链式存储

前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char name[30];//姓名 char sex; //性别 int class;//班级 int age;//年龄 } student={"01",“zhangsan”,"m","201","20"}: 链式存储:初始化 //建立一个空链

栈:顺序栈和链式堆栈

一.堆栈的基本概念: 堆栈(也简称作栈)是一种特殊的线性表,堆栈的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置进行插入和删除操作,而堆栈只允许在固定一端进行插入和删除操作. 先进后出:堆栈中允许进行插入和删除操作的一端称为栈顶,另一端称为栈底.堆栈的插入和删除操作通常称为进栈或入栈,堆栈的删除操作通常称为出栈或退栈. 备注:栈本身就是一个线性表,所以我们之前讨论过线性表的顺序存储和链式存储,对于栈来说,同样适用. 二.堆栈的抽象数据类型: 数据集合: 堆栈的数据

线性表的Java实现--链式存储(单向链表)

线性表的Java实现--链式存储(单向链表) 单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始. 链式存储结构的线性表将采用一组任意的存储单元存放线性表中的数据元素.由于不需要按顺序存储,链表在插入.删除数据元素时比顺序存储要快,但是在查找一个节点时则要比顺序存储要慢. 使用链式存储可以克服顺序线性表需要预先知道数据大小的缺点,链表结构可以充分利用内存空间,实现灵活的内存动态管理.但是链式存储失去了数组随机存取的特点,同时增加了节点的指针域,空

顺序队列和链式队列的实现

队列是一种常用的数据结构,它跟栈一样,操作都受到限制,队列只允许从一端进数据,另一端出数据.队列跟栈不同,栈是一种"后进先出"的模式,而队列是一种"先进先出"的操作模式.就好比日常排队一样,先排队的先出,后排队的后出.例如,进入队列的顺序是1,2,3,4,5则出队列的顺序是1,2,3,4,5(只考虑一次性出列的情况). 队列也分顺序队列和链式队列,跟顺序栈和链表栈一样,顺序队列同样是基于数组实现,链式队列则是基于链表实现. 顺序队列: //顺序队列 #include

栈和队列分别的顺序结构和链式结构

栈和队列 栈和队列本身作为特殊的线性表,要记住他俩本身就费劲.难受的是他俩还能分别考虑顺序结构和链式结构,很复杂,容易混淆. 其实比起FILO(先进后出)和FIFO(先进先出)等特点,更重要的是对指针的把握.进出顺序是在逻辑层面的,只要理解就行,难得是如何用指针来表示这种特点,于是我就此方面进行个总结. 顺序栈 虽然栈只需要对一段进行操作(栈顶),但我们除了栈顶指针(top)外,还需要设置栈底指针(base),主要是为了用于判断栈空栈满. s.top == s.base; //当栈顶指针等于栈底

2、线性表的实现:链式存储、单链表

1 package ren.laughing.datastructure.baseImpl; 2 3 import ren.laughing.datastructure.base.List; 4 import ren.laughing.datastructure.base.Strategy; 5 import ren.laughing.datastructure.exception.OutOfBoundaryException; 6 /** 7 * 线性表的实现:链式存储结构:单链表 8 * @