链表
概述:线性表的链式存储结构称为链表,其中每个节点不仅包含元素本身信息,而且包含标识元素之间的逻辑关系的信息,在C/C++中常用指针来实现,这称为指针域。
在线性表的链式存储中,每个链表都头结点,并通过头结点唯一标识该链表,称为头指针,相应指向首节点指针称为首指针,尾结点称为尾指针(切记单链表是无前驱的),如下图:
链表与顺序表的比较
顺序表中逻辑上相邻元素对应的存储位置也相邻,所以执行插入删除操作时候平均需要移动半个表的元素,而链表不同,逻辑上相邻元素对应的存储位置不一定相邻,它是通过指针来连接的,因此存储位置可以随意安排。
由于顺序表的直接映射,即查找第i个元素时对应的时间复杂度为o(1),而链表不具有随机存取特性
此外顺序表存储密度较高,存储密度即节点中数据元素本身所占的存储量和整个结点所占的存储量之比。一般情况存储密度越大,存储空间利用率越高,而线性表存储密度为1,而链表小于1
单链表操作
1.插入删除操作(秉持先右后左思路)
s->next = p->next
p->next = s
2.删除操作
q=p->next
p->next=q->next
free(q)
3.头插法建立单链表
特点:每次新结点是在头结点之后插入,以此类推
void createA(linknode*&l, elemtype a[], int n)
{
linknode *s;
l=(linknode*)malloc(sizeof(linknode));
l->next=NULL;
for(int i=0;i<n;i++)
{
s=(linknode*)malloc(sizeof(linknode)); //创建数据结点
s-data=a[i];
s->next=l->next; //插入操作
l->next=s;
}
}
若a[]包含元素1、2、3、4则在链表中顺序为4、3、2、1
4.尾插法建立
特点,每次都在链表尾端插入元素,与头插不同的是需要在尾部指定一个指针,并随着插入节点增多依次往后移动,永远指向尾部结点。
void createB(linknode*&l, elemtype a[], int n)
{
linknode *s,*r;
l=(linknode*)malloc(sizeof(linknode));
r=l
for(int i=0;i<n;i++)
{
s=(linknode*)malloc(sizeof(linknode)); //创建数据结点
s-data=a[i];
r->next=s; //插入操作
r=s;
}
r->next=NULL;
}
基本运算实现
1.初始化线性表
void Initlist(linknode*&l)
{
l=(linknode*)malloc(sizeof(linknode));
l->next=NULL; //创建头结点,其next域置为NULL
}
2.销毁线性表
此地方要注意创建一个前驱指针,用于完成节点删除工作
void Destorylist(linknode*&l)
{
linknode *pre=l,*p=l->next;
while(p!=NULL)
{
free(pre);
pre=p;
p=pre->next; //同步移动指针
}
free(pre);
}
3.判断是否为空表
直接判断头结点下一个是否为NULL
4.求线性表长度
void Destorylist(linknode*&l)
{
int n=0;
linknode *p=l;
while(p->next!=NULL)
{
n++;
p=p->next;
}
return(n);
}
5.输出线性表
设置指针指向首节点,依次输出,后移
6.求线性表中某个数据元素值
找到第i个节点,若存在,则输出true,否则返回false
bool getelem(linknode *l, int i, elemtype &e)
{
int j=0;
linknode *p=l;
if(i<=0) return false;
while(j<i&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else
{
e=p->data;
return true;
}
}
7.按元素值查找
int locateelem(linknode *l, elemtype &e)
{
int i=1;
linknode *p=l->next;
while(p!=NULL&&p->data!=e)
{
p=p->next;
i++;
}
if(p==NULL)
return(0); //不存在
else
return(i); //返回逻辑序列号
}
8.插入数据元素
实现过程先在单链表l中找到第i-1个结点,由p指向它,然后插入
bool listinsert(linknode *&l, int i, elemtype e)
{
int j=0;
linknode *p=l,*s;
if(i<=0) return false;
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
if(p=NULL)
return false;
else
{
s=(linknode *)malloc(sizeof(linknode)) //插入操作
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
}
9.删除数据元素
类似于上面插入,先找到第i-1个结点,然后执行删除操作
bool listdelete(linknode *&l, int i, elemtype e)
{
int j=0;
linknode *p=l,*q;
if(i<=0) return false;
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
if(p=NULL)
return false;
else
{
q=p->next;
if(q==NULL)
return false;
e=q->data;
p->next=q->next;
free(q);
return true;
}
}
应用举例
有一个带头节点的单链表l(至少含有一个数据结点),设计一个算法使其递增有序
void sort(linknode *&l)
{
linknode *p,*pre,*q;
p=l->next->next;
l->next->next=NULL;
while(p!=NULL)
{
q=p->next;
pre=l;
while(pre->next!=NULL&&pre->next->next->data<p->data)
pre=pre->next; //pre后移,是结点插入到pre前面
p->next=pre->next; //在pre节点后插入p所指结点
pre->next=p;
p=q;
}
}