今 日 寄 语
知识本身并没有告诉人们怎样运用它,运用的方法乃在书本之外。
——培根
01
链表
1. 链表的基本结构
链表是一种最常用、最典型的动态数据结构。单向链表的数据结构示意如图。
组成链表的每个元素称为“结点”,每个结点由两部分组成:数据部分和指向下一个结点的指针。其中,head是结点类型的指针变量,称为“头指针”,存放链表第一个结点的地址,即指向链表的第一个结点。第一个结点的第二部分存放第二个结点的地址,第二个结点的第二部分存放第三个结点的地址,……,最后一个结点的第二部分地址为空,不指向任何元素,程序据此来判断链表是否结束。
结点的定义形式如下:
struct 结构体名{ 成员类型 成员名1; 成员类型 成员名2; …… 成员类型 成员名n; //以上为第一部分,存放结点数据 结构体类型 *指针变量名; //第二部分,存放下一结点的地址};
第一部分为结点的数据部分,具体数据的类型及形式根据用户需要设定;第二部分是结构体类型的指针变量,存放下一结点的地址。
2. 单向链表
建立单向链表
(1)建立第一个结点
p1=new student; //在内存中开辟存储空间cin>>p1->id>>p1->score;//输入学号和成绩 例如输入:2003 90↙if(p1->id!=0) //当学号不为0时,表示以上动态生成的结点有效{ head=p1; //因为是第一个结点,所以将头指针指向该结点 p2=p1; //p2也指向该结点}
(2)建立第二个结点
p1=new student; //在内存继续动态生成第二个结点cin>>p1->id>>p1->score; //输入学号和成绩 例如输入:2004 95↙if(p1->id!=0) //此次生成的结点有效{ p2->next=p1; //将第一个结点和第二个结点链接起来 p2=p1; //p2指向第二个结点}
(3)链表建立完成
p1=new student; //在内存继续动态生成新结点cin>>p1->id>>p1->score;//输入学号和成绩 例如输入:0 0↙if(p1->id==0) //此次生成的结点无效,表明链表建立完所有的结点 p2->next=NULL; //设置链表的结束标志return head; //返回链表的头指针
3. 链表的基本操作的考察
链表的基本操作中主要考察的是链表的建立、插入结点、删除结点、输出链表。链表的建立是链表部分内容的基础,在做题时既要求我们可以熟练的书写代码,根据需要建立自己的链表,也要求我们能够看懂题目中给出的代码,知道建立的是一条什么样的链表。在代码题中通常也会有补全代码的要求,那就要求对代码有一定的熟悉度,在读懂已有部分的情况下,补充缺失部分,这种题目较为困难,既要对相关知识多加熟悉,也需要在刷一些题目找找感觉。
链表中插入结点和删除结点其实是类似的处理方式,都是要将原链表断开,进行处理后再重新接上。处理这种问题需要使用两个指针P1,P2,P1指向需要处理的结点(将被删除或插入的结点)的前一个结点,P2则指向待处理的指针,再结合前面建立链表的知识就可以很好的处理。
输出链表比较简单,可以类比数组的输出,只是循环条件略有不同,在此不做赘述。
最后给出一段对链表的综合操作的代码
void main(){ student *head,*p0; int x,score; head=create(); //建立链表 print(head); //输出链表 cout<<"请输入欲删除的结点的id: "; cin>>x; while(x!=0) //可以删除多个结点 { head=del(head, x); print(head); cout<<"请输入欲删除的结点的id: "; cin>>x; } cout<<"请输入欲插入的结点的id和score : "; cin>>x>>score; while(x) //可以顺序插入多个结点 { p0=new student; p0->id=x; p0->score=score; head=insert(head,p0); print(head); cout<<"请输入欲插入的结点的id和score : "; cin>>x>>score; } print(head);}
总结一下,在对前面知识(主要是指针和结构体)熟练掌握的情况下,链表的理解以及做题都不算困难,出题时会倾向于出读写程序题。做题和处理问题时,如果脑中无法清晰的想象出链表的结构,不妨和前面建立链表处一样在纸上画图表示链表结构,对于初学者而言,这个方法可以很好地帮助大家认识链表。
02
字符串
1.字符串与字符数组
C++语言将字符串作为字符数组来处理,C++语言中约定用'\0'作为字符串的结束标志,它占内存空间,但不计入串长度。有了结束标志'\0'后,程序往往依据它判断字符串是否结束,而不是根据定义时设定的长度。下面举例说明:
char c[]={'C', 'h','i','n','a'}; //长度为5个字节
而:
char c[]={"China"}; //长度为6个字节,以'\0'结尾
或去掉{}写为:
char c[]="China";
用字符串方式赋值比用字符逐个赋值要多占一个字节,用于存放字符串结束标志'\0'。上面的数组c在内存中的实际存放情况为:
在选择题中会针对这一内容出题,一定要注意字符串的尾“0”。
2. 字符串处理函数
通过调用#include “string.h”,可以方便地使用很多字符串处理函数,这些函数的实参都是字符数组名。
(1)字符串连接函数strcat
格式:
strcat (字符数组名1, 字符数组名2)
功能:把字符数组2中的字符串连接到字符数组1 中字符串的后面,并删去字符串1后的串标志'\0'。本函数返回值是字符数组1的首地址。
(2)字符串拷贝函数strcpy
格式:
strcpy (字符数组名1, 字符数组名2)
功能:把字符数组2中的字符串拷贝到字符数组1中,连'\0'同时拷贝到字符数组1中。
注意:在程序中使用str1=str2;是错误的。因为数组名代表这个数组在内存中的首地址,一经编译,这个地址就是固定的,因此可以把数组名看作是常量,赋值运算符的左边出现常量是错误的。
函数strcpy( )的第二个参数也可以是一个字符串,如:
char ss[90];strcpy(ss, "student");
执行后字符数组ss中的字符串就是student。
(3)字符串比较函数strcmp
格式:
strcmp(字符数组名1, 字符数组名2)
功能:该函数的二个实参均可以是字符数组名,或者是一个字符串。对二个字符串进行比较的规则是:从二字符串的首字符开始自左至右逐个字符进行比较,这种比较是按字符的ASCII码值的大小来进行的,直到出现二个不同的字符或遇到字符串的结束符'\0'为止。如果二个字符串中的字符均相同,则认为二个字符串相等;当二个字符串不同时,则以自左至右出现的第一个不同字符的ASCII码相减的结果作为二个字符串的比较结果,并将这比较结果作为函数的返回值返回。若二个字符串相等,则返回0;若字符串1大于字符串2,返回一个正整数;若字符串1小于字符串2,返回一个负整数。
(4)求字符串的长度函数strlen
格式:
strlen(字符数组名)
功能:该函数的实参可以是字符数组名,也可以是字符串,返回值为数组首字母到第一个字符串结束标志'\0'的长度。并非数组在内存中空间的大小。例如:
char c1[80]="china";cout<<strlen(c1)<<endl;
输出结果是:5
虽然字符串处理函数还有很多,但比较常用的就是上面给出的四个。因为C++会将字符串作为字符数组处理,在学习的时候需要将两者结合起来看,在处理字符串时也会经常用到数组方面的一些知识。熟练掌握数组相关知识以及字符串的性质及其处理函数,字符串相关问题自然迎刃而解。
文字 王瑞
排版 牟育生