线性表链式存储结构定义
线性表的链式存储结构的特点就是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的也可以是不连续的,也就是说数据元素可以占用任意的存储单元。
之前的顺序结构中,每个元素只需要存储数据元素即可,但是在链式结构中数据元素的存储位置是任意的,除了存储数据元素之外,还要存储他们的地址。所以为了表示两相邻数据元素ai与ai+1之间逻辑关系,就在ai元素所在的结点中,同时存入数据元素ai和它的下一个元素ai+1的地址,那么这个结点可以分成两个部分,一部分是数据域用于存放数据元素,另一部分是指针域,存放下一个元素的指针。
思考一下,顺序表在查找元素的时候,可以根据下标查找,那么链式表呢?
根据指针寻找元素,那用谁的指针能找到所有的元素呢?如果用第一个元素的指针,那么第一个元素怎么表示?为了解决这个问题,我们引入一个新的结点,里面放入指向第一个结点的指针,这个新的结点通常被叫做头结点。在头结点的数据域中,可以不存放数据,也可以存放表的长度。没错就它特殊,谁让它是第一个呢!
如何区分头指针和头结点
头指针:
-
是指链表只点第一个结点的指针,如果链表有头结点,则是指向头结点的指针
-
头指针具有标识作用,所以常用头指针冠以链表的名字
-
无论链表是否为空,头指针均不为空。
-
头指针是链表的必要元素
头结点:
-
是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义
-
有了头结点,定义在第一元素结点前插入结点和删除第一节点,其操作与其它结点的操作就统一了
-
头结点不一定是链表必须要素
链式存储结构代码实现
结构描述
typedef struct Node
{
int data;
struct Node * next;
}LNode,*PLNode;
在结点中一部分是数据域,存放数据元素;另一部分是指针域,用于存放下一个元素的指针。假如现有一指针p指向链表中的第i个元素,那么这个结点数据域用p->data表示,指针域用p->next表示,而p->next则指向第i+1个元素。所以需要创建一个数据类型既能够包含数据,又能够包含指针。
创建链表
PLNode create(void)
{
int val;
int len;
PLNode LHead=(PLNode)malloc(sizeof(LNode));
if(!LHead)
{
printf("存储分配失败");
exit(-1);
}
PLNode Ptail=LHead;
Ptail->next=NULL;
printf("请输入表长len的值:");
scanf("%d",&len);
printf("请输入元素的值:");
for(int i=0;i<len;i++)
{
scanf("%d",&val);
PLNode Pnew=(PLNode)malloc(sizeof(LNode));
if(Pnew==NULL)
{
printf("空间分配失败");
exit(-1);
}
Pnew->data=val;
Ptail->next=Pnew;
Pnew->next=NULL;
Ptail=Pnew;
}
return LHead;
}
思路:
-
首先定义两个变量len,val,分别表示表长和表中元素
-
分配存储空间
-
循环插入元素
-
定义新结点存放要存入的元素
插入操作
bool Insert(PLNode L,int i,int val)
{//在第i个位置插入val
PLNode p=L;
int j=0;
while(p && j<i-1)//寻找第i个结点
{
p=p->next;
++j;
}
if(!p || j<i-1)//i小于1或者大于表长+1
return false;
PLNode s=(PLNode)malloc(sizeof(Node));
//插入元素
s->data=val;
s->next=p->next;
p->next=s;
return true;
}
思路:
-
声明一个结点p指向链表的第一个结点
-
寻找第i个元素
-
i值是否合法(查找元素是否在有效范围内)
-
创建一个新的空结点s
-
将插入元素赋值给s->data
-
单链表的标准插入语句s->next=p->next;p->next=s;
删除操作
bool Delete(PLNode L,int i,int *val)
{//在带头节点的链表中删除第i个元素用val换回其值
PLNode p=L;
int j=0;
while(p->next && j<i-1)//寻找第i个结点,并令p指向其前驱
{
p=p->next;
++j;
}
if(!(p->next) || j>i-1)//删除位置不合理
return false;
PLNode q=p->next;
//删除并释放结点
p->next=q->next;
*val=q->data;
free(q);
return true;
}
思路:
-
声明一个结点p指向链表第一个结点
-
遍历寻找第i个结点
-
判断删除元素位置是否合理
-
单链表标准删除语句p->next=q->next
-
将数据赋值给val
-
释放结点
输出操作
void Traverse(PLNode L)
{
PLNode p=L->next;
while(p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
输出操作相对就很简单啦,循环遍历表中元素,直到结点为空时退出循环,输出完毕。
好啦,小伙伴们,今天的分享就到这里。有什么问题可以在微信公众号发私信给我,明天见喽!