数据结构和算法
线性表的链式表示和实现
线性表的链式存储结构
- 线性表中数据元素(结点)在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理位置不一定相邻
- 结点(数据+指针)
- 链表(n个结点由指针链组成一个链表)
- 单链表:每个结点只有指针域
- 双链表:每个结点有两个指针域
- 循环链表:链表节点首尾相接
单链表的定义和表示
- 带头结点的单链表(非空表、空表)
- 单链表是由表头唯一确定,由此单链表可以用头指针的名字来命名,若头指针名为L,则把链表称为表L
- 单链表的存储结构(数据域+指针域)
typedef struct Lnoce{ //声明结点的类型和指向结点的指针类型
ElemType data; //结点的数据域
struct Lnode *next; //结点的指域
}Lnode,*LinkList; //LinkList为指向结构体Lnode的指针类型
- 定义链表L : LinkList L;
- 定义结点指针p: LNode*p <=> LinkList p;
重点:单链表基本操作的实现
单链表的初始化(带头结点的单链表)
- 即构造一个如图的空表
算法步骤
- 生成新结点作头节点,用头指针L指向头节点
- 将头节点的指针域置空
- 算法描述:
Status InitList_L(LinkList &L){
L = new LNode;//或L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
return OK;
}
补充单链表的几个常用简单算法
补充算法1---判断链表是否为空:
空表:链表中无元素,称为空链表(头指针和头结点仍然在)
【算法思路】判断头节点指针域是否为空
int ListEmpty(ListList L){ //若L为空表,则返回1,否则返回0
if(L->next) return 0;//非空
else return 1;
}
补充算法2---单链表的销毁:链表销毁后不存在
【算法思路】从头指针开始,依次释放所有结点
Status Destroylist_L(LinkList &L){ //销毁单链表L
Lnode *p;//或LinkList p;
while(L){
p=L;
L=L-next;
delete p;
}
return OK;
}
补充算法3---清空链表:
链表仍存在,但链表中无元素,成为空链表(头指针和头节点仍然在)
【算法思路】依次释放所有结点,并将头节点指针域设置为空
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;
}
补充算法4---求单链表的表长
【算法思路】从首元结点开始,依次计数所有结点
int ListLength_L(LinkList L){ //返回L中数据元素个数
LinkList p;
p=L->next; //p指向第一个结点
i=0;
while(p){ //遍历单链表,统计结点数
i++;
p=p->next;
}
return i;
}
补充算法5---取值(取单链表中第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,Elem Type &e){
p =L->next;
j=i; //初始化
while(p&&j<i){ //向后扫描,直到p指向第i个元素或p为空
p=p->next;++j;
}
if(!p || j>i) return ERROR; //第i个元素不存在
e =p->data; //取第i个元素
return OK;
}//GeteElem_L
补充算法6---按值查找(根据指定数据获取该数据所在的位置<地址>)
【算法步骤】
- 从第一个结点起,依次和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;
}
补充算法7---按值查找(根据指定数据获取该数据位置序号)
【算法描述】
//在线性表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;
}
补充算法8---插入(在第i个结点前插入值为e的新结点)
【算法步骤】
- 首先找到
的存储位置p
- 生成一个数据域为e的新结点s.
- 插入新结点:
- 新结点的指针域指向结点
- 结点
的指针域指向新结点
- 新结点的指针域指向结点
【算法描述】
Status ListInsert_L(LinkList &L,int i,ElemType e){
p=L;j=0;
while(p&&j<i-1){ //寻找第i-1个结点,p指向i-1结点
p=p->next;++j;
}
if(!p || j>i-1) return ERROR; //i大于表长+1或者小于1,插入位置非法
s = new LNode; s->data = e; //生成新结点s,将结点s的数据域置为e
s->next = p->next; //将结点s插入关键步骤
p->next = s;//将结点s插入关键步骤
return OK;
} //ListInsert_L
补充算法9---删除(删除第i个结点)
【算法步骤】
- 首先找到
的存储位置p,保存要删除的
的值
- 令p->next 指向
- 关键步骤
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
单链表的查找、插入、删除算法事件效率分析
- 查找
因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为O(n). - 插入和删除
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为O(1).
但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n).
补充算法10---建立单链表:头插法(元素插入在链表头部,也叫前插法)
- 从一个空表开始,重复读入数据;
- 生成新结点,将读入数据存放到新结点的数据域中
- 从最后一个结点开始,依次将各结点插入到链表的前端
【算法步骤】首先建造一个空结点
L = new LNode;//或C语言中如下: L=(LinkList)malloc(sizeof(LNode));
void CreateList_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;//输入元素值scand(&p->data);
p->next = L->next; //插入到表头
L->next = p;
}
}//CreateList_H; 算法的时间复杂度是O(n)
补充算法11单链表的建立--后插法(尾插法)
- 从一个空表L开始,将新节点逐个插入到链表的尾部,尾指针r指向链表的尾结点。
- 初始时,r同L均指向头节点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点
- 关键步骤
p->data = a;
p->next = NULL;
r->next = p;
r=p;
【算法描述】
//正位序输入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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程