单链表初探
——链表的节点其实是一个结构体,里边分为两部分:
typedef struct _node
{
int value;
struct _node *next;
}Node,*LinkList;
- 一部分为数据部分,可以是任何的数据类型(int、double、float、char、char*、[]...)
我们引出链表就是为了储存数据(大量,少量的data数组就ojbk),这部分就是其实也就是我们要储存的data。int value;
- 另一部分就是一个比较有意思的东西了struct pointer ,It's amazing;→不要走开哦
struct _node *next;
- ←节点的结构,V是data,*是结构指针。
- 其实链表的出现是弥补数组的不足的,在我们实际开发中,我们用数组的时候,数组的大小是我们原先设定好的,这就很具有局限性了,我们数据很多的时候,数组往往是满足不了我们的需求的,于是前辈的大佬们就想办法解决这一问题,其实,在链表出现之前出现的是动态数组。前辈们想在开发过程中,设计一个能变长的数组,来满足实际开发的需要。这是另一个故事...
- 接下来实际应用链表
操作单链表
一般我们初始一个头指针作为第一个节点,然后其head->next所指向NULL来表示一条完整的链(有头有尾),那么创建这个链子就有两种方法了,一种从头往后插入数据,一种从尾插入,称为头插法和尾插法。
- 头插法创建单链表
void CreateList_H(LinkList head){
LinkList p;
int num;
while(scanf("%d",&num),num!=-1){
p=(LinkList)malloc(sizeof(Node));
p->value=num;
p->next=head->next;
head->next=p;
}
}
①head开始是一个空指针,head->next=NULL,所谓头插,只需把所要储存的数据接到head后边即可。
②p->next=head->next; p结构的next指针指向head结构的next指针所指的内存区域。
③然后,head->next=p;head结构的next指针指向p结构。
- 尾插法创建单链表
void CreateList_T(LinkList head)
{
LinkList p,rear=head;
int num;
while(scanf("%d",&num),num!=-1){
p=(LinkList)malloc(sizeof(Node));
p->value=num;
rear->next=p;
rear=p;
}
rear->next=NULL;
}
①尾插法与头插相反,是与最后一个结构节点进行一系列操作。
②首先定义一个结构指针rear,用它来指向最后一节的元素,首先它指向head结构,新节点p需要插在rear后边,rear->next=p;然后将rear指向最后的p结构。
③创建完毕后,rear的next指针指向NULL,表示完整链。
- 遍历单链表
void PrintList(LinkList head){
LinkList p=head->next;
while(p){
printf("%d ",p->value);
p=p->next;
}
}
①声明一个指针指向head后边的第一个元素
②p不为NULL,输出p->value;
③p=p->next; p后移
- 单链表的插入
void ListInsert(LinkList head,int i,int e){
LinkList p,pre=head;
int j=0;
while(pre&&j<i-1)//找到i-1位置的元素指针
{
pre=pre->next;
j++;
}
if(!pre||i<1){//判断所找元素位置是否存在
printf("i的值有误!\n");
return;
}
p=(LinkList)malloc(sizeof(Node));
p->value=e;
p->next=pre->next;
pre->next=p;
}
①在链表中,确定新元素应该被加入到哪个节点后边,该节点成为前驱节点,用结构指针pre指向
②创建新的节点,使用p指向
③p->next=pre->next;新节点的next指针指向前驱节点的next所指
④pre->next=p;前驱节点的next指向p结构
- 单链表的删除
void ListDelete(LinkList head,int i,int *e)
{
LinkList p,pre=head;
int j=0;
while(pre&&j<i-1){
pre=pre->next;
j++;
}
if(!pre||i<1){
printf("i值有误!!");
return;
}
p=pre->next;
pre->next=p->next;
e=&p->value;
free(p);
}
①定位要删除的节点p的前驱节点pre
②改变前驱节点的next指针绕过p指向p的next所指
③free函数释放p,把删除数据暂存e所指内存区
- 单链表合并
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define LEN 100
typedef struct Lnode
{
int value;
struct Lnode *next;
}Lnode,*LinkList;
void createlist_A(LinkList La)//头插法创建单链表La
{
LinkList p;
int value;
printf("创建单链表La:\n");
while(scanf("%d",&value)&&value!=-1)
{
p=(LinkList)malloc(sizeof(Lnode));
p->value=value;
p->next=La->next;
La->next=p;
}
}
void createlist_B(LinkList Lb)//头插法创建单链表Lb
{
LinkList p;
int value;
printf("创建单链表Lb:\n");
while(scanf("%d",&value)&&value!=-1)
{
p=(LinkList)malloc(sizeof(Lnode));
p->value=value;
p->next=Lb->next;
Lb->next=p;
}
}
void printList(LinkList Lhead)
{
LinkList p=Lhead->next;
while(p)
{
printf("%d ",p->value);
p=p->next;
}
printf("\n");
}
void MergeList(LinkList La,LinkList Lb,LinkList Lc)//两个单链表合并
{
LinkList p,q,r;
p=La->next;q=Lb->next;
Lc=r=La;
while(p&&q)
{
if(p->value<q->value)
{
r->next=p;
r=p; //类似尾插法创建链表
p=p->next;
}
else if(p->value<q->value)
{
r->next=q;
r=q;
q=q->next;
}
else if(p->value==q->value)
{
r->next=p;
r=p;
p=p->next;
q=q->next;
}
}
while(p)
{
r->next=p;
r=p;
p=p->next;
}
while(q)
{
r->next=q;
r=q;
q=q->next;
}
r->next=NULL;
printf("合并后链表Lc的内容为:\n");
printList(Lc);
}
int main(){
LinkList La=NULL,Lb=NULL,Lc=NULL;
Lc=(LinkList)malloc(sizeof(Lnode));
Lc->next=NULL;
La=(LinkList)malloc(sizeof(Lnode));
La->next=NULL;
Lb=(LinkList)malloc(sizeof(Lnode));
Lb->next=NULL;
createlist_A(La);
createlist_B(Lb);
printf("链表La的内容为:\n");
printList(La);
printf("链表La的内容为:\n");
printList(Lb);
MergeList(La,Lb,Lc);
return 0;
}
①创建链表La,Lb,Lc,初始化起始结点不能在局部函数中写,写在main函数中。
②用指针Lc创建一个新的链表作为合并的链表。不需要另建新的表的结点空间,而只需将原来两个链表中结点之间的关系解除,重新按元素值非递减的关系将所有结点链接成一个新的链表即可。
⭐头插法创建的链表是倒序的,先输入的在后边结点。