我们上一篇博客说的线性表的顺序存储结构结构它是有缺点的,最大的 缺点就是插入和删除时需要移动大量的元素。所以我们就想到用链式存储结构来解决这个问题,就是让上一个元素存储下一个元素的地址,方便找到。这样所有元素就都可以通过遍历找到。这篇博客就来说一下单链表的简单实现。
一、单链表的定义
因此,为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。
n个结点链结成一个链表,即为线性表(a1, a2… an)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。如下图所示:
我们把链表中第一个结点的存储位置叫头指针。为了方便操作会在单链表的第一个结点前设一个结点叫头结点。如下图所示:
二、头指针和头结点的异同
1、头指针:
(1)头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
(2)头指针具有标识作用,所以常用头指针冠以链表的名字
(3)无论链表是否为空,头指针均不为空。头指针是链表的必要元素
2、头结点:
(1)头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域般无意义(也可存放链表的长度)
(2)有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了
(3)头结点不定是链表必须要素
三、单链表的基本实现
1、初始化(InitList)
void InitList(List plist)//初始化
{
assert(plist !=NULL);
if(plist==NULL)
{
return;
}
plist->next==NULL;
}
2、插入元素(Insert)
(1)声明结点p指向链表第一个结点;
(2)当p!=NULL时,就遍历链表,让p的指针向后移动,不断指向下一个结点,i++;
(3)若到链表末尾p为空,则说明第i个元素不存在;
(4) 否则查找成功,在系统中生成一个空结点q;
(5) 将数据元素val赋值给q->data;
(6) 单链表的插入标准语句q->next=p->next; p->next=q;
(7) 返回成功。
//第一个数据节点的下标为0
bool Insert(List plist,int pos,int val)
{
int i=0;
Node *p;
for(p=plist,i=0;p!=NULL&&i<pos;i++,p=p->next)
{
;
}
if(i<pos)//没有pos下标
{
return false;
}
Node *q=(Node *)malloc(sizeof(Node));
q->data=val;
//将q插入在p的后面
q->next=p->next;
p->next=q;
return true;
}
3、单链表的删除( Delete)
(1)声明一结点p指向链表的第一个结点,初始化j从1开始。
(2)当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
(3)若到链表末尾p为空,则说明第i个元素不存在;
(4)否则查找成功,将欲删除的结点p->next赋值给q;
(5)单链表的删除标准语句p->next=q->next;
(6)将q结点中的数据赋值给key,作为返回;
(7)释放q结点;
bool Delete(List plist,int key)
{
int j;
List p,q;
p=*L;
j=1;
while(p->next && j<1)
{
p=p->next;
++j;
}
if(!(p->next)|| j>1)
return false;
q=p->next;
p->next =q->next;
key=q->data;
free(q);
return true;
}
4、单链表的创建—头插(Insert_head)
从创建一个空表开始,生成新结点,将读入的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点,知道读入结束时停止。
bool Insert_head(List plist,int n)
{
List p;
List q = plist;
int a;
for(int i=0;i<n;i++)
{
p = (List)malloc(sizeof(Node));
scanf("%d",&a);
p->data = a;
p->next = q->next;//将头指针所指向的下一个结点的地址,赋给新创建结点的next
q->next = p;//将新创建的结点的地址赋给头指针的下一个结点
}
return true;
}
4、单链表的创建—尾插
将新结点插入到当前单链表的表尾,增加一个尾指针r,指向当前链表的表尾
bool Insert_tail(List plist,int n)
{
List p,r;
r = plist;
int a;
for(int i=0;i<n;i++)
{
p = (List)malloc(sizeof(Node));//开辟新的结点
scanf("%d",&a);
p->data = a;//新结点的数据域赋值
r->next = p;
r = p;
}
r->next = NULL;
return true;
}
5、单链表的整表删除(ClearList)
(1)声明一结点p和q;
(2)将第一个结点赋值给p;
(3)循环:
将下一结点赋值给q; 释放p;将q赋值给p。
bool ClearList (List *L)
{
LinkList p,q;
p=(*L) ->next;//p指向第一个结点
while (p)
{
q=p->next;
free (p);
p=q;
}
(*L)->next=NULL;//头指针的指针域指为空
return true;