目录
数据结构很重要!
数据结构很重要!!!
数据结构很重要!!!!
思考
1.线性表的链式表示和实现内容?(What)
2.为什么需要线性表的链式表示和实现?(Why)
3.如何学好线性表的链式表示和实现?(How)
注:特别感谢青岛大学王卓老师
一.线性表的链式表示和实现内容?(What)
总体内容概览:思维导图
1.链式存储结构定义:
结点存储位置是任意的,逻辑上位置跟物理上位置不同。
线性表的链式表示又称为非顺序映像或链式映像。
用一组任意的存储单元,来存放线性表的数据元素。
这组存储单元可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上
2.例子:线性表(赵、钱、孙、李)
链表: 结点
存储地址 数据域 指针域
0112 李涵 0053
单链表:由头指针唯一确定,因此单链表可以用头指针的名字来命令。
3.例子:26个英文小写字母表的链式存储结构
逻辑结构:(a,b,c,d,....,z)
链式存储结构:head(头指针)->{数据域1,指针域1}->{数据域2,指针域2}->{..........}
数据域:存储元素数值数据
指针域:存储直接后继结点的存储位置
4.链式存储术语
结点:数据元素的存储映像
链表:n个结点由指针链接在一起组成一个链表。
5.单链表、双链表、循环链表
单链表:结点只有一个指针域的链表
双链表:结点有俩个指针域的链表。
循环链表:首尾相接的链表称为循环链表
6.头指针、头结点、首元结点
头指针:是指向链表中第一个结点的指针。
首元结点:链表中存储第一个数据元素a1的结点。
头结点:首元结点之前附设的一个结点。
7.不带头结点、带头结点
讨论1:如何表示空表?
无头结点:头指针为空时表述空表
有头结点:当头结点的指针域为空时表示空表
讨论2:在链表中设置头结点有什么好处?
有了头结点和后面首元结点处理是一样的。
讨论3:头结点的数据域内装的是什么?
头结点的数据域可以为空,也可以存放线性表长度。
头结点,不计入链表长度值。
8.链表特点
链表:顺序存储、逻辑位置和存储位置不一定相邻。
访问时只能通过头指针进入链表。然后通过每个结点的指针域依次向后顺序扫描其余结点。
顺序表:随机存取、逻辑位置和存储位置相邻。
9.单链表的定义和表示
单链表是:由表头唯一确定。
Lnode:结点。
Lnode a ; //定义一个节点a
a.data a.next
LinkList L; //L就是节点的一个指针
LNode *p 《==等价于==》LinkList L ;
10.单链表操作
1.单链表的初始化
- 分配内存空间
- L地址指向分配空间
- L->next = Null;
2.判断链表是否为空
空链表:链表中无元素
判断:头结点的指针域是否为空
int ListEmpty(LinkList L){
if(L->next) //非空
return 0;
else
return 1;
}
3.单链表的销毁
销毁:需要删除一个个节点(包括删除头结点、头指针)
- 头指针L指向头结点
- p指针 = L
- L=L->next;
- delete p (C++写法)
status DestroyList_L(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next;
delete p;
}
}
4.清空链表
清空链表:头结点的指针域为空(头指针和头结点都在),清空其他元素。
status ClearList(LinkList &L){
Lnode *p,*q;
p=L->next;
while(p){
q=p->next;
delete p;
p=q;
}
L->next = Null;
return ok;
}
5.求单链表的表长
p=L->next
p=p->next
int ListLength(LinkList L){
LinkList p;
p=L->next;
int i=0;
while(p){
i++;
p=p->next;
}
return i;
}
6.知识回顾
1.单链表的销毁
statu Destory(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next;
delete p;
}
return ok;
}
2.清空单链表
status ClearList(LinkList &L){
Lnode *p,*q;
p=L->next;
while(p){
q=p->next;
delete p;
p=q;
}
L->next=Null;
return ok;
}
3.求单链表的表长
int ListLength(LinkList L){
int i = 0 ;
Lnode *p;
p=L->next;
while(p){
i++;
p=p->next;
}
return i;
}
4.判断链表是否为空
status LinkListEmpty(LinkList L){
Lnode *p;
p=L->next;
if(p) return 1;
else return 0;
}
7.取值-取单链表中第i个元素
int GetElem(LinkList L,int i,ElemType &e){
Lnode *p;
p=L->next;
int j=1;
if(p&&j<i){
p=p->next;
j++;
}
if(!p||j>i) return error;
e=p->data;
return e;
}
8.按值查找-根据指定数据获取该数据所在的位置(地址)
int LocalElem(LinkList L, Elem e){
Lnode *p;
p=L->next;
int i =1;
if(p&&p->data!==e){
p=p->next;
i++;
}
if(!p) return 0;
return i;
}
9.插入-在第i个节点前插入值为e的新节点
Status ListInsert(LinkList &L,int i,ElemType e){
Lnode *p;
p=L; j=0;
while(p||j<i-1){
p=p->next;
++j;
}
if(!p||j>i-1) return error;
s=new Lnode ; s->data = e;
s->next=p->next;
p->next = s;
return ok;
}
10.删除-删除第i个节点
Status ListDelete_L(LinkList &L,int i,ElemType &e){
Lnode *p,*q;
p=L, j=0;
if(!p->next&&j<i-1){
p=p->next;
++j;
}
q=p->next;
p->next=q->next;
e=q->data;
delete q;
return ok;
}
11.单链表的查找、插入、删除算法时间效率分析
1.查找:O(n)
2.插入、删除:O(1)
12.单链表:头插法
void CreateList_H(LinkList &L,int n){
L=new LNode;
L->next=Null;
for(i=n;i>0;--i){
p=new LNode;
scanf(&p->data);
p->next=L->next;
L->next=p;
}
}
13.单链表:尾插法
void CreateList_R(LinkList &L,int n){
L=new LNode;
r=L;
for(i=0;i<n;++i){
p=new LNode;
scanf(p->data);
p->next=null;
r->next=p;
r=p;
}
}
二.为什么需要线性表的链式表示和实现?(Why)
1.顺序表的优缺点?
1.顺序表的优点:任意元素均可随机存取。
2.顺训表的缺点:进行插入和删除操作时,需要移动大量的元素,存储空间不灵活。
3.顺序表的特点:以物理位置相邻表示逻辑关系。
2.链表的优缺点?
1.链表优点:插入、删除所耗费的时间复杂度为O(1),从头查找前驱结点消耗时间复杂度为O(n)。
2.链表缺点:只能顺序存取,查找时间为O(n)
3.链表的特点:逻辑上位置跟物理位置不一样,结点是任意存储的。
3.链表分几类?
- 单链表:结点只有一个指针域的链表
- 双链表:结点有俩个指针域的链表
- 循环链表:首尾相接的链表。
4.设置头结点有什么好处?
有了头结点,和后面处理首元结点是一样的。
5.单链表的定义?
单链表:由表头唯一确定。
存储结构:
typedef struct Lnode{
int data;
struct Lnode *next;
}Lnode,*LinkList
6.单链表操作(13种)
1.单链表的初始化
1.分配内存空间
2.L指向分配空间
3.L->next=Null
Status initList(LinkList L){
L=(LinkList)malloc(sizeof(Lnode));
L->next=Null;
}
2.判断链表是否为空
int emptyList(LinkList L){
if(L->next==null){
return 1; //为空
}else{
return 0;
}
}
3.单链表的销毁
销毁:需要删除一个个结点(包括头结点、头指针)
算法:从头指针开始,依次释放所有结点
status destoryList(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next;
delete p;
}
}
4.清空单链表
头结点的指针域为空(头指针和头结点都在),删除其他元素
status CleanList(LinkList &L){
Lnode *p ,*q;
p=L->next;
while(p){
q=p->next;
delete p;
p=q;
}
L->next=Null;
return ok;
}
5.求单链表的长度
int LengthList(LinkList L){
Lnode *p;
int i = 0;
p=L->next;
while(p){
i++;
p=p->next;
}
return i;
}
6.取第i位置,返回该元素
int GetElem(LinkList L,int i,ElemType &e){
Lnode *p;
p=L->next;
int j=1;
if(p&&j<i){
p=p->next;
j++;
}
if(!p||j>i) return error;
e=p->data;
return e;
}
9.插入-在第i个节点前插入值为e的新节点
1.首先要找到a(i-1)的存储位置
2.生成一个数据域为e的新结点s
3.插入新结点:1.新结点的指针域指向ai
2.结点a(i-1)的指针域指向新结点
s->next=p->next
p->next=s
Status InsertList(LinkList &L,int i,ElemType e){
Lnode *p;
p=L,j=0;
while(p||j<i-1){
p=p->next;
++j;
}
if(!p||j>i-1) return error;
s=new Lnode;s->data=e;
s->next=p->next;
p->next=s;
return ok;
}
10.删除-删除第i个节点
Status deleteList(LinkList &L,int i){
Lnode *p;
p=L,j=0;
while(p||j<i-1){
p=p->next;
++j;
}
if(!p||j>i-1) return error;
q=p->next;
p->next=q>next;
delete q;
return ok;
}
11.单链表:头插法
1.从一个空表开始
2.生成新结点,将读入数据存放到新结点
3.新结点依次插入头部
void CreatList(LinkList &L,int n){
L=new Lnode;
L->next = null;
for(i=n;i>0;i--){
p=new Lnode;
scanf(&p->data);
p->next=L->next;
L->next=p;
}
}
12.单链表:尾插法
void CreatList(LinkList &L,int n){
L=new Lnode;
L->next = null;
int i;
for(i=0;i<n;++i){
p=new Lnode;
scanf(&p->data);
p->next=null;
r->next=p;
r=p;
}
}
三.如何学好线性表的链式表示和实现?(How)
1.多总结、多思考、多输出
多找案例,多练,多敲
2.用心
搞清楚,链表的定义,各种操作,扎实基础。
用心
用心、用心
用心、用心、用心
知其然、知其所以然!!!
多学习,多思考,多总结,多输出,多交流 (five kills)~~~