链表
引言:首先我们要知道什么是链表?
百度百科中对链表的定义为:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
其次要了解链表的创造,输出,插入,删去等操作。
(本篇文章仅讨论单向链表)
单向链表
引言:顾名思义单项链表就是只有一个方向由首节点指向尾节点的链表,可以形象的把链表理解为一串穿着许多珠子的线,其中的珠子有1,2到K的序号,而我们在找珠子的时候,只能从第一个摸到最后一个。如果把线剪断,珠子就会零散到各个地方,但是珠子仍然存在,这个时候它们只是一堆占用内存而没有用处的垃圾,但是如果我们把它们串起来珠子就会变成项链,从而具备不一样的价值。
那么就让我们来看看如何来创造链表吧!
1.链表的组成与插入: 一个链表是由有限个节点组成的,而每个节点包含了一个储存值的空间和指向下一节点的指针,通过每个指针的指向,确定了一条具有顺序的单链表,看到这里聪明的你一定想到了用结构体作为节点的载体。
那么结构体应该如何插入呢?既然节点中的指针是连接每个链表的存在,那么我们只需要改变链表的指针,让它指向下一个节点的地址即可。
代码:
typedef struct node{
int date;//存放数据
struct node *next;//存放一个指向node结构体的指针。
}lnode,*linked;//定义一个结构体名字和结构体指针。
2.链表的创建: 单链表的创建一共有两种方法:头插法和尾插法。
- 头插法顾名思义就是从头插入(听起来像句废话),可以将头插法理解为是许多小朋友去游乐场坐过山车的过程,我们先建立一个首节点,作为检票员,此时的售票员是站着不动的而小朋友是会动的,所有的小朋友都要通过这个检票员到过山车上去,同时我们规定最先进入的小朋友只能坐最后一个并且每个小朋友要把票交给检票员,那么一个个小朋友通过检票员坐到过山车上,那么当所有的小朋友坐上过山车,检票员拿着的票就可以代表他们在过山车上的序列了并且售票员手上的第一张票是最后一个上车的同学。此时节点按插入顺序倒叙排列也就相当于所有的节点都是从上一个节点的头部插入。
代码:
linked get_linkedlist()//建立一个返回值为指针的函数,用来创建节点,本质上它链表是离散存在的,只是人为的给它们建立了联系,它有无意义取决于我们,这点与数组不同。
{
//用头插法建立节点的链表。
linked H,p;
H=(lnode*)malloc(sizeof(lnode));//建立一个头节点,与通道。
H->next=NULL;//要让该节点指向0即末尾位置。
scanf("%d",&x);//输入要存的数据
while(x!=flag)
{
p=(lnode*)malloc(sizeof(lnode));//生成一个新节点。
p->date=x;
p->next=H->next;
H->next=p;
//用这个新的节点的指针指向是H所指向的地址,那么新的节点就排在上一个节点的前面.
//值得注意的是这个时候新节点指向的是上一个节点的位置,而不是H的地址。
//H存在但只是作为一个通道和存放最终头节点的容器,就像游乐场项目的检票员一样所有的人都经过它最后第一个人排到它的面前。
scanf("%d",&x);
}
return H;//返回头部链表
}
- 尾插法顾名思义就是从后面插入(这好像也是句废话,但是要和上面的废话对应),可以将尾插法记作是许多小朋友去游乐场坐过山车的过程,我们先建立一个首节点,作为检票员,所有的小朋友都要在你面前排队,先来的小朋友排在前面,后来的小朋友排在后面,此时的售票员是会动的小朋友是不会动的并且收到哪里就站在谁的后面,我们规定检票员走动收票的时候,先收第一个人的票就站在第一个小朋友的后面,如果第二个小朋友来就收第二个小朋友的票并站在第二个小朋友后面,以此类推,当收票员收完票站在最后一个小朋友小朋友身后时,拿着的是第一张到最后一张小朋友的票,此时的节点按插入顺序排列,相当于每个节点在之前节点的尾部插入,相信聪明的你已经想到那么过程就需要经过收票,换位置的过程对吗?
linked get_linkedlist()//建立一个返回值为指针的函数,用来创建并联系节点
{
//用尾插法建立带头结点的单链表;
linked H,r,p;//创造两个指针,H作为该链表的头部,r是尾指针控制尾部尾部的指向,也就是导游站到谁的后面,p是小朋友生成器
H=(lnode*)malloc(sizeof(lnode));//建立名字为H的头部节点。
H->next=NULL;//初始化头节点,让它指向NULL,即链表的末尾
r=H;//这个时候导游开始换位置,虽然换了个寂寞
scanf("%d",&x);//储存的第一个数据,也就是第一个小朋友。
while(x!=flag)//设置结束标志
{
p=(lnode*)malloc(sizeof(lnode));//生成节点并且生成带有节点地址的指针,也就是生成小朋友和他们的票
p->date=x;//存入数据
r->next=p;//收票
r=p;//换位置,保持售票员一直在最后面。
scanf("%d",&x);
//在每一次的收票的过程中建立联系
}//此时链表是没有收尾的,也就是收票员不知道还有没有小朋友过来
r->next=NULL;//设置尾指针指向NULL,即告诉收票员没有人来了
//如果不收尾就会陷入死循环
return H;//返回头部链表
}
2.链表节点的查找: 我们要怎么查找结点呢,暴力是最好的办法,从第一个一个一个摸,在链表结束前摸到了就摸到了,没摸到就每摸到,是不是十分暴力,为什么是最好的办法,是因为不知道别的办法,聪明的你一定能力理解。
理解万岁
linked Locate(linked H,int K)//输入链表第一个节点的地址,和要查找的数据,返回找到节点的地址。
{
//查找的过程实际上是找节点的过程,只有找到节点才能找到数据,但是有是用数据来找到节点,所以数据节点的关系是,用数据找到节点并返回节点地址,再用节点地址去找到数据
lnode *p;
p=H->next;
while(p!=NULL&&p->date!=K)//判定条件要先判断让链走到最后的条件再判断不相等的条件
{
p=p->next;
}
if(!p){printf("no this point");return NULL;}//对应没有找到的情况
else return p;//对应找到的情况
}
3.链表的输出: 我们已经有了一串珠子,也就是已经创造了一个链表了,那么我们只需从找到第一个从前往后的输出直到末尾结束即可。
void Out(linked H)//输出部分
{
lnode *p;//创造一个索引。
p=H->next;
while(p!=NULL)
{
printf("%d ",p->date);
p=p->next;
}
}
4.链表的删除: 我们已经有了一串珠子了,那么我们要如何去掉一个珠子呢?我们只要先找到这个珠子的前一个珠子,即前驱,然后让前面的这个珠子连着要删去的珠子的后一个珠子,再把这个要删去的珠子释放掉就可以了,所以核心步骤是找前驱,再进行指针的转换。
输入一个n,这个n代表删掉第几个节点
void work(Linked H,int n)
{
Lnode *p;
int k=0;
p=H;
while(p->next!=NULL)
{
k++;
if(k==n)
{
p->next=p->next->next;
break;
}
p=p->next;
}
}
终言:
- 本文总结了单链表创建,插入,查找,删除的常用方法,当然这种方法是冗杂的(等我阶段考完就再写一篇简洁版的出来。
2.看到这里相信你已经理解,明白了单链表的使用方法,放一段上面的结合版,防止聪明的你不知道怎么应用到主函数中去。
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int date;//存放数据
struct node *next;//存放一个指向node结构体的指针。
}lnode,*linked;//定义一个结构体和指针。
int x,K,flag;//x为输入值,K为要找的节点,flag表示到谁停止
/*linked get_linkedlist()//建立一个返回值为指针的函数,用来创建节点,本质上它链表是离散存在的,只是人为的给它们建立了联系,它有无意义取决于我们,这点与数组不同。
{
//用头插法建立节点的链表。
linked H,p;
H=(lnode*)malloc(sizeof(lnode));//建立一个头节点,与通道。
H->next=NULL;//要让该节点指向0即末尾位置。
scanf("%d",&x);//输入要存的数据
while(x!=flag)
{
p=(lnode*)malloc(sizeof(lnode));//生成一个新节点。
p->date=x;
p->next=H->next;
H->next=p;
//用这个新的节点的指针指向是H所指向的地址,那么新的节点就排在上一个节点的前面.
//值得注意的是这个时候新节点指向的是上一个节点的位置,而不是H的地址。
//H存在但只是作为一个通道和存放最终头节点的容器,就像游乐场项目的检票员一样所有的人都经过它最后第一个人拍到它的面前。
scanf("%d",&x);
}
return H;//返回头部链表
}*/
linked get_linkedlist()//建立一个返回值为指针的函数,用来创建并联系节点
{
//用尾插法建立带头结点的单链表;
linked H,r,p;//创造两个指针,H作为该链表的头部,r是尾指针控制尾部尾部的指向,也就是导游站到谁的后面,p是小朋友生成器
H=(lnode*)malloc(sizeof(lnode));//建立名字为H的头部节点。
H->next=NULL;//初始化头节点,让它指向NULL,即链表的末尾
r=H;//这个时候导游开始换位置,虽然换了个寂寞
scanf("%d",&x);//储存的第一个数据,也就是第一个小朋友。
while(x!=flag)//设置结束标志
{
p=(lnode*)malloc(sizeof(lnode));//生成节点并且生成带有节点地址的指针,也就是生成小朋友和他们的票
p->date=x;//存入数据
r->next=p;//收票
r=p;//换位置,保持售票员一直在最后面。
scanf("%d",&x);
//在每一次的收票的过程中建立联系
}//此时链表是没有收尾的,也就是售票员不知道还有没有小朋友过来
r->next=NULL;//设置尾指针指向NULL,即告诉售票员没有人来了
//如果不收尾就会陷入死循环
return H;//返回头部链表
}
void Out(linked H)//输出部分
{
lnode *p;//创造一个索引。
p=H->next;
while(p!=NULL)
{
printf("%d ",p->date);
p=p->next;
}
}
linked Locate(linked H,int K)//输入链表第一个节点的地址,和要查找的数据,返回找到节点的地址。
{
//查找的过程实际上是找节点的过程,只有找到节点才能找到数据,但是有是用数据来找到节点,所以数据节点的关系是,用数据找到节点并返回节点地址,再用节点地址去找到数据
lnode *p;
p=H->next;
while(p!=NULL&&p->date!=K)//判定条件要先判断让链走到最后的条件再判断不相等的条件
{
p=p->next;
}
if(!p){printf("no this point");return NULL;}//对应没有找到的情况
else return p;//对应找到的情况
}
int main()
{
scanf("%d %d",&K,&flag);
linked H,ans;//创建一个钥匙钥匙连接着链表的头部指针
H=get_linkedlist();//输入
Out(H);//输出
printf("\n");
ans=Locate(H,K);//查找是否存在。
if(!ans)printf("%d ",ans->date);
return 0;
}
//链表本质是创建空间,这个 空间即节点,并再在创建时用一个指针指向,再将这个空间(节点)的地址传给上一个或者下一个空间(节点)的指针,从而形成联系。
/*
10 20
1 8 3 60 11 12 30 20
*/
图片来源:
东北林业大学王育英老师,邮箱:wyy@nefu.edu.cn