链表:
通过一组任意的存储单元来存储线性表中的数据元素,由一个个结点构成。
链表之所以让初学者难以理解,是因为要回溯到上一个结点,并利用这个结点,很多人的思维一直都是往下面追溯,所以才会觉得难以理解,多动手画画图会容易理解许多。
结点:
结点类型如下:
typedef struct lnode{
datatype data; //数据域
struct lnode *next; //指针域
}LNode,*LinkedList;
定义头指针变量:LinkedList L;
结点示意图:
必须将第一个结点的地址放到一个指针变量如L中,最后一个结点没有后继,其指针域必需置空,表明此表到此结束,这样就介意从第一个结点的地址开始“顺藤摸瓜”,找到每个结点。
头结点:
为了方便满足插入和删除基本操作,我们在链表的头部加入一个“头结点”,如果没有头结点,第一个结点的插入和删除操作跟其他结点不同,需要单独分析,为了省去这个麻烦,因此我们定义了一个头结点,头结点的类型与数据结点一致,标识链表的头指针变量L中存放该结点的地址,这样即使是空表,头指针变量L也不为空了。
头结点的加入使得“第一个结点”的问题不存在了,也使得处理“空表”和“非空表”的操作变的一致。
头结点的加入纯粹是为了使运算方便,它的数据域即data是没有定义的,指针域中存放的是第一个数 据结点的地址,空表时,指针域为NULL
注:表头插入结点不需要头结点,会增加繁琐的操作,表尾插入结点增添头结点会很省事
基本运算:
一、建立单链表
(1)在链表的头部插入结点
插入的过程图:(29,45,18,76,29)链表的建立过程
因为实在链表的头部插入,读入数据的顺序和线性中的逻辑顺序是相反的
插入的代码:
//在表头插入结点
LinkList Create_LinkList1(){
L = NULL; //定义L为空链表
int x; //设数据元素为int类型
LNode s;
scanf("%d",&x);
while(x!=-1){
s = (LNode)malloc(sizeof(LNode)); //申请内存
if(s==NULL){
printf(“申请内存空间失败!”);
break;
}
s->data = x;
s->next = L; //若是第一个结点,则将NULL赋给s,从第二个开始,
//因为是从头部插入,所以s->next指向的是上一轮定义的结点
L = s; //头指针指向最新的结点s
scanf("%d",&x);
}
return L; //返回头指针,通过头指针可以遍历该链表
}
(2)在链表的尾部插入结点
在链表的头部插入结点建立单链表比较简单,但读入数据元素的顺序与生成的链表中元素的顺序是相反的,若希望次序一致,则用尾插入的方法。因为每次是将新结点插入到链表的尾部,所以需要加入一个新的指针r(rear的缩写),该指针r永远指向链表中的尾结点,以便能够将新结点插入到链表的尾部。
插入结点示意图:
算法思路:
初始状态:头指针 L=NULL,尾指针r=NULL。按线性表中元素的顺序依次读入数据元素,非结束标志时,申请结点,将新结点插入到r所指结点的后面,然后r指向新结点。(这里为了方便,该单链表是带有头结点的)
代码:
//在表尾插入结点
LinkList Create_LinkList2(){
L = NULL;
LNode s; //定义结点
LNode r; //定义尾指针,永远指向最后一个结点
int x;
s = (LNode)malloc(sizeof(LNode)); //定义头结点,申请内存
if(s==NULL){
printf(“申请内存空间失败!”);
}
s->next = NULL;
L = s; //头指针指向头结点
r = s; //尾指针指向头结点 注:此时链表里面没有数据结点
scanf("%d",&x);
while(x!=-1){
s = (LNode)malloc(sizeof(LNode));
if(s==NULL){
printf(“申请内存空间失败!”);
break;
}
s->data = x;
s->next = NULL;
r->next = s; //将尾结点的next指向最新的结点
r = s; //尾指针指向最新的结点
scanf("%d",&x);
}
return L;
}
二、求表长
算法思路:
设一个移动指针p和计数器j。初始化后,p所指节点后面若还有结点,p向后移动,计数器自增即++
//获取链表长度(带头结点)
int Length_LinkList1(LinkList L){
LNode *p = L;
int j=0;
while(p->next){
p = p->next;
j++;
}
return j;
}
//获取链表长度(不带头结点)
int Length_LinkList2(LinkList L){
LNode *p = L; //非空表下指向的就是第一个结点
int j=0;
while§{
j++;
p = p->next;
}
return j;
}
时间复杂度均为O(n)
三、查找操作
(1)按序号操作
算法思路:(均为带头结点的情况)
从链表的而第一个元素结点起,判断当前结点是否是第i个,若是,则返回该结点的指针,否则,继续后一个,表结束为止。没有第i个结点时返回空。
//按序号查找单链表中的第i个元素结点,找到返回指针,否则返回空 (带头结点)
LNode *Get_LinkList(LinkList L,int i){
LNode *p = L;
int j=0;
while(j<i&&p->next!=NULL){
p = p->next;
j++;
}
if(j==i)
return p;
else
return NULL;
}
(2)按值操作
算法思路:(均为带头结点的情况)
从链表的而第一个元素结点起,判断当前结点值是否等于x,若是,则返回该结点的指针,否则,继续后一个,直到表结束为止。找不到时返回空。
// 按值查找(带头结点)
LNode *Locate_LinkList(LinkList L,int x){
LNode *p = L->next;
while(p!=NULL&&p->data!=x){
p = p->next;
}
return p;
}
时间复杂度均为O(n)
插入和删除操作都比较容易理解,代码中有注释,这里就不多说了
四、插入操作
//插入(前插结点)(带头结点)(失败返回0,成功返回1)
int Insert_LinkList(LinkList L,int i,int x){
LNode *p,s;
p = Get_LinkList(L,i-1); //获取第i-1个结点
if(p==NULL){
printf(“参数i错误!\n”);
return 0;
}
else{
s = (LNode)malloc(sizeof(LNode)); //申请、填装结点
if(s==NULL){
printf(“申请内存空间失败!”);
return 0;
}
s->data = x;
s->next = p->next;
p->next = s;
return 1;
}
}
五、删除操作
//删除结点
int Delete_LinkList(LinkList L,int i){
LinkList p,s;
p = Get_LinkList(L,i-1); //获取第i-1个节点
if(pNULL){
printf(“第i-1个结点不存在\n”);
return -1;
}else if(p->nextNULL){
printf(“第i个结点不存在”);
return 0;
}else{
s = p->next;
p->next = s->next;
free(s); //释放*s;
return 1;
}
}
六、遍历链表
void Find(LinkList L){
LNode *p = L->next;
int i=0;
while§{
i++;
printf("---->|Node%d->data:%d|\n",i,p->data);
p = p->next;
}
}
附完整的单链表代码:
#include <stdio.h>
#include <malloc.h>
typedef int Elemtype; //Elemtype定义为int型
typedef struct lnode{ //结点
Elemtype data; //数据域
struct lnode *next; //指针域
}LNode,*LinkList; //LNode是结点类型,LinkList是指向LNode类型结点的指针类型
LinkList L; //定义头指针变量
//在表头插入结点
LinkList Create_LinkList1(){
L = NULL; //定义L为空链表
int x; //设数据元素为int类型
LNode s;
scanf("%d",&x);
while(x!=-1){
s = (LNode)malloc(sizeof(LNode)); //申请内存
if(s==NULL){
printf(“申请内存空间失败!”);
break;
}
s->data = x;
s->next = L; //若是第一个结点,则将NULL赋给s,从第二个开始,
//因为是从头部插入,所以s->next指向的是上一轮定义的结点
L = s; //头指针指向最新的结点s
scanf("%d",&x);
}
return L; //返回头指针,通过头指针可以遍历该链表
}
//在表尾插入结点
LinkList Create_LinkList2(){
L = NULL;
LNode s; //定义结点
LNode r; //定义尾指针,永远指向最后一个结点
int x;
s = (LNode)malloc(sizeof(LNode)); //定义头结点,申请内存
if(s==NULL){
printf(“申请内存空间失败!”);
}
s->next = NULL;
L = s; //头指针指向头结点
r = s; //尾指针指向头结点 注:此时链表里面没有数据结点
scanf("%d",&x);
while(x!=-1){
s = (LNode)malloc(sizeof(LNode));
if(s==NULL){
printf(“申请内存空间失败!”);
break;
}
s->data = x;
s->next = NULL;
r->next = s; //将尾结点的next指向最新的结点
r = s; //尾指针指向最新的结点
scanf("%d",&x);
}
return L;
}
//获取链表长度(带头结点)
int Length_LinkList1(LinkList L){
LNode *p = L;
int j=0;
while(p->next){
p = p->next;
j++;
}
return j;
}
//获取链表长度(不带头结点)
int Length_LinkList2(LinkList L){
LNode *p = L; //非空表下指向的就是第一个结点
int j=0;
while§{
j++;
p = p->next;
}
return j;
}
//按序号查找单链表中的第i个元素结点,找到返回指针,否则返回空 (带头结点)
LNode *Get_LinkList(LinkList L,int i){
LNode *p = L;
int j=0;
while(j<i&&p->next!=NULL){
p = p->next;
j++;
}
if(j==i)
return p;
else
return NULL;
}
// 按值查找(带头结点)
LNode *Locate_LinkList(LinkList L,int x){
LNode *p = L->next;
while(p!=NULL&&p->data!=x){
p = p->next;
}
return p;
}
//插入(前插结点)(带头结点)(失败返回0,成功返回1)
int Insert_LinkList(LinkList L,int i,int x){
LNode *p,s;
p = Get_LinkList(L,i-1); //获取第i-1个结点
if(p==NULL){
printf(“参数i错误!\n”);
return 0;
}
else{
s = (LNode)malloc(sizeof(LNode)); //申请、填装结点
if(s==NULL){
printf(“申请内存空间失败!”);
return 0;
}
s->data = x;
s->next = p->next;
p->next = s;
return 1;
}
}
//删除结点
int Delete_LinkList(LinkList L,int i){
LinkList p,s;
p = Get_LinkList(L,i-1); //获取第i-1个节点
if(pNULL){
printf(“第i-1个结点不存在\n”);
return -1;
}else if(p->nextNULL){
printf(“第i个结点不存在”);
return 0;
}else{
s = p->next;
p->next = s->next;
free(s); //释放*s;
return 1;
}
}
//遍历链表
void Find(LinkList L){
LNode *p = L->next;
int i=0;
while§{
i++;
printf("---->|Node%d->data:%d|\n",i,p->data);
p = p->next;
}
}
void list(){
printf(“This is a Singly Linked List.\n------------------------------------------\nPlease press the button:\n”);
printf(“Button 1 —> Create_LinkList()\n”); //创建单链表 (带头结点、表尾插入)
printf(“Button 2 —> Length_LinkList(L)\n”); //获取链表长度
printf(“Button 3 —> Get_LinkList(L,i)\n”); //按序号查找
printf(“Button 4 —> Locate_LinkList(L,x)\n”); //按值查找
printf(“Button 5 —> Insert_LinkList(L,i,number)\n”); //插入结点
printf(“Button 6 —> Delete_LinkList(L,i)\n”); //删除结点
printf(“Button 7 —> Find(L)\n”); //遍历链表
printf(“Button 8 —> Exit the program\n-----------------------------------\n”); //退出程序
}
int main(){
list();
while(true){
printf(“Choose Button: “);
int n;
int flag =1;
int length,i,number,item;//item用于Insert插入和Detele删除的返回数据
LNode *p;
scanf(”%d”,&n);
switch(n){
case 1:
printf(“If you enter ‘-1’,the list is created\n”); //若输入-1,则表示链表元素创建完成
L = Create_LinkList2();
printf("--------------------------------------------------------\n");
break;
case 2:
length = Length_LinkList1(L);
printf("The Singly Link List length is: %d\n-------------------------------------\n",length); //单链表的长度为:
break;
case 3:
printf("Please enter you want to find serial number-->i: ");//请输入你想查找的序号
scanf("%d",&i);
p =Get_LinkList(L,i);
if(p==NULL){
printf("Sorry!Wrong!\n----------------------------------------------\n"); //第i个位置为NULL,错误
}else{
printf("Successsful!The Node data is:%d\n--------------------------------------\n",p->data);//不为NULL,查找成功,输出该结点数据域
}
break;
case 4:
printf("Please enter you want to find number: ");
scanf("%d",&number);
p = Locate_LinkList(L,number);
if(p==NULL)
printf("Sorry!Wrong!\n-------------------------------------------------\n"); //第i个位置为NULL,错误
else
printf("Successsful!The Node data is:%d\n-------------------------------------------\n",p->data);//不为NULL,查找成功,输出该结点数据域
break;
case 5: //插入结点
printf("Please enter i and number: ");
scanf("%d%d",&i,&number);
item = Insert_LinkList(L,i,number);
if(item==0)
printf("Insert failed-----------------------------------------------------\n");
else
printf("Insert successful--------------------------------------------------------\n");
break;
case 6: //删除结点
printf("Please enter i: ");
scanf("%d",&i);
item = Delete_LinkList(L,i);
if(item==0 || item==-1)
printf("Delete failed----------------------------------------------------------\n");
else
printf("Delete successful-------------------------------------------------------------\n");
break;
case 7:
printf("Traverse Singly Linked List:\n"); //遍历该单链表
Find(L);
printf("-------------------------------------------------------------------------\n");
break;
case 8:
printf("Exit the program successful!\n");
flag = -1;
break;
default:
printf("Sorry!You can't do that!\n");
flag = -1;
break;
}
if(flag==-1)
break;
}
}