学习数据结构:线性表的链式存储结构

一、线性表的链式存储结构
线性表顺序存储结构缺点是插入和删除元素,比较麻烦,需要移动元素,并且会造成空间的浪费。而链式存储结构,插入删除元素,而不用移动其他元素。用一组任意的存储单元存储线性表的数据元素,可以是连续的,也可以是不连续的,这些数据元素可以存在内存未被占用的任意位置。

每一个数据元素,除了存储数据信息外,还会存储下一个元素的内存地址,用一个指针指向这个地址,每个元素与所存的指针构成了线性链表的一个结点。
存储数据元素的叫做数据域;
存储指针信息的叫做指针域。
在这里插入图片描述
每一个结点只有一个指针域,各个结点靠指针域链结在一起,这个链表有个开头,存储的第一个数据元素的结点称为头结点(一般我们在第一个结点前还会放置一个结点,这个结点只存放指针不放数据信息);最后一个元素指针域没有存放指针设位NULL,只存有数据元素,表示到这结束了。

头指针:如果链表没有头结点,则是指向链表第一个结点的指针;若这个链表有头结点,则该指针指向头结点。
在这里插入图片描述
在这里插入图片描述头结点:存放指向第一个结点的指针,一般数据域不存放信息。若线性表位空,则头结点的指针域为NULL。

单链表存储的结构体

typedef struct node
{
	DataType data;
	struct Node *next;
}LinkNode; *LinkList;

1.单链表的初始化

void InitList(LinkList *head)
{
	*head=(LinkList)malloc(sizeof(LinkNode));  //为头结点申请分配内存空间
	(*head)->next=NULL;      //让头结点的指针指向空
}

2.判断单链表是否为空

int ListEmpty(LinkList head)
{
	if(head->next==NULL)
		return 1;
	else
		return 0;
}

3.根据序号读取单链表的元素内容

(1)声明一个指针p指向链表第一个结点,让计数器j从1开始
(2)当j<i时,不断的循环,并每一次让p指向下一个节点
(3)如果p最后为空,且j>i,说明i结点不存在,否则就是查找成功,返回结点p的数据


Status GetElemType(LinkList head,int i,int *e)
{
	int j=1;        //定义一个计数器
	LinkList p;     //定义一个LinkList结构体类型的指针p
	p=head->next;      //让p指向链表的第一个结点
	while(p&&j<i)   //让j从第一个结点开始循环,直到读取的位置i,且满足p最后指向的指针域不为空
	{
		p=p->next;  //让p指向下一个结点
		j++;        //计数器加1
	}
	if(!p || j>i)   //要取的数据下标不在表的范围内
		return error;
	*e=p->data;     //把第i个位置的数据传递给e
	return error;
}
		

单链表没有定义表长,无法使用for循环,当要查询的结点是第一个结点时,则时间复杂度为O(1),当i=n时遍历n-1次,所以最坏情况的时间复杂度为O(n)。这种方法称为指针后移。

4.按内容查找链表中与给定内容是否相等,是则返回指针,否则返回NULL

LinkList* LocateElem(LinkList head,DataType e)
{
	LinkList *p;     //声明一个LinkList结构体类型的指针p
	p=head->next;    //让p指向第一个结点
	while(p)         //p为NULL则跳出循环
	{
		if(p->data==e)  //如果当前节点的数据等于要查找的内容,则跳出循环
			break;
		else            //如果不等于则指针下移一位
			p=p->next;
	}
	return p;     //返回指针p  
}
			

5.插入元素

(1)先申明两个指针,p作为可移动的指针用于循环指向i前面一个结点,s作为新插入结点指针。
(2)循环找到i前面的结点
(3)把数据赋值给新结点s
(4)把p的后继赋值给s的后继,把s赋值给p的后继
(5)插入成功返回1,失败返回0
在这里插入图片描述

Status LinkInsert(LinkList *head,int i,int e)
{
	LinkList p,s;     //声明两个LinkList结构体类型的指针
	int j=1;          //定义一个计数器
	p=*head; 		      //让p指向头结点L
	while(p&& j<i)    
	{
		p=p->next;    //让p指向下一个结点
		j++;          
	}//当跳出循环后,p指向i前面的一个结点,i=p->next
	if(!p || j>i)     //判断结点i是否在链表的范围内
		return 0;
	s=(LinkList)malloc(sizeof(Node));//申请分配内存
	s->data=e;		  //把数据传递给新结点的数据域
	s->next=p->next;  //把p的后继结点(结点i)赋值给s的后继
	p->next=s;		  //把s赋值给p的后继
	return 1;
}

6.删除元素

(1)先申明两个指针,p作为可移动的指针用于循环指向i前面一个结点,q作为要删除的结点指针。
(2)循环找到i前面的结点
(3)把p的后继赋值给q(即此时的q就是要删除的结点i)
(4)把q的后继赋值给p的后继
(5)释放结点q
在这里插入图片描述

Status LinkDelete(LinkList head,int i,int *e)
{
	LinkList p,q;     //声明两个LinkList结构体类型的指针
	int j=1;          //定义一个计数器
	p=*head; 		      //让p指向头结点L
	while(p&& j<i)    
	{
		p=p->next;    //让p指向下一个结点
		j++;          
	}//当跳出循环后,p指向i前面的一个结点,i=p->next
	if(!p || j>i)     //判断结点i是否在链表的范围内
		return 0;
	q=p->next;       //把p的后继赋值给q(即此时的q就是要删除的结点i)
	*e=q->data;		 //把要删除的结点的数据传递给e
	p->next=q->next; //把要删除结点q的后继赋值给p的后继
	free(q);		 //回收q
	return 1;

(7)整张链表的删除

void DestryList(LinkList head)
{
	LinkNode p,q;
	p=head;
	while(p!=NULL)
	{
		q=p;
		p=p->next;
 		free(q);
 	}
}

二、创建链表的两种方法
1.头插法

void CreateListHead(LinkList *head,int n)
{  //传入的参数,第一个为链表的头结点,第二个参数为要生产链表的元素的个数
	LinkList p;          //声明一个LinkList类型的指针
	int i,e;			 //定义一个计数器,和要传入的数据类型
	*head=(LinkList)malloc(sizeof(Node));//为头结点申请内存空间
	head->next=NULL;     //让头结点的指针先指向空
	for(i=0;i<n;i++)     //开始循环,直到给定的链表结点个数为止
	{
		p=(LinkList)malloc(sizeof(Node)); //为新结点申请空间
		printf("请新输入数据:\n");  
		scanf("%d",&e);           //获取结点数据
		p->data=e;                //把数据传递给新结点的数据域
		p->next=*head->next;      //让新结点的指针指向NULL
		*head->next=p;            //让头结点的指针指向新结点
	}
}

2.尾插法

void CreateListTail(LinkList *head,int n)
{  //传入的参数,第一个为链表的头结点,第二个参数为要生产链表的元素的个数
	LinkList p,r;          //声明两个LinkList类型的指针
	int i,e;			   //定义一个计数器,和要传入的数据类型
	*head=(LinkList)malloc(sizeof(Node));//为头结点申请内存空间
	r=*head;               //把头结点赋给指针r
	for(i=0;i<n;i++)
	{
		p=(LinkList)malloc(sizeof(Node)); //为新结点申请空间
		printf("请新输入数据:\n");  
		scanf("%d",&e);         //获取结点数据
		p->data=e;              //把数据传递给新结点的数据域
		r->next=p;              //先把新结点加入到r(此时r为头结点)结点的后面
		r=p;       //再把新结点赋值给r,下一次循环时,r就作为这个链表的结尾,然后在进行加新结点的操作
	}
	r-next=NULL;
}
		

注:尾插法中,r永远为指向最后一个结点的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值