单链表(完整 含main方法)

链表:

通过一组任意的存储单元来存储线性表中的数据元素,由一个个结点构成。

链表之所以让初学者难以理解,是因为要回溯到上一个结点,并利用这个结点,很多人的思维一直都是往下面追溯,所以才会觉得难以理解,多动手画画图会容易理解许多。

链表示意图:

 

 

结点:

结点类型如下:

typedef struct lnode{

datatype data; //数据域

struct lnode *next; //指针域

}LNode,*LinkedList;

定义头指针变量:LinkedList L;

 

结点示意图:

必须将第一个结点的地址放到一个指针变量如L中,最后一个结点没有后继,其指针域必需置空,表明此表到此结束,这样就介意从第一个结点的地址开始“顺藤摸瓜”,找到每个结点。

 

 

头结点:

为了方便满足插入和删除基本操作,我们在链表的头部加入一个“头结点”,如果没有头结点,第一个结点的插入和删除操作跟其他结点不同,需要单独分析,为了省去这个麻烦,因此我们定义了一个头结点,头结点的类型与数据结点一致,标识链表的头指针变量L中存放该结点的地址,这样即使是空表,头指针变量L也不为空了。

头结点的加入使得“第一个结点”的问题不存在了,也使得处理“空表”和“非空表”的操作变的一致。

头结点的加入纯粹是为了使运算方便,它的数据域即data是没有定义的,指针域中存放的是第一个数 据结点的地址,空表时,指针域为NULL

注:表头插入结点不需要头结点,会增加繁琐的操作,表尾插入结点增添头结点会很省事

 

 

基本运算:

一、建立单链表

(1)在链表的头部插入结点

插入的过程图:(25,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(p){
		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(p==NULL){
		printf("第i-1个结点不存在\n");
		return -1; 
	}else if(p->next==NULL){
		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(p){
		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(p){
		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(p==NULL){
		printf("第i-1个结点不存在\n");
		return -1; 
	}else if(p->next==NULL){
		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(p){
		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:
				if(L==NULL)
				{	
					printf("Sorry!Wrong!\n-------------------------------------------------\n");
					break;
				}
				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);
				if(L==NULL)
				{	
					printf("Sorry!Wrong!\n-------------------------------------------------\n");
					break;
				}
				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);
				if(L==NULL)
				{	
					printf("Sorry!Wrong!\n-------------------------------------------------\n");
					break;
				}
				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");		//遍历该单链表 
				if(L==NULL)
				{	
					printf("Sorry!Wrong!\n-------------------------------------------------\n");
					break;
				}
				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;
	}
 
}

单链表的缺点(最主要):

不具有随机访问的特点,只能从头指针开始一个个顺序操作进行

  • 49
    点赞
  • 232
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值