目录
①按值查找:根据指定数据获取该数据所在的位置(该数据的地址)
一、链表(链式存储结构)的特点
(1)结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻;
(2)访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。
这种存取元素的方法被称为顺序存取法。
顺序表:随机存取;
链表:顺序存取。
二、单链表的定义和表示
1.带头结点的单链表
单链表是由表头唯一确定,因此单链表可以用头指针的名字来命名,若头指针名是L,则把链表称为表L。
2.单链表的存储结构
数据域 | 指针域 |
data | next |
typedef struct Lnode{ //声明结点的类型和指向结点的指针类型
ElemType data; //结点的数据域
struct Lnode *next; //结点的指针域
}Lnode,*LinkList; //LinkList为指向结构体Lnode的指针类型
三、单链表基本操作的实现
1.单链表的初始化(带头结点的单链表)
即构造一个空表
算法步骤:
(1)生成新结点作头结点,用头指针L指向头结点;
(2)将头结点的指针域置空。
算法描述:
Status InitList_L(LinkList &L){
L=new LNode; //或L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
return OK;
}
2.补充单链表的几个常用简单算法
判断链表是否为空:
空表:链表中无元素,称为空链表(头指针和头结点仍然在)
算法思路:判断头结点指针域是否为空
int ListEmpty(LinkList L){ //若L为空表,则返回1,否则返回0
if(L->next) //非空
return 0;
else
return 1;
}
销毁单链表L:
Status DestroyList_L(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next;
delete p;
}
return OK;
}
清空链表:
链表仍存在,但链表中无元素,成为空链表(头指针和头结点仍然在)
【算法思路】依次释放所有结点,并将头结点指针域设置为空。
Status ClearList(LinkList &L){ //将L重置为空表
Lnode *p,*q; //或LinkList p,q;
p=L->next;
while(p){ //没到表尾
q=p->next;
delete p;
p=q;
}
L->next=NULL; //头结点指针域为空
return OK;
}
求链表的表长:
【算法思路】从首元结点开始,依次计数所有结点
int ListLength_L(LinkList L){ //返回L中数据元素个数
LinkList p;
p=L->next; //p指向第一个结点
i=0;
while(p){ //遍历单链表,统计结点数
i++;
p=p->next;
}
return i;
}
知识回顾:
带头结点的单链表:
类型定义:
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
变量定义:
LinkList L;
LNode *P,*s;
重要操作:
p=L; //p指向头结点
s=L->next; //s指向首元结点
p=p->next; //p指向下一结点
取值——取单链表中第i个元素的内容:
【算法思路】从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构。
【算法步骤】
- 从第1 个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p=L->next。
- j做计数器,累计当前扫描过的结点数,j初值为1。
- 当p指向扫描到的下一结点时,计数器j加1。
- 当j==i时,p所指的结点就是要找的第i个结点。
【算法描述】
Status GetElem_L(LInkList L,int i,ElemType &e){ //获取线性表L中的某个数据元素的内容,通过变量e返回。
p=L->next;j=1; //初始化
while(p&&j<i){ //向后扫描,直到p指向第i个元素或p为空
p=p->next;++j;
}
if(!p||j>i)return ERROR; //第i个元素不存在
e=p->data; //取第i个元素
return OK;
}GetElem_L
查找:
①按值查找:根据指定数据获取该数据所在的位置(该数据的地址)
【算法步骤】
- 从第一个结点起,依次和e相比较。
- 如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置”或地址;
- 如果查遍整个链表都没有找到其值和e相等的元素,则返回0或“NULL”。
【算法描述】
Lnode *LocateElem_L(LinkList L,Elemtype e){
//在线性表L中查找值为e的数据元素
//找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p;
}
②按值查找——根据指定数据获取该数据位置序号
【算法描述】
//在线性表L中查找值为e的数据元素的位置序号
int LocateElem_L(LinkList L,Elemtype e){
//返回L中值为e的数据元素的位置序号,查找失败返回0
p=L->next;j=1;
while(p&&p->data!=e)
{p=p->next;j++;}
if(p)return j;
else return 0;
}
插入——在第i个结点前插入值为e的新结点
【算法步骤】
- 首先找到a(i-1)的存储位置p。
- 生成一个数据域为e的新结点s
- 插入新结点:①新结点的指针域指向结点a(i);②结点a(i-1)的指针域指向新结点
①s->next=p->next;②p->next=s;
思考:步骤①和②能互换吗?先执行②,后执行①,可以么?
不可以!会丢失ai的地址
【算法描述】
//在L中第i个元素之前插入数据元素e
Status ListInsert_L(LinkList &L,int i,ElemType e){
p=L;j=0;
while(p&&j<i-1){p=p->next;++j;} //寻找第i-1个结点,p指向i-1结点
if(!p||j>i-1)return ERROR; //i大于表长+1或者小于1,插入位置非法
s=new LNode; s->data=e; //生成新结点s,将结点s的数据域置为e
s->next=p->next; //将结点s插入L中
p->next=s;
return OK;
}ListInsert_L
删除——删除第i个结点
【算法步骤】
- 首先找到a(i-1)的存储位置p,保存要删除的a(i)的值。
- 令p->next指向a(i+1)。
p->next=p->next->next;
//将线性表L中第i个数据元素删除
Status ListDelete_L(LinkList &L,int i,ElemType &e){
p=L;j=0;
while(p->next&&j<i-1){p=p->next;++j;} //寻找第i个结点,并令p指向其前驱
if(!(p->next)||j>i-1)return ERROR; //删除位置不合理
q=p->next; //临时保存被删除结点的地址以备释放
p->next=q->next; //改变删除结点前驱结点的指针域
e=q->data; //保存删除结点的数据域
delete q; //释放删除结点的空间
return OK;
}ListDelete_L
单链表的查找、插入、删除算法时间效率分析
1.查找:
因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为O(n)。
2.插入和删除:
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为O(1)。
但是,如果要在单链表中进行前插或删除操作,由于从头查找前驱结点,所耗时间复杂度为O(n)。
建立单链表:头插法——元素插入在链表头部,也叫前插
- 从一个空表开始,重复读入数据;
- 生成新结点,将读入数据存放在新结点的数据域中;
- 从最后一个结点开始,依次将各结点插入到链表的前端。
【算法描述】
void CreatList_H(LinkList &L,int n){
L=new LNode;
L->next=NULL; //先建立一个带头结点的单链表
for(i=n;i>0;--i){
p=new LNode; //生成新结点p=(LNode*)malloc(sizeof(LNode));
cin>>p->data; //输入元素值scanf(&p->data);
p->next=L->next; //插入到表头
L->next=p;
}
}CreatList_H
建立单链表:尾插法——元素插入在链表尾部,也叫后插法
- 从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
- 初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。
【算法描述】
//正位序输入n个元素的值,建立带表头结点的单链表L
void CreateList_R(LinkList&L,int n){
L=new LNode; L->next=NULL;
r=L; //尾指针r指向头结点
for(i=0;i<n;++i){
p=new LNode;cin>>p->data; //生成新结点,输入元素值
p->next=NULL;
r->next=p; //插到表尾
r=p; //r指向新的尾结点
}
}CreateList_R
算法的时间复杂度是:O(n)