StringDes(s1);//释放原来空间
s1->ch=(char*)malloc(sizeof(char)*n);//重新开辟空间
}
f)去字符串中常见的控制符size_tStringTrim(Str*s)
功能:
把字符串s中常见的文本占位符:
回车(“\n”值13)、换行(“\r”值10、也叫软回车)、走纸符(“\f”值12)、水平制表符(“\t”值9)、垂直制表符(“\v”值9)、空格(“”值32)。
返回值为修改后s的长度。
实现思路:
一次从第一个字符开始查找,若第i个字符为控制符,查找后面的字符,若不为控制符,赋值给第i个字符。
字符串的长度为新字符串的长度,并返回长度值。
关键代码:
for(i=0;ilen+1;i++)
{/*若第i个字符为控制符,查找第i个字符之后第一个不为控制符的字符*/
if(isspace(s->ch[i])!
=0){
for(t=i;tlen+1;t++)
{/*找到后赋值给第i个字符*/
if(isspace(s->ch[t])==0)
s->ch[i++]=s->ch[t];
}
}
}
g)字符串的文件输入输出size_tfgetSring(Str*s,intn,FILE*stream)、size_tfputSring(Str*s,FILE*stream)
功能:
fputSring()把s中的字符串内容输出到文件流stream中,fgetSring从文件流stream中读取长度不超过n的字符,放入字符串s中。
返回值意为实际读取或写入的字符串长度。
实现思路:
打开文件,若不能读或写,返回0,否则,从文件读出字符串初始化为Str型,返回读取的字符串的长度;把字符串写入文件,返回写入的字符串的长度。
h)字符串的字符终端界面的标准输入输出char*getSring(Str*s,intn)、size_tfputSring(Str*s,FILE*stream)
功能:
从屏幕输入字符串和把字符串输出到屏幕,返回字符串的长度值。
3.动态双向链表的操作
3.1.需求陈述:
设计一套C函数库用于动态双向循环链表的操作,同时把链表的结点数和链表的数据内容串封装在一起,使关于链表的各种信息更加方便被用户运用。
3.2.需求分析:
函数功能需求:
项目
功能
子功能
备注
LinkList
基本功能
创建结点creat_node()
/**链表结点中存储的数据的类型:
LNodeT**/
typedefintLNodeT;
/**链表结点的数据类型的预先声明**/
structlinklist;
/*双向链表结点的数据类型,两部分:
data为数据、next指向下一结点的地址,pre指向上一个结点的地址*/
structlinklist{
LNodeTdata;
structlinklist*next;
structlinklist*pre;
};
字符串的销毁erase_node()
头部插入insert_first()
尾部追加insert_tail()
头部删除delete_first()
尾部删除delete_tail()
按数据内容在链表中插入结点insert_by_data()
按顺序在链表中插入结点insert_by_sub()
获取链表的长度linklen()
按数据内容匹配结点travel_bydata()
按下标访问链表trabel_byseq()
数据需求:
为提高计算准确性和节约时间,故提前在主函数中录入,链表操作时大多直接调用。
3.3.程序设计思想
3.3.1数据类型
链表结点中存储的数据的类型为LNodeT,这里先定义为int类型。
typedefintLNodeT;
双向循环链表结点的数据类型包括两部分:
data为数据、next和pre为向前和向后指针、分别用来存储前一结点和后一结点的地址。
structlinklist{
LNodeTdata;
structlinklist*next;/**向后的指针**/
structlinklist*pre;/**向前的指针**/
};
定义链表的数据类型LinkT,链表里每个结点都采用动态的内存分配。
typedefstructlinklistLinkT;
3.3.2功能分析
a)创建结点LinkT*creat_node(LNodeTa)、
字符串的销毁#defineerase_node(p)(free(p))
功能:
creat_node()用来初始化一个结点,在这个函数里为结点分配内存空间,所以要用一个LNodeT类型的参数把结点数据传进来;返回一个链表结点的指针LinkT*,把访问存储空间的首地址传出去。
erase_node()用来释放一个链表的结点,它需要一个结点的存储位置。
实现思路:
首先为新结点开辟内存空间,然后把数据赋值给新节点,把向前和向后的指针赋值为空,返回该结点的地址。
关键代码:
LinkT*p=(LinkT*)malloc(sizeof(LinkT));//为新节点开辟空间
p->next=NULL;//把向后的指针赋值为空
p->pre=NULL;//把向前的指针赋值为空
b)头部插入LinkT*insert_first(LinkT*head,LinkT*p)
LinkT*insert_first1(LinkT*head,LNodeTa)
尾部追加LinkT*insert_tail(LinkT*head,LinkT*p)
功能:
在链表的头部和尾部分别插入一个结点,返回新链表的头指针。
实现思路:
头部插入:
若原链表为空,新链表只有一个结点,向前和向后的指针指向本身。
否则,新结点的向前的指针指向原链表的尾,向后的指针指向原链表的第一个结点,head指向新结点,插入新结点。
若根据结点的数据内容插入,需先创建结点。
尾部追加:
若原链表为空,新链表只有一个结点,相当于在头部插入,否则,相当于先在头部插入,再把head向后移动一个位置。
关键代码:
1头部插入中
if(!
head)//此时原链表为空链表,插入后只有一个结点
{
p->next=p;
p->pre=p;
head=p;
}
else
{
p->pre=head->pre;//p的前一个结点指向原链表尾
p->next=head;//p的后一个结点指向原链表头
head->pre->next=p;//原链表尾的下一个结点指向p
head->pre=p;//原链表头的上一个结点指向p
head=p;//链表头改变
}
2尾部追加中
if(!
head)//原链表为空,相当于在头部插入一个结点
head=insert_first(head,p);
else
{/*相当于先在头部插入,再把head向后移动一个位置*/
q=insert_first(head,p);
head=q->next;
}
c)头部删除LinkT*delete_first(LinkT*head)、
尾部删除LinkT*delete_tail(LinkT*head)
功能:
在链表的头部和尾部分别删除一个结点,返回新链表的头指针。
实现思路:
头部删除:
链表为空,不用删除,若有一个结点,删除该结点,新链表为空。
否则,head后移一个位置,原来的head结点释放掉。
尾部删除:
链表为空,不用删除,否则,相当于head向前移动一个位置,删除头结点。
关键代码:
1头部删除中
elseif(head->next==head)//只有一个结点,删除该结点{
erase_node(head);//释放结点
head=NULL;//此时新链表为空
}
else
{/*把头结点删除,并释放掉,head向后移动一个位置*/
p=head->next;
head->pre->next=p;
p->pre=head->pre;
erase_node(head);//释放结点
head=p;
}
2尾部删除中
else
{/*相当于head向前移动一个位置,删除头结点*/
head=q;
head->next=p;
p->pre=head;
q->pre->next=head;
returndelete_first(head);//返回删除结点后的头指针
}
d)按数据内容在链表中插入结点LinkT*insert_by_data(LinkT*head,LNodeTnode)
按顺序在链表中插入结点LinkT*insert_by_sub(LinkT*head,LinkT*p,intn)
功能:
实现在链表中插入新结点,返回插入结点后的头指针。
实现思路:
按数据内容在链表中插入结点:
先把数据内容初始化为结点,若链表为空,相当于在头部插入,若第一个结点数据大于node,相当于在头部插入,否则,查找第一个大于node的结点,把新结点插入到该结点前面,没有找到,把新结点插入到尾部。
按顺序在链表中插入结点:
链表为空,相当于在尾部插入,若输入顺序值超过链表长度,不插入。
若n=1相当于在头部插入,否则,遍历链表,找到第n个位置,插入结点。
关键代码:
1按数据内容插入
LinkT*q,*p=creat_node(node);//先