链表_C语言快速入门与计算机二级备考
链表的概念
-
为了克服数组连续存储难以修改的问题,采用链式存储,将分散的数据连接起来
-
本质上是一种分为两部分的数据,一部分是普通数据,在最后是一个指针,指向下一个同类单元,这下一个单元又能储存数据,并且再指向下一个……而在最开始有一个指针指向第一个的开头,这个指针就是头指针
这两部分分别为“数据域”和“指针域”,两部分结合,成为链表的一个结点
-
链表中第一个节点是首元节点
(有时在它之前还有一个头节点,不存放数据/或者只存放链表长度信息,只有指针指向首元结点,头指针此时指向头节点)
-
下列代码构建的是单向链表
链表的创建
创建链表的结构
-
typedef struct _node { int value;//数据域 struct _node *next;//指针域,这个指针指向struct _node类型的数据 } Node;//用Node作为类型struct _node的名字 //定义一个数据结构_list用以表达整个链表,将其链表的指针保存在全局的结构体中,便于之后使用函数与拓展 typedef struct _list { Node* head; Node* tail;//例如增加一个总是指向链表末尾的tail指针,然后不必遍历去寻找尾结点 } List;
创建链表的函数
-
#include <stdio.h> #include <stdlib.h> void add(List* pList, int number); int main(){ List list; list.head = NULL;//先申请一个新的链表,将指向首元结点的头指针初始化 int number;//储存输入数据 do{ scanf("%d",&number);//为结点输入数据 if(number!=-1)//输入为-1时结束输入 add(&list,number);//传入list的指针与录入的结点数据 }while(number!=-1);//不断输入结点数据,输入为-1时结束 } //将新建结点封装为函数add void add(List* pList, int number)//传入函数的是list的指针 { //先创建一个新结点,写入其数据域与指针域 Node *p=(Node*)malloc(sizeof(Node));//为其分配内存 if(p){ p->value=number;//让输入的number成为新结点中的value p->next=NULL;//作为新加入的结点,它是链表中的最后一个,故其指针无结点可指 } //同时,原先处在末尾的结点现在作为倒数第二个结点,其指针应该指向新加入的这个结点 //要做到这一点,要先找到这个原先末尾的结点是谁 Node *last=pList->head;//创建一个新指针last用来找原末尾结点,先让它指向头结点 if(last)//当last不是NULL时(即链表不是只有头指针的空链表) { while(last->next){ //当结点last指向下一个结点的指针next不是NULL,就说明还有下一个结点 last =last->next;//让last变为下一个结点 //last一开始是head,不断循环到下个结点,当找不到时就是最后的结点了 } last->next=p;//让last中的next等于p,即指向新加入的结点 }else{//要是last是NULL,表示整个链表是空的,只有一个指向NULL的头指针 pList->head=p;//所以新加入的p就应该是第一个结点 }
函数功能的拓展
-
//借助尾指针添加元素,不必使用遍历的方式来寻找最后一个结点,提高了效率 list.head = list.tail = NULL;//在申请新链表同时创建尾指针 //修改后的函数add void add(List* pList, int number) { Node *p=(Node*)malloc(sizeof(Node)); if(p){ p->value=number; p->next=NULL; } Node* last = pList -> head; //先让last与head指向同个结点 if(last==NULL)//last无结点指,即head也无结点指,整个链表为空 { pList -> head = p; pList -> tail = p;//当链表为空,头尾指针都应指向这个新结点 }else{ last = pList -> tail;//让原本与head同指向的last变为与之前保存的tail同指向 last -> next = p;//原来的尾结点last指向新增结点p pList -> tail = p;//新结点变为了新的尾结点,故让tail指向它 //通过head与last的交替指向,不断的在链表尾部连接新增的结点 //tail指针作为一个中间变量,用于全局地存放链表尾结点的地址 } }//如此,不使用遍历也可直接通过tail指针访问到链表尾部,便于新添加结点
对链表的操作
链表的遍历
-
void print(List *pList)//输出链表中每一项 { Node *p;//遍历链表的方法:先创建一个遍历用指针让它指向链表头 while (p!=NULL) { printf("%d",p->value);//递归中对链表各个结点的操作 p=p->next//不断递归指向下个结点,直到p为NULL(无下一个结点) } } print(&list);//使用方法:传入所要遍历链表的指针
链表的搜索
-
//基于遍历 int print(List *pList,number) { Node *p=pList->head; int isFound = 0;//是否找到对应结点的标志 while (p) { if(p->value == number){ printf("找到了"); isFound = 1; break; } p=p->next; } if(!isFound){ printf("没有找到"); } return isFound; }
结点的删除
-
当要删除某一结点时,让前一结点的指针直接越过这个结点指向下一个
之后,把这个结点的内存释放,防止错误
//基于搜索,先找到要删除的结点 //同时,需要知道要删除结点的前一个结点,故需要两个临时指针 void del(List *pList) { Node *p=pList->head; Node *q=NULL;//用q指针来指向要删除结点的前一个结点 while (p) //当p指向了下一个结点时,q等于p之前的值,也就是p的前一结点 { if(p->value == number){ if(q){ q->next = p->next; } else { list.head = p->next; } free(p); break; }//找到要删除的结点后,让q绕过p直接指向p的下一项,再释放p q=p; p=p->next;//对下一结点进行同样的操作 } } //注意:在操作指针所指时(放在->左侧),指针不能为空指针 //p可以确定不为NULL,因为p为NULL时循环终止,不进行操作 //但是,若整个链表的第一项就是要删除的结点,则q指针将为NULL //故在q为NULL时,让p的下一项作为head
链表的清除
-
使用完整个链表后,将之前为结点
molloc
分配的内存清除干净//同样使用两个临时指针,一个指向当前要删除的结点,一个指向下一个删除的结点 void clear(List *pList) { Node *q; Node *p=pList->head; while (p) { q=p->next; free(p); p=q } }
本文来自博客园,作者:无术师,转载请注明原文链接:https://www.cnblogs.com/untit1ed/p/18547876
本文使用知识共享4.0协议许可 CC BY-NC-SA 4.0
请注意: 特别说明版权归属的文章以及不归属于本人的转载内容(如引用的文章与图片)除外