C解析之十链表进阶
前言:最直观的图解,解链表最迷惑之点。下面将介绍链表的核心操作:插入,删除,查询,掌握这些操作,链表便可在你的程序中大放光彩啦。
1.链表的插入:将新的节点插入到链表指定的位置。
如下图:
等待插入的新节点(p),插入链表两个节点之间(左边为p1,右边为p2),完成这一操作需要两步:1.将p1的指针指向等待插入的节点;2将等待插入节点的指针指向p2。(如图)。
上图数字1,2分别指示了这两个操作步骤。插入节点的关键代码:
p1->next = p;
p->next = p2;
2.链表的删除: 将一个节点从链表删除。
如下图:
删除节点操作是上一步插入节点的逆操作。删除节点P,只需要把P1的指针指向P2,P便不在属于链表了。(如图)。
上图的数字1指示了删除所需的一步操作。有同学可能觉得,P的指针还指向P2呢,这丝毫不能改变P已经被链表删除了的事实。链表删除操作的核心代码:
P1->next=P->next; //P-next指向的是P2
或者 :P1->next=P2;
3.链表的查询:遍历链表查找满足特定要求的节点。
链表的查询,需要遍历每一个节点,那么在遍历的过程中同样可以输出链表,遍历的最后可以找到链表的末尾(此处同时解释【C解析之九】链表初探中最后读者可能存在疑问的链表输出与找到链表的末尾)。
如下图:
链表习惯上表头Head不存储数据,Head之后的节点才真正是存有数据的节点,不管是查询时的比较,还是遍历时的输出都是操作链表节点内的数据。我们定义一个指针,通过指针的偏移(依次指向每个节点)来访问所有的节点。
依次分析:
数字1: 将指针指向第一个链表节点,代码:P=Head->next;
数字2: 指针指向下一个链表节点, 代码:P=P->next;
数字3: 指针继续指向下一个节点, 代码:P=P->next;
......
最后的结果:P-next=NULL,此时P指向的加点便是末尾的节点,这可以成为控制while循环的条件之一。
继续扩展上一篇的例子:这个例子是初学者进一步学习C的良好例子,有兴趣的同学可以试试。掌握这个例子,C语言可以说已经初步入门了。同时,这个例子对链表操作而言,可谓五脏俱全,借助上面的图解仔细阅读,理解其中的关系。笔者认为,链表的操作,能极好地锻炼逻辑思维,同时考验开发者的细致程度。为便初学者,笔者决定不再增加更多的干扰,此程序便不做模块化处理了。
#include
#include
//************
//定义链表节点
//************
struct Node{
int id; //id为学号,Score为成绩
int Score; //id,Score为数据区
struct Node * next; //指针next,指向下一个节点,故类型为Node *
};
//************
//定义链表头
//************
Node *Head;
int main(){
Node *temp;
Node *p1;
Node *p2;
Head=(Node *)malloc(sizeof(Node));
if(Head!=NULL){ //检查是否申请空间成功
Head->next=NULL; //只有头节点,故此时指向空NULL
Head->id=0; //赋值id
Head->Score=0; //赋值Score,一般而言,头节点不用于存储数据,此处均象征性赋值为0
}
printf("现在创建含4带个节点的链表:\n");
for(int i=0;i<4;i++){
//************
//新建一个节点并赋值
//************
temp=(Node *)malloc(sizeof(Node));
if(temp!=NULL){ //检查是否申请空间成功
temp->next=NULL;
printf("请输入id与Score:\n");
scanf("%d %d",&temp->id,&temp->Score);
}
//************
//将节点添加到链表末尾
//************
p1=Head;
while(p1->next!=NULL)
{
p1=p1->next; //找到链表的末尾
}
p1->next=temp; //将节添加到链表的末尾
//printf("*****%d",Head->next->id);
}
printf("现在输出链表中的4个节点:\n");
//************
//输出链表
//************
p1=Head->next;
while(p1!=NULL){
printf("id为%d,成绩为%d\n",p1->id,p1->Score);
p1=p1->next;
}
//*********
//查找节点
//*********
printf("请输入你要查找的学生id:\n");
int tempid;
scanf("%d",&tempid);
p1=Head->next;
while(p1!=NULL) //到链表末尾或找到该id则停止循环
{
if(p1->id==tempid) break;
p1=p1->next;
}
if(p1!=NULL){ //如果不是到链表末尾,当然就是找到该id
printf("找到该id学生,其分数为%d:\n",p1->Score);
}
else{ //到末尾了,说明该id没找到
printf("没有找到该id的学生\n");
}
//**********
//删除节点
//**********
printf("请输入你要删除的学生id:\n");
scanf("%d",&tempid);
p1=Head;
while(p1!=NULL)
{
if(p1->id==tempid) break;
p2=p1; //记录要删除节点的上一个加点
p1=p1->next;
}
if(p1!=NULL){
p2->next=p1->next; //删除的核心代码
free(p1);
}
else{
printf("没有找到该id的学生\n");
}
//************
//输出链表
//************
p1=Head->next; //用于验证是否删除成功
while(p1!=NULL){
printf("id为%d,成绩为%d\n",p1->id,p1->Score);
p1=p1->next;
}
return 0;
}