链表的表示和实现
链表的定义
链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
链表的存储方法
- 单链表
- 循环链表
- 双向链表
优缺点
数据元素的个数可以扩充;插入,删除等操作修改效率高;存储密度小;存取效率低,必须采取顺序存取,即按照链表顺序依次访问。
与链式存储相关的术语
-
头指针:指向链表中第一个结点的指针
-
首元结点:链表中存储第一个数据元素a1的结点
-
头结点:在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息。为了操作一致性而设定。
单链表的类型定义
typedef struct {
ElemType data;//数据域
struct LNode *next; //指针域
}LNode,*LinkList;//*LinkList为LNode类型的指针
LNode *p 与 LinkList p等价
单链表的重要基本操作
- 初始化
//初始化 (构造一个空的链表)
Status InitList(LinkList &L){
L=new LNode; //生成新结点作为头结点,用头指针L指向头结点
L->next=NULL;//头结点的指针域置空
return OK;
}
- 取值
//取值 (取出第i个结点的元素的值,并赋值给e)
Status GetElem(LinkList L,int i,ElemType &e)
{
p=L->next;
j=1; //计数器j初赋值为1
while(p && j<i) //往后遍历,直到p为空或者p指向第i个元素
{
p=p->next; //p指向下一个结点
j++; //计数器加一
}
if(!p || j>i ) return ERROR; //i>n 或者 i≤0 都不合法
p=p->next;
e=p->data; //取出第i个结点的数据域
return OK;
}
- 查找
//查找(查找值为e的元素)
LNode* LocateElem(LinkList L,ElemType e)
{
p=L->next;
while(p && p->data!=e)
{
p=p->next;
}
return p; //查找成功返回值为e的结点地址;查找失败返回NULL
}
- 插入
步骤:
①查找结点ai-1并由指针p指向该结点
②生成一个新结点 *s
③将 *s的数据域设为e,指针域指向结点ai
④将结点 *p的指针域指向新结点 *s
//插入 将e插入到表的第i个结点的位置
Status ListInsert(LinkList &L,int i,ElemType e)
{
p=L->next;
j=1;
while(p && j<i-1) //查找第i-1个结点
{
p=p->next;
j++;
}
if( !p || j>i-1) return ERROR; //i>n+1 或者 i<1 都不合法
LNode *s;
s=new LNode;
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
- 删除
步骤:
①查找结点ai-1并由指针p指向该结点
②临时保存待删除结点ai的地址在q中,已备释放
③将结点 *p的指针域指向ai的直接后继结点
④释放ai的空间
//删除 删除第i个结点
Status ListDelete(LinkList &L,int i)
{
p=L->next;
j=1;
while(p && j<i-1 )
{
p=p->next;
j++;
}
if(!(p->next) || j>i-1) return ERROR;//i>n 或者 i<1 都不合法
//注意此语句与插入的不同点!
q=p->next;
p->next=q->next;
delete q;
return OK;
}
- 清空
//清空
Status ClearList(LinkList &L){
LinkList p,q;
p=L->next;
while(p)
{
q=p->next;
delete p;
p=q;
}
L->next=NULL;
return OK;
}
7.销毁
Status Destroy_List(LinkList &L){
LinkList p;
while(L)
{
p=L;
L=L->next;
delete p;
}
return OK;
}
- 创建单链表(前插法)
//前插法
void CreateList_H(LinkList &L,int n)
{
L=new LNode;
L->next=NULL;
for(int i=0;i<n;i++)
{
p=new LNode;//生成新结点
cin>>p->data;
p->next=L->next;//将新结点*p插入到头节点之后
L->next=p;//更新头结点的指针域
}
}
- 创建单链表(尾插法)
//尾插法
void CreateList_R(LinkList &L,int n)
{
L=new LNode;
L->next=NULL;
r=L; //尾指针r先指向头结点
for(int i=0;i<n;i++)
{
p=new LNode;//生成新结点
cin>>p->data;
p->next=NULL;
r->next=p;//将新结点*p插入尾节点*r之后
r=p; //更新尾指针
}
}
循环链表
定义:尾节点的指针域指向L
特点:从循环链表中的任何一个结点的位置都可以找到其他所有结点,而单链表做不到
注意:
- 循环链表没有明显的尾端,要避免死循环!
循环条件:
单链表 | 循环链表 |
---|---|
p != NULL | p != L |
p->next != NULL | p->next != L |
-
对循环链表,有时不给出头指针,而给出尾指针,可以更方便的找到第一个和最后一个结点
首元结点:rear->next->next
终端节点:rear
- 两个单循环链表的链接
LinkList Connect(LinkList Ta,LinkList Tb)
{//假设Ta、Tb都是非空的单循环链表
p=Ta->next; //p存表头结点
Ta->next=Tb->next->next; //Tb表头连结Ta表尾
delete Tb->next; //释放Tb表头结点
Tb->next=p; //修改指针
return Tb;
}
双向链表
- 双向链表的插入
Status ListInsert_DuL(DuLinkList &L,int i,ElemType e){
if(!(p=GetElemP_DuL(L,i))) return ERROR;
s=new DuLNode;
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return OK;
}
- 双向链表的删除
Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e)
{
if(!(p=GetElemP_DuL(L,i))) return ERROR;
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
delete p;
return OK;
}