链式存储相关术语:
数据域:存储元素数值数据
指针域:存储直接后继结点的存储位置
-
结点:数据元素的存储映像。又1数据域和指针域两部分组成
-
链表:n个结点由指针链组成一个链表
-
单链表,双链表,循环链表
-
结点只有一个指针域的链表,称为单链表或线性链表
-
结点有两个指针域的链表,称为双链表
-
首尾相接的链表称为循环链表
-
-
头指针、头结点、和首元结点
-
头指针:是指向链表中第一个结点的指针
-
首元结点:是指链表中存储第一个数据元素a1的结点
-
头结点:是在链表的首元结点之前附设的一个结点
-
讨论一、如何判断空表
不带头结点:头指针指向为NULL
带头结点:头结点指向为空
讨论二、在链表中设置头结点有什么好处?
-
便于首元结点的处理
-
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无须进行特殊处理;
-
-
便于空表和非空表的统一处理
-
无论链表是否为空,头指针都指是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
-
讨论三、头结点的数据域内装的是什么
头结点的数据域可以为空,可以存放线性表的长度等附加信息,此时结点不能计入链表长度值。
链式存储的特点:
-
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
-
访问时只能通过头指针进入链表,并通过每个节点的指针域依次向后顺序扫描其余节点,所以寻找第一个结点和最后一个结点所花费的时间不等
-
顺序存储法
单链表的存储结构:
例如,存储学生学号、姓名、成绩的单链表结点类型如下:
单链表的初始化(算法2.6)(带头结点的单链表)
-
即构造一个空表
【算法步骤】
-
生成新结点作头结点,用头指针L指向头结点
-
将头结点的指针域置空
【算法描述】
// 单链表的初始化
Status InitLinkList(LinkList &L){
L = (LinkList)malloc(sizeof(LNode));// 删除的时候用free(L) // 或 L = new LNode; 删除的时候用 delete L;
L->next = NULL;
return OK;
}
单链表的销毁
// 单链表的销毁
Status DistoryLinkList(LinkList &L){
Lnode *p; // 或者 LinkList p;
while(L){
p=L;
L=L->next;
free(p);
}
return OK;
}
清空单链表
链表依然存在,但是链表中无元素,成为空链表(头指针和头结点仍然在)
【算法思路】:依次释放所有结点,并将头结点指针域设置为空
// 清空单链表
Status ClearLinkList(LinkList &L){ // 将链表L重置为空表
Lnode *p,*q; // 或者 LinkList p,q;
p=L->next;
while(p){
q = p->next;
free(p);
p = q;
}
L->next = NULL; // 头结点指针域为空
return OK;
}
单链表的取值-- 取单链表中第i个元素的内容
算发步骤:
-
从第一个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p = L->next。
-
j做计数器,累计当前扫描过的结点数,j 初始值为1
-
当p指向扫描到的下一结点时,计数器j加1
-
当j == i 时,p 所指的结点就是要找的第 i 个结点
// 获取单链表中指定位置的数据
Status GetElem(LinkList L,int i,ElemType &e){ // 获取单链表中指定位置数据元素的内容,通过变量 e 返回
Lnode *p; // 初始化
int j = 1;
p = L->next;
while(p&&j<i){ // 向后扫面,直到 p 指向第i个元素,或 p 为空
p=p->next;
j++;
}
if(!p&&j>i){ // 第i个元素不存在
return ERROR;
}
e = p->date; // 取第i个元素
return OK;
}
按值查找--根据指定数据获取该数据所在的位置(地址)
【算法步骤】:
-
从第一个结点起,依次和 e 相比较
-
如果找到一个其值与e 相等的数据元素,则返回其在链表中的位置或地址
-
如果查遍整个链表都没有找到与e相等的元素,则返回0或者NULL
// 1、按值查找,返回地址
/*
在线性表L中查找值为 e 的数据元素
找到,则返回对应的数据元素的地址,查找失败则返回NULL
*/
Lnode *LocateElem(LinkList L,ElemType e){
Lnode *p;
p = L->next;
while(p && p->date != e){
p = p->next;
}
return p;
}
// 2、按值查找,返回指定位置序号
// 查找 对应的值所在的位置,并返回位置序号
int LocateElem_L(LinkList L,ElemType e){
Lnode *p;
int j=1;
p = L->next;
while(p&&p->date!=e){
p = p->next;
j++;
}
if(p){
return j;
}
else{
return 0;
}
}
插入 --在第i 个结点前插入值为e 的新结点
【算法步骤】:
-
首先找到ai-1的存储位置p
-
生成一个数据域为e的新节点s
-
插入新结点:
-
新结点的指针域指向ai
-
新结点ai-1的指针域指向新结点
-
// 插入 --在第i 个结点前插入值为e 的新结点
Status InsertLinkList(LinkList &L,int i,ElemType e){
Lnode *p,*s;
int j = 0;
p = L;
while(p&&j<i-1){ // 寻找i-1个结点
p = p->next;
j++;
}
if(!p||j>i-1){ // i大于表长加1或者小于1,插入位置非法
return ERROR;
}
s = new Lnode;
s->date = e; // 生成新结点s ,将结点的数据域置为e
s->next=p->next; // 将结点s插入L中
p->next=s;
return OK;
}
删除---删除第i个节点
// 删除-- 将链表中第i个元素删除
Status DeleteLinkList(LinkList &L,int i,ElemType &e){
Lnode *p,*q;
int j=0;
p = L;
while(j<i-1&&p->next){ // 寻找第i个结点,并令p指向其前趋
p = p->next;
j++;
}
if(j>i-1||p->next){ // 删除位置不合理
return ERROR;
}
q = p->next; // 临时保存被删除结点的地址以备释放
p->next = q->next; // 改变删除结点前驱结点的指针域
e = q->date; // 保存删除结点的数据域
free(q); // 释放删除结点的空间
return OK;
}
建立单链表:头插法-元素插入在链表的头部也叫前插法
-
从一个空表开始,重复读入数据
-
生成新结点吗,将读入数据存放到新结点的数据域中
-
从最后一个结点开始,依次将各结点插入到链表的前端
// 头插法
void CreateLinkListHead(LinkList &L,int n){
Lnode *p;
L = (LinkList)malloc(sizeof(Lnode));
L->next = NULL; // 先建立一个带头结点的单链表
for(int i = n;i>0;i--){
p = (LinkList)malloc(sizeof(Lnode)); // 生成新结点
scanf(&p->date); // 输入元素值
p->next = L->next; // 插到表头
L->next = p;
}
}
尾插法--元素插入在链表的尾部,也叫后插法
//尾插法
void CreateLinkList_R(LinkList &L,int n){
Lnode *p,*r;
L = new Lnode;
L->next = NULL;
r = L; // 尾指针指向头结点
for(int i = 0;i<n;i++){
p = new Lnode; // 生成新结点
scanf(&p->date); // 输入元素值
p->next = NULL;
r->next = p; // 插到表尾
r = p; // r 指向新的尾结点
}
}
汇总
#include<stdio.h>
# define OK 1
# define ERROR 0
typedef char ElemType;
typedef int Status;
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
// 1、单链表的初始化
Status InitLinkList(LinkList &L){
L = new LNode;
L->next = NULL;
return OK;
}
// 2、判断链表是否为空
void IsEmpty(LinkList &L){
if(L->next){
printf("单链表非空\n");
}else{
printf("单链表为空\n");
}
}
// 3、单链表的销毁
void DistoryLinkList(LinkList &L){
LNode *p;
while(L){
p=L;
L=L->next;
delete p;
}
printf("单链表已经销毁\n");
}
// 4、清空单链表
void ClearLinkList(LinkList &L){
LNode *p,*q;
p = L->next;
while(p){
q = p->next;
delete p;
p = q;
}
L->next=NULL;
printf("链表已经清空\n");
}
// 5、求单链表的表长
int LinkListLength(LinkList &L){
LNode *p;
int i = 0;
p = L->next;
while(p){
i++;
p = p->next;
}
return i;
}
// 6、获取单链表中指定位置的数据
Status GetElem(LinkList L,int i,ElemType &e){ // 获取单链表中指定位置数据元素的内容,通过变量 e 返回
Lnode *p; // 初始化
int j = 1;
p = L->next;
while(p&&j<i){ // 向后扫面,直到 p 指向第i个元素,或 p 为空
p=p->next;
j++;
}
if(!p&&j>i){ // 第i个元素不存在
return ERROR;
}
e = p->date; // 取第i个元素
return OK;
}
// 7-1、按值查找,返回地址
/*
在线性表L中查找值为 e 的数据元素
找到,则返回对应的数据元素的地址,查找失败则返回NULL
*/
Lnode *LocateElem(LinkList L,ElemType e){
Lnode *p;
p = L->next;
while(p && p->date != e){
p = p->next;
}
return p;
}
// 7-2、按值查找,返回指定位置序号
// 查找 对应的值所在的位置,并返回位置序号
int LocateElem_L(LinkList L,ElemType e){
Lnode *p;
int j=1;
p = L->next;
while(p&&p->date!=e){
p = p->next;
j++;
}
if(p){
return j;
}
else{
return 0;
}
}
// 8、插入 --在第i 个结点前插入值为e 的新结点
void InsertLinkList(LinkList &L,int i,ElemType e){
LNode *p,*q;
int j = 0;
p = L;
while(p&&j<i-1){
p = p->next;
j++;
}
if(!p||j>i-1){
printf("结点插入失败\n");
}
q = new LNode;
q->data = e;
q->next=p->next;
p->next = q;
printf("结点插入成功\n");
}
// 9、删除-- 将链表中第i个元素删除
void DeleteList(LinkList &L,int i){
LNode *p,*q;
int j = 0;
p=L;
while(p&&j<i-1){
p = p->next;
j++;
}
if(!p||j>i-1){
printf("删除失败\n");
}
q= p->next;
p->next = q->next;
delete q;
printf("结点删除成功!\n");
}
// 10、头插法
void InsertList_H(LinkList &L,int n){
LNode *p;
L = new LNode;
L->next = NULL;
for(int i = n;i>0;i--){
p = new LNode;
scanf(&p->data);
p->next=L->next;
L->next = p;
}
printf("数据插入头成功\n");
}
// 11、尾插法
void InsertList_R(LinkList &L,int n){
LNode *p,*r;
L = new LNode;
L->next = NULL;
r = L;
for(int i=0;i<n;i++){
p = new LNode;
scanf(&p->data);
p->next=NULL;
r->next = p;
r = p;
}
printf("尾插法插入成功!\n");
}
int main(){
// 1、单链表的初始化
LinkList L;
int init = InitLinkList(L);
printf("单链表的初始化是否成功:%d\n",init);
// 2、判断链表是否为空
IsEmpty(L);
// 3、单链表的销毁
// DistoryLinkList(L);
// 4、链表清空
ClearLinkList(L);
// 8、节点的插入
InsertLinkList(L,1,'A');
InsertLinkList(L,2,'A');
// 10、头插法
InsertList_H(L,3);
// 11、尾插法
InsertList_R(L,3);
// 5、求链表的表长
int length = LinkListLength(L);
printf("链表的表长:%d\n",length);
// 9、结点删除
DeleteList(L,1);
// 5、求链表的表长
int length1 = LinkListLength(L);
printf("链表的表长:%d\n",length1);
return 0;
}