赞
踩
参考:大话数据结构
参考:创建单链表的头插法与尾插法详解
https://blog.csdn.net/NINIYUANJ123/article/details/89847543
个人理解链表的实质为多个结构体变量之间通过指针的连接。刚学数据结构,可能会存在错误。
首先,我们先区分一下头指针和头节点的区别:
图3-6-7是不带头节点的链表,可看到,头指针指向第一个节点的数据域。
图3-6-8/9是带头节点的链表,可看到头指针指向这个头节点的数据域,这个数据域里面可以不存任何信息,添加这个头节点的目的是方便对链表进行操作。
一般称图中a1所在的节点为首节点。
typedef struct student{ //创建链表中的每个节点,包括数据域和指针域;
int score;//数据域;
struct student *next;//指针域;
}Linklist;
这里需要注意的意思typedef关键字,typedef作用是为已有的数据类型定义一个新的类型名称,其语法格式为:typedef 已有类型名 新类型名表;
前面提过结构体也是一种数据类型,相比于我们熟悉的int等等基本类型,结构体类型是一种构造类型,因此可以用typedef为其重新起一个名字。在链表这里,这样做的目的是,之后就可以用新类型名Linklist去定义结构体变量了。
看严蔚敏的书时,发现里面时这样写:
当时对这种写法纠结了很久,后来看了这两篇博客:对链表结构体定义LNode,*LinkList的理解,烂烂三三才理解的。
总的来说,这两种一个时是对结构体类型起别名,一个是对结构体指针类型起别名。相当于:typedef struct Lnode Lnode
,typedef struct Lnode* LinkList
,当我们声明指向该结构体的指针变量时, 使用Lnode * p
与 LinkList L
二者等价的。
书上的目的是,为了区分头节点和其他节点,让LinkList L
特意指向头节点,其他普通节点用Lnode * p
,后面在讲链表相关的操作时容易理解。
本篇笔记的代码没用这个。
先介绍简单的遍历链表的代码,后面的相关操作都会或多或少的包含这个遍历操作,只不过在循环条件那加了一点判断语句。下面这段代码应该是很好理解的。
void link_print(Linklist *list){
Linklist *p = list; //定义一个临时指针p,刚开始时与头指针一样,都指向的是头节点;
while(p!=NULL){
printf("%d\n",p->score);
p = p->next;
}
}
在有头节点和无头结点时,这里遍历操作会存在一点不同,具体可看:
单链表创建且遍历之带头结点与不带头结点
首先说一下我对书中几个概念的理解。
我们要把s节点插入到p节点和p->next节点之间。先进行第1步,把s节点中的指针(也就是s->next)指向p->next节点。对应语句就是 s->next = p->next
。再进行第二步,再把p节点中的指针指向s节点,对应语句是 p->next = s
。
即关于插值的操作,为两句话:
s->next = p->next;
p->next = s;
注意,这两句的顺序不能反过来。假如先是p->next = s
,这里p->next已经是指向了s节点,再进行 s->next = p->next
时,相当于变成了 s->next=s,这样子是矛盾的。故切记这个顺序不能反,然后,就可以通过自己画图推理出这个插入的过程了,而不必反复地去看书了。
在链表中插入节点的代码:
void insert(Linklist *list,int n){
Linklist *p = list;
Linklist *s;
int i = 0;
while(i<n-1&&p->next){
p=p->next;
i++;
}
s = (Linklist*)malloc(sizeof(Linklist));
printf("请输入插入的新节点的数据值:");
scanf("%d",&s->score);
s->next = p->next;
p->next = s;
}
联系第二小节,一个单链表的创建过程实际上就是多个链表插入节点的过程,有点不同之处在于不断在头部插入节点和在不断在尾部插入节点。后面介绍头插法与尾插法时,均以带头节点的单链表为例,可以通过头插法的例子来理解一下加入头节点的好处。
加入头节点后,其实就和第一部分插入节点类似,只不过每次是在头节点和首节点之间插值,已插入的节点依次后移。
理解了第一部分的插入节点,这里也就好理解了。
关于头插法的代码:
#include<stdio.h> #include<malloc.h> typedef struct student{ //创建链表中的每个节点,包括数据域和指针域; int score;//数据域; struct student *next;//指针域; }Linklist; Linklist *creat(int n)//创建链表 ,为指针函数,返回的是一个指针 { Linklist *head,*node; //定义两个结构体指针 head=(Linklist*)malloc(sizeof(Linklist));//为头节点开辟内存空间 for(int i=0;i<n;i++) { node=(Linklist*)malloc(sizeof(Linklist));//为每次新插入的节点分配内存空间; scanf("%d",&node->score); //新节点数据域进行赋值 node->next=head->next; //头插法:新节点中的指针指向head->next节点; head->next=node; //头插法:head中的指针指向新节点; } return head; }
即类似排队,每次新插入的节点都排在后面。这里需要定义一个尾指针r,如图所示。
这个看图也应该容易理解了。
尾插法代码:
Linklist *creat(int n)//创建链表 ,为指针函数,返回的是一个指针
{
Linklist *head,*node,*end; //定义三个结构体指针
head=(Linklist*)malloc(sizeof(Linklist));//为头节点开辟内存空间
end=head; //刚开始时头节点同时也是尾节点
for(int i=0;i<n;i++)
{
node=(Linklist*)malloc(sizeof(Linklist));//为每次新插入的节点分配内存空间;
scanf("%d",&node->score); //新节点数据域进行赋值
end->next=node; //尾插法:尾节点中的指针指向新插入的node
end=node; //尾插法:尾指针挪到新插入的节点上来
}
end->next=NULL; //循环结束后,尾节点中的指针指向空
return head;
}
如下图所示
删除数据ai所在的节点,只需要把
a
i
a_i
ai所在的节点中的指针指向
a
i
+
1
a{_{i+1}}
ai+1所在节点的数据域,用表达式描述即:p->next = p->next->next
,两个箭头的符号在c程序中不存在定义,用q来代替这个p->next,即上式变为:
q = p->next; p->next = q->next;
删除链表中第i个位置节点的代码:
思路是1.从首节点开始,先找(遍历)到第i个节点;2.删除该节点
void delet(Linklist *list, int n){ Linklist *p = list; Linklist *q; int i=1; while(i<n && p->next){ // p = p->next; i++; } if(p->next == NULL){ printf("待删除位置超出表长,不合理\n"); } else{ q=p->next; //用q来代替; p->next = q->next; //p中的指针指向q中的指针 free(q); //释放内存 } }
还有一些操作就不放上来了,理解了上面的思想单链表应该就没问题了。总的代码如下:
#include<stdio.h> #include<malloc.h> #include<string.h> typedef struct student{ int score;//数据域; struct student *next;//指针域; }Linklist; Linklist *creat(int n)//创建链表 ,为指针函数,返回的是一个指针 { Linklist *head,*node,*end; //定义三个结构体指针 head=(Linklist*)malloc(sizeof(Linklist));//为头节点开辟内存空间 end=head; //刚开始时头节点同时也是尾节点 for(int i=0;i<n;i++) { node=(Linklist*)malloc(sizeof(Linklist));//为每次新插入的节点分配内存空间; scanf("%d",&node->score); //新节点数据域进行赋值 end->next=node; //尾插法:尾节点中的指针指向新插入的node end=node; //尾插法:尾指针挪到新插入的节点上来 } end->next=NULL; //循环结束后,尾节点中的指针指向空 return head; } void delet(Linklist *list, int n){ Linklist *p = list; Linklist *q; int i=1; while(i<n && p->next){ // p = p->next; i++; } if(p->next == NULL){ printf("待删除位置超出表长,不合理\n"); } else{ q=p->next; //用q来代替; p->next = q->next; //p中的指针指向q中的指针 free(q); //释放内存 } } void link_print(Linklist *list){ Linklist *p = list; //定义一个临时指针p,刚开始时与头指针一样,都指向的是头节点; while(p->next != NULL){ p = p->next; printf("%d\n",p->score); } } void insert(Linklist *list,int n){ Linklist *p = list; Linklist *s; int i = 0; while(i<n-1&&p->next){ p=p->next; i++; } s = (Linklist*)malloc(sizeof(Linklist)); printf("请输入插入的新节点的数据值:"); scanf("%d",&s->score); s->next = p->next; p->next = s; } int main(){ Linklist *head; int n,i; i=2;//插入和删除的节点位置 printf("请输入初始创建链表的长度:"); scanf("%d\n",&n); head = creat(n); insert(head,i); link_print(head); printf("///\n"); delet(head,i); link_print(head); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。