单链表

链表是常见的动态存储方法,

链表定义:用一组任意的存储单元来存放线性表的节点,这组存储单元可以是连续的,也可以是非连续的。

结点包括数据域和指针域。

 

数据域是用来存放结点的值,指针域存放数据元素直接后继的地址。

由于每一个线性链表中只有一个next指针域,所以线性链表是单链表。

 

单链表的存储结构

Typedef struct Node  /*结点类型定义*/
{
ElemType  data;
Struct Node * next;
}Node ,*LinkList; /* LinkList为结构指针类型*/

 说明:

1) LinkListNode  *同为结构指针类型。LinkList主要强调是单链表的头指针变量,指向的是头指针。Node  *来定义单链表中结点的指针。

2) L是单链表的头指针,则L是指向单链表的头指针,若单链表是带头结点的指针,则L是指向头结点的指针。

3) 判断是空表:

① 一种是带头结点的空表,L->next==NULL;

② 一种是不带头结点的空表,L==NULL;

4) 链表的元素,若是带头结点的链表,则P=L->next,p是指向第一个元素的结点,p->data就是第一个元素的数据域.

 

单链表的基本运算

1.初始化单链表

InitList(LinkList *L ){
*L =(LinkList )malloc (sizeof (Node));
(*L)->next=NULL;
}


注意

① L是指向头结点的指针,用来接收主程序中待初始化单链表的头指针变量的地址。

② *L相当于主程序中待初始化单链表的头指针变量。

 

2.建立单链表

① 头插法建表

算法思路


//用头插法建立单链表
void CreateFromHead(LinkList   L)
{ 
Node   *s;
char 	c;
int 	flag=1;
printf("用头插法建立线性链表,请输入字符,以$结尾\n"); 
 	while(flag)   // flag初值为1,当输入"$"时,置flag为0,建表结束
{
c=getchar();   
if(c!='$')
{
s=(Node*)malloc(sizeof(Node)); //建立新结点s
s->data=c;
s->next=L->next;//将s结点插入表头,将新结点接入到第一个结点的前面
L->next=s;//将头结点连接新建结点
}
else
flag=0;
}
 
}


② 用尾插法建表

算法思路

① 建空表,同时用H,r指向空表

② 申请新结点并赋值,s指向结点,s->data=c

③ 对新结点操作,r->next=s, r=s;

//用尾插法建立单链表
void CreateFromTail(LinkList  L)
{ 
 	printf("用尾插法建立线性链表,请输入字符,以$结尾\n"); 
Node *r, *s;
char c;
int   flag =1; /*设置一个标志,初值为1,当输入"$"时,flag为0,建表结束*/
r=L;                /*r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点*/
while(flag)         /*循环输入表中元素值,将建立新结点s插入表尾*/
{
c=getchar();
if(c!='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
r->next=s;   //新建结点接入到表尾
r=s;   //r始终指向单链表的表尾
}
else
{
flag=0;
r->next=NULL;   /*将最后一个结点的next链域置为空,表示链表的结束*/
}
}   
} 
 


3.查找

① 按序号查找

算法思路

① 定义一个指针指向头结点,累加器,

② 没有找到就让指针依次往后移,累加器加一,

③ 找到后,输出结点的值

//单链表的查找,按序号查找
Node * Get (LinkList  L, int i)
/*在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储位置; 否则返回NULL*/
{  
int j;
Node  *p;
p=L;
j=0;   /*从头结点开始扫描*/ 
while ((p->next!=NULL)&&(j<i))
{ 
p=p->next;    /* 扫描下一结点*/
j++;   /* 已扫描结点计数器 */
}
if(i == j)
return  p;   /* 找到了第i个结点 */
else 
return  NULL;   /* 找不到,i≤0或i>n */
}   


② 按值查找

算法思路

用一个指针指向第一个结点,让指针所指向的数据域与值相比较,如相同就输出其位置p

//单链表按值查找
Node *Locate( LinkList L,ElemType key)
/*在带头结点的单链表L中查找其结点值等于key的结点,若找到则返回该结点的位置p,否则返回NULL*/ 
{ 
Node *p;
p=L->next;    /*从表中第一个结点开始 */
while (p!=NULL)
{
if (p->data!=key)
p=p->next; 
else  
break;      /*找到结点值=key时退出循环 */
}
return p;
} 

4.求单链表的长度

算法思路

j做累加器,初始值为0,

//求单链表长度
int	ListLength(LinkList L)
/*求带头结点的单链表L的长度*/
{   
Node *p;
int j;
p=L->next;
j=0;   /*用来存放单链表的长度*/
while(p!=NULL)
{	  
p=p->next;
j++;
}
return j;	/*j为求得的单链表长度*/
} 



5.单链表插入操作

在线性表的第i1<= i <=N+1)个位置之前插入一个新元素e.

算法思路:

① 查找,在单链表中找到第i-1个结点并由指针pre指示;

② 申请,申请新结点s,将其数据域的值置为es->data=e

③ 插入挂链,通过修改指针域将新结点s挂入单链表L, s->next=pre->next;

Pre->next=s;

 
//单链表的元素插入
int InsList(LinkList L,int i,ElemType e)
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
{  
Node *pre,*s;
int k;
pre=L;  
k=0;                     /*从"头"开始,查找第i-1个结点*/
while(pre!=NULL&&k<i-1)  /*表未查完且未查到第i-1个时重复,找到pre指向第i-1个*/ 
{ 
pre=pre->next;
k=k+1; 
}	 /*查找第i-1结点*/
if(!pre)      /*如当前位置pre为空表已找完还未数到第i个,说明插入位置不合理*/ 
{ 
printf("插入位置不合理!");
return ERROR;
}
s=(Node*)malloc(sizeof(Node));   /*申请一个新的结点S */
s->data=e;                       /*值e置入s的数据域*/
s->next=pre->next;	 /*修改指针,完成插入操作*/
pre->next=s;
return OK;
}


6.单链表的删除操作


将线性表中第i个元素e删除

① 查找,通过计数方式找到第i-1个结点,并由指针pre指向;

② 删除,删除第i个结点并释放空间,r=pre->next;pre->next=r->next;free(r);

//单链表的删除元素
 
int DelList(LinkList L,int i,ElemType *e)
/*在带头结点的单链表L中删除第i个元素,并将删除的元素保存到变量*e中*/
{  
printf("请输入你要删除的位置:");
scanf("%d",&i);
Node *pre,*r;
int k;
pre=L;
k=0;
while(pre->next!=NULL && k<i-1)	/*寻找被删除结点i的前驱结点i-1使p指向它*/
{ 
pre=pre->next; 
k=k+1;
}	 /*查找第i-1个结点*/
if(!(pre->next))     /* 即while循环是因为p->next=NULL或i<1而跳出的,而是因为没有找到合法的前驱位置,说明删除位置i不合法。*/
{
printf("删除结点的位置i不合理!");
return ERROR;
}
r=pre->next;
pre->next=pre->next->next;    /*修改指针,删除结点r*/
*e = r->data;
free(r);    /*释放被删除的结点所占的内存空间*/
printf("成功删除结点!");
return OK;
}


7.清空线性链表

//清空线性链表
void Destoy(LinkList L){
if(L->next!=NULL){
L->next=NULL;
}
printf("清空成功!");
}


8.显示单链表

//  显示单链表
void   Display(LinkList  L){
printf("线性链表中的元素有:\n"); 
Node *p;
p=L;
while(p->next!=NULL)
{
p=p->next;
printf("%c   ",p->data);
}
printf("\n");
}


9.将单链表进行就地逆置

算法思路:

单链表逆置,是将每一个结点从原链表中摘下来,再以头插法的方式插入

 


 

 

 

//将单链表进行就地逆置
void ReverseList(LinkList L)
{
Node *p,*q;
p=L->next;
L->next=NULL;
while(p!=NULL)
{
q=p->next;
p->next=L->next;
L->next=p;
p=q;
}
}
 


 /*

  
目的:掌握单链表建立、清空、删除、插入、遍历等操作。
要求:用头插法或尾插法建立单链表、将这个链表的内容进行遍历输出,删除某指定位置节点、在某个指定位置插入节点
*/


//单链表的头文件


#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define OK   1
#define ERROR  0
#define TRUE 1
#define FALSE 0

typedef char ElemType;//重新定义char类型的为ElemType
typedef struct Node    /*结点类型定义*/ 
{ 
	ElemType data;
	struct Node  * next;
}Node, *LinkList;  /* LinkList为结构指针类型*/

//初始化单链表
void init_linklist(LinkList *L)/*对单链表进行初始化*/
{
	*L=(LinkList)malloc(sizeof(Node)); /*申请结点空间*/
	(*L)->next=NULL;                      /*置为空表*/
}

//用头插法建立单链表
void CreateFromHead(LinkList   L)
{ 
//	printf("用头插法建立线性链表,请输入字符,以$结尾\n"); 
	Node   *s;
	char 	c;
	int 	flag=1;
	printf("用头插法建立线性链表,请输入字符,以$结尾\n"); 
 	while(flag)   // flag初值为1,当输入"$"时,置flag为0,建表结束
	{
		
		c=getchar();   
		if(c!='$')
		{
			s=(Node*)malloc(sizeof(Node)); //建立新结点s
			s->data=c;
			s->next=L->next;//将s结点插入表头
			L->next=s;
		}
		else
			flag=0;
	}
	 
}

//用尾插法建立单链表
void CreateFromTail(LinkList L)
{ 
 	printf("用尾插法建立线性链表,请输入字符,以$结尾\n"); 
	Node *r, *s;
	char c;
	int   flag =1; /*设置一个标志,初值为1,当输入"$"时,flag为0,建表结束*/
	r=L;                /*r指针动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点*/
	while(flag)         /*循环输入表中元素值,将建立新结点s插入表尾*/
	{
		c=getchar();
		if(c!='$')
		{
			s=(Node*)malloc(sizeof(Node));
			s->data=c;
			r->next=s;
			r=s;
		}
		else
		{
			flag=0;
			r->next=NULL;   /*将最后一个结点的next链域置为空,表示链表的结束*/
		}
	}   
} 

//  显示单链表
void   Display(LinkList  L){
printf("线性链表中的元素有:\n"); 
Node *p;
p=L;
while(p->next!=NULL)
{
		p=p->next;
		printf("%c   ",p->data);
}
printf("\n");
}
//单链表的元素插入
int InsList(LinkList L,int i,ElemType e)
/*在带头结点的单链表L中第i个位置插入值为e的新结点*/
{  
	Node *pre,*s;
	int k;
	pre=L;  
	k=0;                     /*从"头"开始,查找第i-1个结点*/
	while(pre!=NULL&&k<i-1)  /*表未查完且未查到第i-1个时重复,找到pre指向第i-1个*/ 
	{ 
		pre=pre->next;
		k=k+1; 
	}									/*查找第i-1结点*/
	if(!pre)      /*如当前位置pre为空表已找完还未数到第i个,说明插入位置不合理*/ 
	{ 
		printf("插入位置不合理!");
		return ERROR;
	}
	s=(Node*)malloc(sizeof(Node));   /*申请一个新的结点S */
	s->data=e;                       /*值e置入s的数据域*/
	s->next=pre->next;				/*修改指针,完成插入操作*/
	pre->next=s;
	return OK;
}

//单链表的查找,按序号查找
Node * Get (LinkList  L, int i)
/*在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储位置; 否则返回NULL*/
{  
	int j;
	Node  *p;
	p=L;
	j=0;   /*从头结点开始扫描*/ 
	while ((p->next!=NULL)&&(j<i))
	{ 
		p=p->next;    /* 扫描下一结点*/
		j++;   /* 已扫描结点计数器 */
	}
	if(i == j)
		return p;   /* 找到了第i个结点 */
	else 
		return NULL;   /* 找不到,i≤0或i>n */
}   
//单链表按值查找
Node *Locate( LinkList L,ElemType key)
/*在带头结点的单链表L中查找其结点值等于key的结点,若找到则返回该结点的位置p,否则返回NULL*/ 
{ 
	Node *p;
	p=L->next;    /*从表中第一个结点开始 */
	while (p!=NULL)
	{
		if (p->data!=key)
			p=p->next; 
		else  
			break;      /*找到结点值=key时退出循环 */
	}
	return p;
} 

//求单链表长度
int	ListLength(LinkList L)
/*求带头结点的单链表L的长度*/
{   
	Node *p;
	int j;
	p=L->next;
	j=0;   /*用来存放单链表的长度*/
	while(p!=NULL)
	{	  
		p=p->next;
		j++;
	}
	return j;	/*j为求得的单链表长度*/
}  
//单链表的删除元素

int DelList(LinkList L,int i,ElemType *e)
/*在带头结点的单链表L中删除第i个元素,并将删除的元素保存到变量*e中*/
{  
	printf("请输入你要删除的位置:");
	scanf("%d",&i);
	Node *pre,*r;
	int k;
	pre=L;
	k=0;
	while(pre->next!=NULL && k<i-1)	/*寻找被删除结点i的前驱结点i-1使p指向它*/
	{ 
		pre=pre->next; 
		k=k+1;
	}								/*查找第i-1个结点*/
	if(!(pre->next))     /* 即while循环是因为p->next=NULL或i<1而跳出的,而是因为没有找到合法的前驱位置,说明删除位置i不合法。*/
	{
		printf("删除结点的位置i不合理!");
		return ERROR;
	}
	r=pre->next;
	pre->next=pre->next->next;    /*修改指针,删除结点r*/
	*e = r->data;
	free(r);    /*释放被删除的结点所占的内存空间*/
	printf("成功删除结点!");
	return OK;
}

//清空线性链表
void Destoy(LinkList L){
	if(L->next!=NULL){
		L->next=NULL;
	}
	printf("清空成功!");
}
//将单链表进行就地逆置
void ReverseList(LinkList L)
{
	Node *p,*q;
	p=L->next;
	L->next=NULL;
	while(p!=NULL)
	{
		q=p->next;
		p->next=L->next;
		L->next=p;
		p=q;
	}
}
//显示提示信息
void   Displays()
{
	printf("-----------------------\n");
	printf("1.显示线性表\n");
	printf("2.用头插法建立单链表\n");
	printf("3.用尾插法建立单链表\n");
	printf("4.删除元素\n");
	printf("5.清空线性链表\n");
    	printf("6.将单链表进行就地逆置\n");
 	printf("7.退出\n");
	printf("-----------------------\n");
}
//主函数
int main(){
	LinkList L;
	ElemType *e;
	int x,i=0;
	e=(char*)malloc(sizeof(char));
	Displays();
	printf("请选择功能1-7\n");
	scanf("%d",&x);
	while(1){
		switch(x){
		case 1:Display(L);break;
		case 2:init_linklist(&L);
			CreateFromHead(L);break;
		case 3:
			init_linklist(&L);
			CreateFromTail(L);break;
		case 4:DelList(L, i, e);break;
		case 5:Destoy(L);break;
		case 6:ReverseList( L);break;
		case 7:exit(0);break;
		}
	printf("请选择功能1-7\n");
	scanf("%d",&x);
	}
return 0;
}




 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

evanth_2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值