上一节简单的介绍了链表的简单访问,这一节将更具体的介绍链表的插入与删除操作:
作为动态存储数据的链表,他有着静态存储(例如数组等)不具有的优势,能够随时的开辟新的存储单元。
例子:
例如已经有一个学生信息构成的链表,各个结点是按照学号排列的,突然发现有一个学生的信息漏掉了,需要插入进去,这个时候就用到了链表的插入操作:
要在链表中包含值X的结点之前插入一个包含值y的结点(按条件插入),过程如下:
(1),首先需要申请新的结点s,并对其数据与赋值。
(2),在链表中寻找插入位置,将包含x的结点的地址设为p2,将x前的结点的地址设为p1。
(3),将包含x的结点插入在p2前边,为了实现这一步,需要改变两个数据的指针域。
具体操作如下:
1,使s结点的指针域内容改为指向p2,s=next->p2;
2.使结点p1的指针域内容改为指向s,p1->next=s;
(注意这两个步骤不能交换)
插入完成。
当插入结点插在p2所指结点之前称之为“前插”,同样的当插入结点插在p2所指结点之后称之为后插。
在整个过程中共需要三个指针,具体操作自己慢慢体会。
例子:编写函数insert,在单向链表中插入一个结点,其具体功能为:在值x的结点前,插入值为y的结点,若x结点不存在则插在表尾。
分析:在过程中可能会出现三种情况:
1,链表非空,值x结点存在,在其前方插入
2,链表非空,值x不存在,在表尾插入。
3,链表为空,这时候等同于x结点不存在,则插在表尾,也就是头结点的后边,作为链表的一个有效结点。
在函数insert()中需要针对这三种情况进行处理,为简化书写,下面的程序中对结点的设计进行简化,进给出一个整形数据域表示学号,其他数据暂时省略。
源程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct node
{
int id;
struct node *next;
};
typedef struct node SNODE;
int main()
{
SNODE *creat(SNODE *head); //三个函数声明
void print(SNODE *head);
SNODE *insert_node(SNODE *head,int x,int y); /*在包含x的结点前面插入一个包含y的结点的函数声明*/
int new_score,location;
SNODE *head;
head=NULL;
printf("请输入学生信息:\n");
head=creat(head);
printf("现有学生信息:\n");
print(head);
printf("请输入插入点的学号:\n");
scanf("%d",&location);
printf("输入要插入的学号:\n");
scanf("%d",&new_score);
insert_node(head,location,new_score);
printf("新的学生信息为:\n");
print(head);
return 0;
}
//创建链表函数(根据头指针head)
SNODE *creat(SNODE *head)
{
int id;
SNODE *s;
SNODE *r;
head=(SNODE*)malloc(sizeof(SNODE));
r=head;
printf("输入学号:");
scanf("%d",&id);
while(id!=0)
{
s=(SNODE*)malloc(sizeof(SNODE)); //申请新的结点
//将数据存放在新的节点中
{
s->id=id;
r->next=s;
}
//指针移动
r=s;
//输入下一个数据
printf("输入学号");
scanf("%d",&id);
}
r->next='\0';
return head;
}
//输出函数;
void print(SNODE *head)
{
SNODE *p;
p=head->next; //p指向头节点后的第一个结点
if(p=='0')
print("这是个空链表");
else
{
printf("学生信息为\n");
do
{
printf("%d",p->id);
p=p->next;
}while(p!=NULL);
printf("end\n");
}
}
//头指针为head的链表中在包含x的结点前插入包含元素y的结点的函数(重点)
SNODE *insert(SNODE *head,int x,int y)
{
SNODE *s,*p1,*p2;
s=(SNODE*)malloc(sizeof(SNODE)); //申请新的结点为s
s->id=y; //填充数据
p1=head;
p2=head->next;
while((p2!='\0')&&(p2->next!=x))
{
p1=p2; //p2不断向后移动,p1始终指向p2的前趋结点
p2=p2->next;
}
s->next=p2; //若x存在,则插在x前,若不存在则插入表最后
p1->next=s;
return head;
}