链表
目录:
1.链表的简介
2.链表的实现
3.动态链表
4.链表的插入和删除
1.链表的简介
链表是一种常见的数据结构,我们经常会使用数组来存放数据,但使用数组时,要先指定数组的大小,如果向这个数组加入过多的元素,则会超出,导致无法保存数据;如果数据太小,又会非常浪费内存空间。
所以,我们希望有一种存储方式,其储存的元素个数是不受限定的,当进行添加元素时,存储的个数也会增加,这种储存方式就是链表。
在链表中,有一个头指针head变量,用这个指针变量保存第一个元素的地址,其中,这个元素包括两个部分:数据部分和指针部分。数据部分用来存放元素的数据,指针部分用来指向下一个元素。于是,头指针指向第一个元素,第一个元素中的指针又指向第二个元素,第二个元素中的指针又指向第三个元素,依次下去,到了最后一个元素的指针就指向Null,表示指向的地址为空。
2.链表的实现
需要实现链表这种存储方式,必须要使用指针才能完成。结构体和指针一起实现链表功能。
我们看一下一个简单的元素结点,包含有数据部分和指针部分。
struct student
{
char cname[20]; //学生姓名 (数据部分)
int inumber; //学生学号 (数据部分)
struct student *pNext; //指向下一个结点的指针 (指针部分)
};
如果要向链表添加多一结点,那应该如何操作呢?我们看一个图:
当有新的结点添加到链表中时,原来最后一个结点的指针将保存新添加的结点的地址,而新的结点的指针将指向NULL(空),新的结点就变为最后一个结点。
3.动态链表
建立动态链表就是指程序运行过程中,从无到有建立一个链表,即一个一个分配结点内存空间,然后输入结点中的数据,并建立结点间的相连关系。
下面我们来建立一个动态链表:
首先创建结点结构:
struct student
{
char cname[20]; //学生姓名 (数据部分)
int inumber; //学生学号 (数据部分)
struct student *pNext; //指向下一个结点的指针 (指针部分)
}
然后定义一个Create函数,用来创建链表,其函数返回链表头指针:
Static int icount=0; //定义一个全局变量表示链表长度,结点计数器
struct student*create()
{
struct student *pHead=NULL; //初始化头指针
struct student *pone,*PoneNew; //初始化两个结构体指针
*PoneNew=(struct student*) malloc(sizeof (struct student)); //申请一个新的内存空间,使PoneNew指向这个新内存。
printf("请输入姓名和名字\n");
scanf("%S",&poneNew->cname); //赋值给新元素内的成员
scanf("%S",&poneNew->inumber); //赋值给新元素内的成员
while(poneNew->inumber !=0) //当输入了新学号时
{
icount++; //代表多了一个元素结点 icount加1
if(count==1) //如果该链表第一个元素结点还没有建立
{
poneNew->pNext=NULL; //将新的结点的指针部分指向空
pone=poneNew; //加入新的结点
pHead=poneNew; //头指针指向新的结点
}
else
{
poneNew->pNext=NULL; //将新的结点的指针部分指向空
pone->Next=poneNew; //将原来的结点的指针部分指向新的结点
pone=poneNew; //将新的结点地址给pone,使得下一次再增加结点前,pone为最后一个元素结点。
}
}
return pHead;
}
以上是从无到有建立一个动态链表的过程。
4.链表的插入和删除
上面我们从0到1建立了动态链表,那如何实现插入和删除操作呢,我们接着往下看。
我们设计一个函数可以实现链表的插入:
struct Node
{
..... //省略数据部分
struct Node * Next; //指向下一个结点的指针 (指针部分)
}:
int InsertElement (struct Node *List,int I, struct Node *Node)
{ // List为链表第一个节点地址,I为要插入的第n个元素,如果存在,返回I,否则返回 0
struct Node *pOne=NULL;
struct Node *pNewOne=NULL;
int j=0; //计数器
pOne= List; //指向数组的第一个节点
While((pOne!=NULL)&&j<I-1) //顺序查找,一直到目标节点的前一节点
{
pOne=pOne->Next; //到下一个节点
j++; //计数加1
}
if((pOne->Next==NULL)||(j>I-1)) //第I个元素不存在
return 0:
pNewOne= ( struct Node *)malloc(sizeof(struct Node)); //申请一个内存空间,并且将内存地址赋值给pNewOne
{/*拷贝参数Node信息到新增加的节点pNewOne*/
pNewOne ->Next= Node ->Next; //(这里的Node是函数里传来的形参)把新的Node的指针部分指向的地址赋给pNewOne里的指针部分。
pNewOne ->Next= pOne ->Next; //将下一结点的地址赋值给插入的节点里,也就是让插入的节点指向下一节点(把新节点和后一位连起来)
pOne ->Next= pNewOne; //将前一节点的指针部分指向插入的新节点的地址(把新节点和前一位节点的连起来)
return j:
}
下面我们再进行链表的删除操作:
struct Node
{
..... //省略数据部分
struct Node * Next; //指向下一个结点的指针 (指针部分)
}:
int InsertElement (struct Node *List,int I)
{ // List为链表第一个节点地址,I为要删除的第n个元素,如果存在,返回I,否则返回 0
struct Node *pOnepre=NULL; //ponepre表示被删除节点的前一个节点
struct Node *pDeleteOne=NULL; //pDeleteOne表示要删除的节点
int j=0; //计数器
pOnepre= List; //指向数组的第一个节点
pDeleteOne=List;
While((pDeleteOne!=NULL)&&j<I-1) //顺序查找,一直到要删除的目标节点
{
ponepre=pDeleteOne; //保存好前一节点
pDeleteOne=pDeleteOne->Next; //到下一个节点
j++; //计数加1
}
if((pDeleteOne->Next==NULL)||(j>I-1)) //第I个元素不存在
return 0:
pOne ->Next= pDeleteOne ->Next; //将要删除的节点的下一节点的地址赋给要删除的节点的前一节点的指针中(有点拗口)
free(pDeleteOne); //使用free释放内存空间,删除节点
return j:
}
好了,以上就是链表的插入和删除操作,希望能对你有帮助~~后面再补充双链表和循环链表的内容。