0
点赞
收藏
分享

微信扫一扫

数据结构与算法(4)

彭维盛 2022-02-11 阅读 96

1.线性表的链式储存结构

一、线性表链式储存结构定义

线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。这就意味着,这些数据元素可以存在内存未被占用的任意位置。

以前在顺序结构中,每个数据元素只需要存储数据元素信息就可以了。现在链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址。

因此,为了表示每个数据元素Ai与其直接后继数据元素Ai+1之间的逻辑关系,对数据元素Ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针城中存储的信息称作指针或链。这两部分信息组成数据元素Ai的存储映像,称为结点。

n个结点(Ai的存储映像)链结成一个链表,即为线性表(A1,A2,…,An)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。单链表正是通过每个结点的指针域将线性表的数据元素按其逻辑次序链接在一起。

对于线性表,我们把链表中第一个结点的存储位置叫做头指针,那么整个链表的存取就必须是从头指针开始进行了。之后的每一个结点,其实就是上一个的后继指针指向的位置。最后一个,意味着直接后继不存在了,所以我们规定,线性链表的最后一个结点指针为空。

为了更加方便地对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储任何信息。也可以存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针。

 二、头指针与头节点的异同

头指针:

  • 头指针是指链表指向第一个结点的指针,若链表有头结点.则是指向头结点的指针
  • 头指针具有标志作用,所以常用头指针冠以链表的名字
  • 无论链表是否为空,头指针均不为空。头指针是链表的必要元素

头节点:

  • 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可存放链表的长度)
  • 有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了
  • 头结点不一定是链表必需要素

2.单链表的插入与删除、

一、单链表的插入

  1. 单链表第i个数据插入结点的算法思路:
  2. 声明一指针p指向链表头结点,初始化j从1开始;
  3. 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1;
  4. 若到链表末尾p为空,则说明第i个结点不存在;
  5. 否则查找成功,在系统中生成一个空结点s;
  6. 将数据元素e赋值给s->data;
  7. 单链表的插入标准语句s->next=p->next;p->next=s;
  8. 返回成功。
//初始条件 :链式线性表中L已存在,1<=i<=ListLength(L)
//操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1
Status ListInsert(LinkList *L,int i,ElemType e) {
	int j;
	LinkList p,s;
	p=*L;
	j=1;
	while(p&&j<i) {
		p=p->next;
		++j;
	}
	if(!p||j>1)
		return ERROR;
	s=(LinkList)malloc(sizeof(Node));
	s->data=e;
	s->next=s;
	return OK;
}

二、单链表的删除

  1. 单链表第i个数据删除结点的算法思路:
  2. 声明一指针p指向链表头结点,初始化j从1开始
  3. 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1
  4. 若到链表末尾p为空,则说明第i个结点不存在
  5. 否则查找成功,将欲删除的结点p->next赋值给q
  6. 单链表的删除标准语句 p−>next=q−>next
  7. 将q结点中的数据赋值给e,作为返回
  8. 释放q结点
  9. 返回成功
//初始条件 :链式线性表中L已存在,1<=i<=ListLength(L)
//操作结果:删除L的第i个元素,并用e返回其值,L的长度减1
Status ListInsert(LinkList *L,int i,ElemType e) {
	int j;
	LinkList p,q;
	p=*L;
	j=1;
	while(p->next&&j<i) {
		p=p->next;
		++j;
	}
	if(!(p->next)||j>i)
		return ERROR;
	q=p->next;
	p->next=q->next;
	*e=q->data;
	free(q);
	return OK;
}

三、单链表的整表创建

  1. 单链表整表创建的算法思路:
  2. 声明一指针p和计数器变量i。
  3. 初始化一空链表L。
  4. 让L的头结点的指针指向NULL,即建立一个带头结点的单链表。
  5. 循环:①生成一新结点赋值给p;②随机生成一数字赋值给p的数据域p->data;③将p插入到头结点与前一新结点之间。
//随机产生n个元素的值,建立带表头结点的单链线性表L(头插法)
void CreateListHead(LinkList *L,int n)
{
	LinkList p;
	int i;
	srand(time(0));//初始化随机数种子
	*L=(LinkList)malloc(sizeof(Node));
	(*L)->next=NULL;
	for(i=0;i<n;i++)
	{
		p=(LinkList)malloc(sizeof(Node));
		p->data=rand()%100+1//随机产生100以内的数字
		p->next=(*L)->next;
		(*L)->next=p; 
	}
}
//随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法)
void CreateListHead(LinkList *L,int n)
{
	LinkList p,r;
	int i;
	srand(time(0));//初始化随机数种子
	*L=(LinkList)malloc(sizeof(Node));
	r=*L;
	for(i=0;i<n;i++)
	{
		p=(LinkList)malloc(sizeof(Node));
		p->data=rand()%100+1//随机产生100以内的数字
		r->next=p;
		r=p; 
	}
	r->next=NULL;
}

四、单链表整表的删除

单链表整表删除的算法思路如下:

  1. 声明一指针p和q。
  2. 将第一个结点赋值给p。
  3. 循环:①将下一结点赋值给q;②释放p;③将q赋值给p。
//初始条件:链式线性表L已存在
//操作结果:将L重置为空表
Status ClearList(LinkList *L)
{
	LinlList p,q;
	p=(*L)->next;
	while(p)
	{
		q=p->next;
		free(p);
		p=q;
	}
	(*L)->next=NULL;
	return OK;
} 

3.单链表结构与顺序储存结构的优缺点

1)储存分配方式

顺序储存结构用一段连续储存单元依次储存线性表的数据元素

单链表采用链式储存结构,用一组任意的储存单元存放线性表的元素

2)时间性能

查找:顺序储存结构O(1);单链表O(n);

插入和删除:顺序储存结构需要平均移动表长一半的元素,时间复杂度为O(n);单链表再找出位置的指针后,插入和删除时间复杂度仅为O(1)

空间性能:顺序储存结构需要预分配储存空间,分大了浪费,分小了易发生上溢;单链表不需要分配储存空间,只要有就可以分配,元素个数也不受限制

若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构。若需要频繁插入和删除时,宜采用单链表结构。

当线性表中的元素个数变化较大或者根本不知道有多大时,最好用单链表结构。这样可以不需要考虑存储空间的大小问题。而如果事先知道线性表的大致长度,用顺序存储结构效率会高很多。

举报

相关推荐

0 条评论