2.1链表的定义和表示
存储特点:用一组任意的储存单元存储线性表的数据元素(存储单元可以是连续的,也可以是不连续的)。
对于数据元素来说,需要存储本身信息和一个指示其直接后继的信息(后面的存储位置)。
这两部分信息组成数据的存储影像,称为结点。
两个域:数据域和指针域。
指针域中的储存信息为指针或链,n个结点的储存影像链接成一个链表,若每个结点只包含一个指针域称单链表,若含有两个指针域,则称其为双链表。
单链表的储存结构:
typedef struct LNode
{
ElemType data;
struct LNode *next;//指针域
}LNode,*LinkList;//两个变量名强调的重点不同
双链表的储存结构:
//双向链表
typedef struct DuLNode//这个因为有两个指针,需重新定义
{
ElemType data;//数据域
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode,*DuLinkList;
2.2单链表基本操作的实现
1)初始化
构造一个空链表
//初始化(构造一个空的链表)
Status InitList(LinkList &L)//Status为函数类型(当return值为函数结果状态时用)
{
L=new LNode;
l->=NULL;
return OK;
}
2)取值
和顺序表不同的,储存单元并没有相邻,而是根据下个链域来依次访问。
//取值
Status GetElem(LinkList &L,int i,ElemType &e)//获取第i个数据元素的值
{
p=L->next;
int n=1;//这的取值与获取元素的位置有关;不是0
while(p!=NULL && n<i)
{
p=p->next;
n=n+1;
}
if(n>i && p==NULL)
return ERROR;//其实我感觉不太需要这步;
e=p->data;
return OK;
}
3)查找
跟顺序表中特别类似依次与元素值比较来进行寻找。
//查找
Status *LocateElem(LinkList L,ElemType e)//这个是根据值查找,elemtype代表所有可能的数据类型,
{
p=L->next;
n=1;
while(!p && p->data!=e)
{
p=p->next;
n=n+1;//这个有点多余,另一种方法,比较麻烦;
}
return p;//这里也可以返回n,根据取值也可以找到
}
4)插入
单链表的插入
根据位置进行插入,插入第i个位置,首先找到第i-1个结点,根据第i-1结点来进行插入。
//插入
Status ListInsert(LinkList &L,int i,ElemType e)//这是根据位置插入,带头结点的单链表
{
p=L,j=0;
while(!p && j<i-1)
{
p=p->next;
++j;
}
s=new LNode;
s->data=e;
s->next=p->next;//画个图更容易理解
p->next=s;
return ok;
}
双链表的插入
跟单链表特别相似,就是将前后两个指针域都利用上,两个都是从插入的后指针域开始操作,从后往前。
//双向链表的插入
Status LinkInsertDuL(DuLinkList &L,int i,ElemType e)//在第i个位置之后插入
{
p=GetElemDuL(L,i);//调用之前的查找函数
if(p=NULL)//判断p是否为空
{
return ERROR;
}
s=new DuLNode;
s->data=e;
s->next=p->next;//插入一般都是从插入的后指针开始,画个图容易理解,跟单链表放在一起比较
p->next->prior=s;
s->prior=p;
p->next=s;
return OK;
}
5)删除
单链表的删除
就是将删除结点的前结点指向删除结点的后节点,将删除结点架空。
//删除
Status ListDelete(LinkList &L,int i)//删除第i个元素
{
p=L,j=0;
while(!p && j<i-1)
{
p=p->next;
++j;
}
if(p->next==NULL || j>i-1 )//判断前后两端位置是否合理
{
return ERROR;
}
q=p->next;
p->next=q->next;
delete q;//c++中能用,也可以用free;
return OK;
}
双链表的删除
跟单链表一个性质,前后两个结点都利用上。
//双链表的删除
Status ListDeleteDuL(DuLinkList &L,int i)
{
p=GetElemDuL(L,i);//调用之前的查找函数
if(p==NULL)
{
return ERROR;
}
p->prior->next=p->next;
p->next->prior=p->prior;
return OK;
}
6)创建单链表
前插法
从头结点后面依次插入。
//前插法
void CreateListH(LinkList &L,int n)//从L的后面依次插入
{
L=new LNode;
L->next=NULL;
for(i=0;i<n;i++)
{
p=new LNode;
cin>>p->data;
p->next=L->next;
L->next=p;
}
}
后插法
从链表的末尾插入。
//后插法
void CreateListR(LinkList &L,int n)//依次从最后插入
{
L=new LNode;//这是个头指针
L->next=NULL;
r=L;//尾指针
for(i=0;i<n;i++)
{
p=new LNode;
cin>>p->data;
r->next=p;//这里不能L,为了后续循环方便
p->next=NULL;
r=p;//每次调动尾指针
}
}
2.3顺序表与链表的比较
顺序表:
优点:支持随机存储、存储密度高
缺点:大片连续空间分配不方便,改容量不方便。
链表:
优点:离散的小空间分配方便,改容量方便。
缺点:不可随机存储,储存密度低。
顺序表多用于表长可预估、查询(搜索)操作较多时适用。
链表多用于难以预估、经常要增加/删除元素是适用。