目录
以自行车的链条来比喻我们的单链表,或者双链表
单链表是线性表的一种,因为从逻辑上来看单链表是一条线
节点由指针域和数据域组成,所以一个节点含有两个不同类型的元素,那么我们学过的知识中,那种数据类型可以
保存不同类型的元素呢,那就是结构体,所以我们可以用结构体来定义一个节点
struct node
{
int id;//数据域,节点要保存的数据
struct node*next://指针成员,用于指向下一节点
};
单链表只要知道一个节点,那么就可以知道之后的任意节点位置,直接通过指针域就可以查找到,所以对于
单链表来说,指针域是灵魂,顺序表的内存地址都是连续的,那么增加删除数据就非常的麻烦,所以我们用
单链表,不需要元素都相邻,用一个指针域来找到其他节点,这样即使不相邻,也可以找到单链表中所有的元素
一:什么是链表:
链表是数据结构中线性表的一种,其中的每个元素实际上是一个单独的结构体对象,而所有对象都通过每个
元素中的指针连接在一起,它是以结构体为节点,将一个结构体看成数据域和指针域两个部分,数据域用于
存储数据,指针域用于链接下一个节点,链表中每个结构体对象叫做节点,其中第一个数据节点叫做链表的
首元节点,如果第一个节点不用于存储数据,只用于代表链表的起始点,则这个节点称为链表的头结点
也就是说头节点和首元节点的区别就在于,是否存储数据,而头结点并不是单链表的必需,可以有也可以没有
不保存数据,仅仅是作为单链表的开头,指向单链表,我们叫它头节点
二:单链表的特点:
1:单链表没有固定的长度,可以自由增加节点
2:单链表能够实现快速的插入删除数据
3:与数组类似,单链表也是一种线性数据结构
4:单链表的尾节点的后继必定指向空
单链表和数组的区别就在于:数组是顺序存储的,这就是为什么可以用数组表示一个顺序表,而单链表是链式存储的
三:单链表头节点的初始化:
头指针变量:保存单链表节点的首地址,因为知道了单链表节点的首地址,也就是第一个元素的地址,那么根据节点的指针域,依次寻找就可以找到我们想要的任意元素
struct node*head = NULL;
初始化单链表:
申请一个首元节点:让head指向这个节点
定义了头指针变量,就是为了让这个头指针变量保存首元节点的内存地址,那么我们现在还没有一个首元节点
所以我们要创建一个首元节点,让这个头指针指向这个首元节点,那么我们就先申请一个首元节点呗,那就定
义一个函数,用来申请首元节点呗,然后把这个首元节点的地址返回出来,交给头节点指针保存
struct node*init()
{
struct node*temp = (struct node*)malloc(sizeof(struct node))//temp是一个临时变量,用来保存从
//堆区中申请出来的内存的首地址
temp->id = 0;
temp->next = NULL;
return temp;
}
head = init()//通过调用inti函数,将申请的首元节点的地址temp交给头指针节点head保存,这样我们就有了
//一个首元节点*temp,需要注意的是虽然我们单独定义了一个函数用来申请首元节点,但是仅仅是申请了这样
//一个地址,并不代表我们就拥有了一个首元节点,首元节点只有是属于单链表中的时候才有意义,所以要把
//首元节点的地址交给头指针变量head保存,这样和head共同构成一个含有两个节点:头节点,首元节点的单
//链表
元素得增加:
头插法思想:从前面开始插入,可以理解为排队,大家都想排第一,那么来插队的也肯定插队首的位置,
头插法三步骤:以单链表中只有两个节点:头节点和首元节点为例子,插入的元素先和后面的元素
相连,插入的元素变为首元节点,原本的首元节点和头结点的连接断开,头结点改为连接新来的首元节点,而
首元节点此时连着原本的首元节点
具体表现为:头节点连接新首元节点连接旧首元节点
头插法三步骤:
1;申请节点,把数据放进这个新节点s
2:新节点s指针域保存头结点的下一个节点的地址
3:头结点指针保存新节点s的地址
代码体现:
void insert(struct node*head, int date)//head 用来接收首元节点的地址,date用来接收要增加的元素
{
struct node*s = (struct node*)malloc(sizeof(struct node));
s->id = date;//保存要增加的元素值
s->next = head->next;//新节点保存申请的头结点
head ->next= s;
}
尾插法三步骤:
1:定义一个指针变量P,P用来保存当前单链表中的最后一个节点的地址
2:定义一个指针变量s,申请一个新的节点(保存要增加的数据,生成新节点s)
3:新节点s指针为空,节点P的指针域指向新节点s
void inserEnd(struct node*head, int date)//date用来接收新增加的数据
{
struct node*p = head;
while (p->next != NULL)//p不是单链表中最后一个节点
{
//p++;不可以的,因为内存不连续
p = p->next;//P指向下一个节点
}
//循环执行完之后,P指向单链表中的最后一个节点
//定义一个指针变量S,申请一个新的节点(保存要增加的数据),生成新的节点S
struct node*s = (struct node*)malloc(sizeof(struct node));
s->id = date;//新节点保存要增加的元素值
//新节点S指针为空,节点P的指针域指向新节点S
s->next = p->next;
p->next = s;
}
输出单链表中所有的元素
void print(struct node*head)
{
struct node*p = head->next;
while (p->next != NULL)
{
printf("%d -->", p->id);
p = p->next;
}
printf("NULL\n\n");
}
单链表元素的删除:
1:定义两个指针变量p1 p2 p1指向头节点,p2指向头结点的下一个节点
2:使用循环,使p1,p2同时往后移,当P2指向要删除的这个节点时,结束循环
3:P1指向的节点里面的指针,保存P2下一个节点的地址
4:释放P2指向的节点
代码:
void del(struct node*head, int val)//val表示要被删除的值
{
//P1指向删除节点的前驱节点
//P2指向删除节点
struct node*P1 = head;
struct node*P2 = head->next;
while (p2 != NULL)//p2不为NULL,正在遍历单链表中的数据
{
if (p2->id == val)//当P2指向要被删除的节点时
{
p1->next = p2->next;
free(p2);
p2 = p1->next;
}
else//两个指针变量同时往后移
{
p1 = p1->next;
p2 = p2->next;
}
}
}
单链表元素的查找
int find(struct node*head, int val)//val表示要查找的值
{
int flag = 0;//标记是第几个节点
struct node*p = head->next;
while (p != NULL)
{
flag++;
if (p->id = val)//找到要查找的值
{
printf("这个值在第%个位置\n\n", flag);
return p->id;
}
p = p->next;
}
}
单链表节点的释放:不用考虑链表断没断,只要全部释放就好了。