0.单向链表的创建
链表是数据结构的基础内容,希望这篇文章尽量做到事无巨细,还请大家多多斧正。
1.链表的组成原理
2.单向链表的创建
3.链表的函数实现需要注意的问题
1.链表的组成原理
想想一下一群人排成一条对,后面的人的手搭在前面的人的肩上。
这就构成了一条单向链表。(黑人问号脸???)
解释一下:
这里有四个小朋友编号为1,2,3,4
1把手搭2肩上依次向后然后4的手在空中
这样每个小朋友都有一个编号并且可以知道自己摸的小朋友的编号,除了最后一个小朋友外(指向NULL)
再来看看链表的原理图是不是好理解些了
总结一下单向链表的结构特点(划重点!!)
1.单向链表由多个相互联系的结点(Node)构成
2.每个结点由值和指向下一个结点的指针构成
3.有一个head指针作为标记指向初结点,最后一个结点中的指针指向NULL
是不是很好理解呢?嘿嘿嘿
2.单向链表的创建
啥也不说先上一段代码
typedef struct _node{ int value; struct _node *next; }Node;
知识点:结构类型
创建了一个结构类型名字叫Node,这个结构中含有链表节点中的值与指针两个部分,我们以int类型的值举例
好了,我们创建好了单个节点了,现在我们要做的就是要把这些节点连起来!
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct _node{ 5 int value; 6 struct _node *next; 7 }Node; 8 9 int main(int argc, char const *argv[]) 10 { 11 Node * head=NULL; 12 int number; 13 do{ 14 scanf("%d",&number); 15 if(number!=-1){ 16 //将number插入linklist 17 Node *p =(Node*)malloc(sizeof(Node)); 18 //相当于创建了一个指向Node类型的指针并且开辟了一块空间储存Node结构所含变量 19 //malloc 返回结果为void*而需要Node*需要强制类型转换 20 p->value = number; 21 p->next = NULL; 22 23 Node *last=head; 24 if(last){ 25 while(last->next){ 26 last = last->next; 27 } 28 last->next = p; 29 }else{ 30 head = p; 31 } 32 } 33 }while(number!=-1); 34 return 0; 35 }
我们打算创建一个由从键盘中读取的整数组成的链表,每个新数据储存在上一个数据后面,直到读到-1结束。
下面我分行来解释一下。
基础知识点:动态分配内存,结构指针
11:声明一个指向Node的指针作为head 注意:这里head=NULL是必须的,未赋值的指针是很危险的,将随机指向任何一内存。
13:do-while循环很好理解,连续从键盘读取数字,然后执行循环体,直到-1时结束,-1将被抛弃。
17-21:敲黑板!重点来了。我们现在读取了一个number的量,我们需要把这个量放到一个节点Node里面,我们怎么办呢?
第一步:创造一个节点
我们这里是用的是为指针动态分配内存的形式创造的结点,你可能说为什么不像下面写,一定要用到malloc
Node a;
Node *p=a;
原因就是动态!动态!动态!链表的最大特点就是其动态性,以后的删除就依赖这一特性,如果你没用malloc,就无法free掉这块内存,那你声明的这个结点未来就会永远存在,就算删除了也依旧会占据内存。
第二步:为这个节点赋值
这个容易理解,用刚声明的指针为这个结点的value和next赋值,由于我们是从末尾添加结点,所以next指向NULL
23-31:制造出了一个含有数据的结点然后我们需要干嘛?是这些结点形成关联,这是链表的核心。这里我的想法是,每要添加一个结点时,做一遍结点的遍历,找到最后的一个结点,然后把最后一个结点的next从NULL指向新的结点就可以了。
灵魂画师出场解释一下:
再敲黑板!!!这里尤其要注意第一个结点添加的情况
1 Node *last=head; 2 if(last){ 3 while(last->next){ 4 last = last->next; 5 } 6 last->next = p; 7 }else{ 8 head = p; 9 }
第一步:声明一个指针等于head,指向这个链表的第一个结点。
注意:当创建第一个结点时,我们的head实际上指向的是NULL,记得最开始创建head时候说的话吗。
这个时候我们是无法对一个空指针进行操作(->)的,所以我们用if(last)判断last是否为空,如果是的话就执行else中的head=p,让head指向了我们的第一个结点
第二步:从读入第二个数据开始,就可以开始遍历啦
刚才的last指向的是第一个结构,如果last->next不为NULL那么他就不是最后一个结构
那么就让last=last->next,指向下一个结构,直到last->next为NULL时,使Last->next=p,就完成了新增结点的链接。
链表创建大概就是这样啦!
撒花撒花,给灵魂画师点个赞,是不是很好理解呢~~~
3.链表创建的函数实现需要注意的问题
函数实现的意义我就不多说了,这里尤其要注意传址与传值的问题。
先上代码好喽
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct _node{ 5 int value; 6 struct _node *next; 7 }Node; 8 9 typedef struct _list 10 { 11 Node *head; 12 }List; 13 void creat_node(List *plist,int number); 14 15 int main(int argc, char const *argv[]) 16 { 17 List list1;//为链表1单独创建了一个结构 18 list1.head=NULL; 19 20 int number; 21 do{ 22 scanf("%d",&number); 23 if(number!=-1) 24 creat_node(&list1,number);//传入链表1的地址,指针指向该地址 25 }while(number!=-1); 26 } 27 void creat_node(List *plist,int number) 28 { 29 30 Node *p=(Node *)malloc(sizeof(Node)); 31 p->value=number; 32 p->next=NULL; 33 34 Node *last=plist->head; 35 if(last) 36 { 37 while(last->next) 38 last=last->next; 39 last->next=p; 40 } 41 else 42 plist->head=p; 43 }
还是刚才那个情景哈,读入数字直到-1。
怎么看起来变复杂了,不怕不怕,其实没啥。
typedef struct _list { Node *head; }List;
这一步是给每个链表定义了一个结构类型List,也就是说链表终于有自己的名字啦,以后可以定义List1 List2 List小王八等等等了
每个List类型里面包含了这个链表的head指针,确定了链表的身份。
void creat_node(List *plist,int number);
想用一个函数完成结点的创建和链接,这个函数需要什么参数呢?
很容易想到,需要一个链表,和希望写入结点的值。
我们知道,c语言函数传值的时候是拷贝传值,就是说将原值拷贝一份传入这个函数进行操作,我们是无法改变原值的,而我们现在是要改变原来的链表,就不能使用传值这种方式,所以我们得向这个函数传入原链表的地址。再用一个指针指向这个地址,完成对链表的修改。
1 void creat_list(List *plist,int number) 2 { 3 4 Node *p=(Node *)malloc(sizeof(Node)); 5 p->value=number; 6 p->next=NULL; 7 8 Node *last=plist->head; 9 if(last) 10 { 11 while(last->next) 12 last=last->next;//链接 13 last->next=p; 14 } 15 else 16 plist->head=p; 17 }
既然传来了的是地址,那么函数中也要发生相应变化。
指针plist指向链表list1
创造结点过程还是没有变。
由于我们新建了一个链表的类型
所以原来Node*last=head变成了Node*last=plist->head。
后面遍历结点的过程就和前文的过程一样了。
传址是后续添加删除查找等操作的基础,后面的操作都会用函数的方式写了。
以上