入门小白学数据结构与算法(2)——线性表

数据结构与算法(2)——线性表

一、概念:由零个或多个数据元素组成的有限序列

注意:1.序列性,元素间有先来后到
2.第一个元素无前驱,最后一个元素无后继,其他元素有且只有一个前驱和后继,一对一的关系,就像一条线。
3.线性表是有限的
4.线性表元素的个数n定义为线性表的长度,当n=0时,称为空表

按存储结构分类:顺序存储结构(数组)和链式存储结构(链表)
顺序存储:用一段地址连续的存储单元依次存放线性表的数据结构

二、操作

1.InitList(*L):初始化线性表,建立一个新的空的线性链表
2.ListEmpty(L):判断线性表是否为空表,若是则返回true,否则返回false
3.ClearList(*L):清空线性表
4.GetElem(L, i, *e):将线性表L中的第i个位置的元素值返回给e
5.LocateElem(L, e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中的序号,否则返回0表示失败
6.ListInsert(*L, i, e):在线性表L中第i个位置插入新元素e
7.ListDelte(*L, i, *e):删除线性表L中第i个位置元素,并用e返回其值
8.ListLength(L):返回线性表L的元素的个数

三、顺序存储结构 (数组)

线性表顺序存储结构代码

#define MAXSIZE 20
typedef int ElemType;
typedef struct
{
	ElemType data[MAXSIZE];
	int length;
}Sqlist;

线性表的长度不应该超过数组的长度,线性表存储时间性能为O(1),通常称有这种特性的存储结构为随机存储结构

获取数组元素操作

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
Status GetElem(Sqlist L, int i, ElemType *e)
{
	if(L.length==0||i<1||i>L.length)
	{
		return ERROR;
	}
	*e = L.data[i-1];
	return OK;
}

数组元素插入操作

Status ListInsert(Sqlist *L, int i, ElemType e)
{
	int k;
	if(L->length>=MAXSIZE)
	{
		return ERROR;
	}
	if(i<1||i>L->length+1)//插入位置不合理
	{
		return ERROR;
	}
	if(i<L->length)
	{
		for(k=L->length-1;k>=i-1;k--)
		{
			L->data[k+1]=L->data[k];
		}
	}
	L->data[i-1]=e;
	L->length+=1;
	return OK;
}

数组元素删除操作

Status ListDelte(Sqlist *L, int i, ElemType *e)
{
	int k;
	if(L->length==0)
	{
		return ERROR;
	}
	if(i<1||i>L->length)
	{
		return ERROR;
	}
	*e=L->data[i-1];
	if(i<L->length)
	{
		for(k=i-1;k<=L->length-1;k++)
		{
			L->data[k]=L->data[k+1];
		}
	}
	L->length-=1;
	return OK;
}

顺序存储结构的优缺点在这里插入图片描述

四、链式存储结构 (链表)——表中的每个元素包含两个内容:本身数据(存储这个数据的地方叫数据域)和下一个元素的地址(存储这个指针的地方叫指针域),每个元素又叫做链表的结点(Node)。由于这样的链表里面每个节点只包含一个指针域,所以又叫单链表。

链表中第一个结点的存储位置(不是第一个结点中指针域的指针,是指向第一个结点的指针)叫做头指针,最后一个结点里面的指针为空(NULL)
有时候为了数据操作方便,会在第一个结点之前再设置一个结点,这个结点叫做头结点,这个头结点的数据域可以不存放任何数据,其指针域存放指向第一个结点的指针(头指针)
在这里插入图片描述
在这里插入图片描述
C语言中可用结构体指针来表示单链表

typedef struct Node
{
	ElemType data;
	struct Node *next;
}Node;
typedef struct Node *LinkList

单链表的读取

Status GetElem(LinkList L, int i, ElemType * e)
{
	LinkList p;
	p=L->next;//让p指向链表L的第一个结点,不是头结点,链表名称L代表头结点
	j=1;
	while(p&&j<i)
	{
		p=p->next;
		++j;
	}
	if(!p||j>i)
	{
		return ERROR;
	}
	*e=p->data;
	return OK;
}

单链表元素的插入

Status ListInsert(LinkList *L, int i, ElemType e)
{
	LinkList p, s;
	p=(*L);//让P等于链表L的头结点
	int j=1;
	while(p&&j<i)
	{
		p=p->next;
		++j;
	}
	if(!p||j>i)
	{
		return ERROR;
	}
	s=(LinkList)malloc(sizeof(Node))//在内存中开辟一块和Node数据类型大小一样的地方,用来存放LinkList类型的数据,s为指向该地方的指针
	s->data=e;
	s->next=p->next;
	p->next=s;
	return Ok;
}

单链表元素的删除

Status ListDelte(LinkList *L, int i, ElemType *e)
{
	int j=1;
	LinkList p, q;
	p=*L;
	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;
	
}

单链表整表的创建(头插法)

void CreateListHead(LinkList* L, int n)//其中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;
		p->next=(*L)->next;
		(*L)->next=p;
	}
}

单链表整表的创建(尾插法)

void CreateListTail(LinkList* L, int n)
{
	int i;
	LinkList q,p;
	srand(time(0))
	*L=(LinkList)malloc(sizeof(Node));
	q=*L;
	for(i=0;i<n;i++)
	{
		p=(LinkList)malloc(sizeof(Node))
		q->next=p;
		q=p;	
	}
	q->next=NULL;
}

单链表整表的删除

Status ClearList(LinkList* L)
{
	LinkList p,q;
	q=*L->next;//q指向L的第一个结点
	while(p)
	{
		p=q->next;
		free(q);
		q=p;
	}
	*L->next=NULL;//L的头结点指针域清空
	return OK;
}

两种线性表优缺点比较:
1.如果查找程序更多,应使用顺序存储结构,若插入删除较多,则应使用链式存储结构
2.顺序存储结构需要预先分配内存,在存储数据时,元素个数会受限制,链式存储结构不需要预先分配内存,元素个数也不受限制

五、静态链表——用数组描述的链表,数组元素同时包含数据(data)和后继元素的下标(cur)

静态链表的创建(结构体数组)

#define MAXSIZE 1000
typedef struct
{
	ElemType data;
	int cur;
}Component, StaticLinkList[MAXSIZE];//后面的程序可直接用StaticLinkList定义大小为1000的结构体数组

静态链表的初始化:数组的第一个元素,也就是下标为0的元素的cur存放备用链表(未被使用的数组)的第一个结点的下标,而数组的最后一个元素的cur则存放第一个有数组元素的下标

Status InitList(StaticLinkList space)
{
	int i;
	for(i=0;i<=MAXSIZE-1;i++)
	{
		space[i].cur=i+1;
	}
	space[MAXSIZE-1].cur=0;
	return OK;
}

静态链表的malloc功能实现函数,思路是找到第一个备用链表(假设为A)的位置,然后将这个备用链表(A)所指向的下一个备用链表(B)给[0].cur,这样一来,就相当于B变成了A,而A就变成了非备用链表,表示被征用了

int Malloc_SLL(StaticLinkList space)
{
	int i=space[0].cur;
	if(space[0].cur)
		space[0].cur=space[i].cur;
	return i;
}

静态链表插入数据——找到第i-1个数据所在的下标,改变cur即可

Status ListInsert(StaticLinkList L, int i, ElemType e)//将e插在第i个数据,而非下标i的地方
{
	int j,k,l;
	k=MAXSIZE-1;//首先将k等于最后一个元素的下标,那么L[k].cur的值就是第一个数据元素的下标
	if(i<1||i>ListLength(L)+1)
		return ERROR;
	int j=Malloc_SLL(L);//将第一个备用链表的空间腾出来用来存放新的数据
	if(j)
	{
		L[j].data=e;//写入数据
		for(l=1;l<=i-1;l++)//找到第i-1个数据所在的下标k
			k=L[k].cur;
		L[j].cur=L[k].cur;//将第i-1个数据中的cur(指向原第i个数据的下标)给新的数据的cur,新的数据插在了第i个数据的位置,其cur指向原第i个数据(插完后变为第i+1个数据)的下标
		L[k].cur=j;//新的数据变为第i个数据后,把其下标值给第i-1个数据的cur
		return OK;
	}
	return ERROR;
}

静态链表的free函数实现

void Free_SLL(StaticLinkList space, int k)
{
	space[k].cur=space[0].cur;
	space[0].cur=k
}

静态链表的删除操作(删除L中第i个元素)

Status ListDelete(StaticLinkList L, int i)
{
	int j,k;
	K=MAXSIZE-1;
	for(j=1;j<=i-1;j++)
	{
		k=L[k].cur;
	}
	j=L[k].cur;
	L[k].cur=L[j].cur;
	Free_SLL(L, j);
	return Ok;
	
}

六、循环链表——把单链表中终结结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相连的单链表成为循环链表。

七、双向链表——在单链表的每个结点中,再设置一个指向前驱结点的指针域

八、总结

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值