单链表——c语言

1.单链表的定义

        线性表的链式存储结构是指用一组任意的存储单元(可以连续,也可以不连续)存储线性表中的数据元素。为了反映数据元素之间的逻辑关系,对于每个数据元素不仅要表示它的具体内容,还要附加一个表示它的直接后继元素存储位置的信息,这样构成的链表称为线性单向链接表,简称单链表,其中data部分为数据域,用于存储一个数据元素(结点)的信息。next部分称为指针域,用于存储其直接后继的存储地址的信息。

        

单链表分为带头结点和不带头结点两种类型。

        带头结点的单链表,头结点的数据域可以不存储任何信息,也可以存放特殊的信息;头结点的指针域存储链表中第一个结点的地址。当头结点的指针域为空,则此表为空表。在非空表中,当某个结点的指针域为空,表示它为链表的最后一个结点。

由于单向链表不能随机存取存储的数据,在单向链表中存取第i个元素,必须从头指针出发寻找,其寻找的时间复杂度为O(n)。

2.单链表的类型定义

单链表由一个个结点构成,我们用C语言的结构体指针来描述

typedef int DataType; /* 定义DataType为int类型*/
typedef struct linknode		/* 单链表存储类型 */
{
	DataType data;		/* 定义结点的数据域 */
	struct linknode *next;		/* 定义结点的指针域 */
}LinkList;

上面定义的LinkList是结点类型,如果想定义一个指向该结点类型的指针head,C语言中的语句如下:

LinkList *head;

如果想动态申请一个结点空间,并让指针head指向该结点空间,语句如下:

head=(LinkList *)malloc(sizeof(LinkList));

这样,head指针就该指向该结点了,则这个结点的数据域为head->data或是(*head).data,而指针域为head->next或(*head).next;

C语言中释放指针head所指结点空间的语句为:

free(head);

3.单链表的基本操作实现

1.单链表的初始化

        单链表的初始化即构造一个仅包含头结点的空单链表。其过程是首先申请一个结点并让指针head指向该结点,然后将它的指针域赋为空(NULL),最后返回头指针head。

/* 初始化链表函数 */
LinkList *InitList()
{
	LinkList *head;
	head = (LinkList *)malloc(sizeof(LinkList));//动态分配一个结点空间
	head->next=NULL;
	return head;	//头结点指针域为空,表示空链表
}

2.单链表的建立

(1)头插法建表

        链表是一种动态管理的存储结构,链表中的每个结点占用的存储空间不是预先分配,而是运行时系统根据需求生成的。因此,建立在初始化链表后,建立线性链表从空表开始,每读入有效的数据则申请一个结点s,并将读取的数据存放到新节点s的数据域中,然后将新结点插入到当前链表head的表头上,直到循环结束为止。

/* 头插法建立链表函数 */
void CreatListH(LinkList *head,int n)
{
	LinkList *s;
	int i;
	printf("请输入%d个整数: ",n);
	for(i=0;i<n;i++)
	{
		s=(LinkList *)malloc(sizeof(LinkList)); //生成新节点
		scanf("%d",&s->data);	//读入新节点的数据域
		s->next=head->next;		//将新节点的指针域存放头结点的指针域
		head->next=s;	//将新节点插入头结点之后
	}
	printf("建立链表操作成功!");
}

(2)尾插法建表

        头插法建立链表虽然算法简单易理解,但生成的链表中结点的次序和输入的次序相反。而尾插法建立链表可实现次序的一致,该算法依旧从空表开始,但需增加一个尾指针last,使其指向当前链表的尾结点。其过程是:每读入有效的数据则申请一个结点s,并将读取的数据存放到新结点s的数据域中,将s的尾指针设为空指针(NULL),然后将新结点插入到当前链表尾部(last指针所指的结点后面),直到循环结束为止。

/* 尾插法建表 */
void CreateListL(LinkList *head,int n)
{
	LinkList *s,*last;
	int i;
	last=head;	//last始终指向尾结点,开始时指向头结点
	printf("请输入%d个整数:",n)
	for(i=0;i<n;i++)
	{
		s=(LinkList *)malloc(sizeof(LinkList));		//生成新节点
		scanf("%d",&s->data);	//读入新节点的数据域
		s->next=NULL;	//将新节点的指针域为空
		last->next=s;	//将新节点插入表尾
		last=s;		//将last指针指向表尾结点
	}
	printf("建立链表操作成功!");
}

3.求表长操作

因为链表是链式结构,所以链表中元素个数不是已知的。想求表中元素个数还要设一个计数变量j(初值为0),将一个指针p先指向链表中的第一个元素,当p不为空时,循环将p指针后移,j加1,循环结束后j值即为链表长度。

/* 求链表长度函数 */
int LengthList(LinkList *head)
{
	LinkList *p = head->next;
	int j=0;
	while(p != NULL)	//当p不指向链表尾时
	{
		p = p->next;
		j++;
	}
	return j;
}

 4.查找操作

(1)按值查找

从链表的第一个元素结点开始,由前向后依次比较单链表中各结点数据域中的值,若某结点数据域中的值与给定的值x相等,则循环结束;否则继续向后比较直到表结束,然后判断指针p,若p不为空表示单链表中有x结点,输出查找成功的信息并输出x所在表中的位置;否则输出查找失败的信息。

/* 在链表中查找值为x的元素位置 */
void Locate(LengthList *head,DataType x)
{
	int j=1;	//计数器
	LinkList *p;
	p = head->next;
	while(p! = NULL && p->data != x)
	{
		p=p->next;
		j++;
	}
	if(p!=NULL)
	{
		printf("在表的第%d位找到值为%d的结点!",j,x);
	}
	else
	{
		printf("未找到值为%d的结点!",x);
	}
}

(2)按序号查找

首先判断i值是否大于表长,如果大于表长则输出位置错误信息。否则从链表的头结点开始,判断当前结点序号是否是i,成立则提前结束循环。最后输出第i位上的结点的数据域值和位置i。


/* 在链表中按位置查找元素 */
void SearchList(LinkList *head,int i)
{
	LinkList *p;
	int j=0;
	p=head;		//p指向链表的头结点
	if(i>LengthList(head))
		printf("位置错误,链表中没有该位置! ");
	while(p->next!=NULL && j<i)
	{
		p=p->next;
		j++;
	}
	if(j==i)	//判断与给定的序号是否相等
		printf("在第%d位上的元素值为%d!",i,p->data);
}

5.插入操作

在指针所指的结点后插入新结点。若要在链表中指针p所指位置后面插入一个结点,则插入操作步骤如下:

(1)先将结点s的指针域指向结点p的下一个结点(执行语句s->next=p->next);

(2)再将结点p的指针域改为指向新结点s(执行语句p->next=s);

由于单链表的结点结构是单向后指的,因此要完成此操作需要找到第i结点的前驱结点即第 i-1 结点的指针p,然后在已知结点p后方插入新结点即可。

/* 按位置插入元素函数 */
void InsList(LinkList *head,int i,DataType x)
{
	int j=0;
	LengthList *p,*s;
	p=head;
	while(p->next!=NULL && j<i-1)	//定位插入点
	{
		p=p->next;
		j++;
	}
	if(p!=NULL)		//p不为空则将新节点插到p后
	{
		s=(LengthList *)malloc(sizeof(LengthList));		//生成新节点s
		s->data=x;		//将数据x放入新节点的数据域
		s->next=p->next;	//将新节点s的指针域与p结点后面元素相连
		p->next=s;		//将p与新节点s连接
		printf("插入元素成功! ");
	}
	else{
		printf("插入元素失败!");
	}
}

链表插入操作主要时间耗费在查找操作上,其时间复杂度为O(n)。

6.删除操作

首先通过循环定位求出第i结点的前驱节点(第i-1结点)p的地址,然后将指针s指向被删除的结点,修改p->next指针,使其指向s后的结点,最后释放指针s所指结点。

/* 按位置删除链表中元素函数 */
void DelList(LengthList *head,int i)
{
	int j=0;
	DataType x;
	LengthList *p=head,*s;
	while(p->next!=NULL && j<i-1)	//定位插入点
	{
		p=p->next;
		j++;
	}
	if(p->next!=NULL && j==i-1)
	{
		s=p->next;		//s为要删除结点
		x=s.data;		//将要删除的数据放入变量x中
		p->next=s->next;	//将p结点的指针域与q结点后面元素相连
		free(s);
		printf("删除第%d位上的元素%d成功!",i,x);
	}
	else
		printf("删除结点位置错误,删除失败!");
}

链表的删除操作也主要时间耗费在查找操作上,其时间复杂度为O(n)。

7.单链表的输出操作

扫描单链表,输出各元素的值

/* 显示输出链表函数 */
void DispList(LengthList *head)
{
	LengthList *p;
	p=head->next;	//p指针指向链表第一个结点
	if(p!=NULL)		//当p指针不为空时输出链表每个数据域值
	{
		printf("%5d",p->data);
		p=p->next;
	}
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值