参考网课数据结构-青岛大学-王卓-第三周
参考书籍《数据结构C语言版第二版》 中国工信出版集团 人民邮电出版社出版 严蔚敏 编著
线性表的链式存储
单链表,双链表,循环链表
头指针,头结点,首元节点
Note
在链表中设计头结点有什么好处? 1.在处理首元节点的变化会比较方便——与其他位置一致。 2.便于非空表和空表的统一处理——头指针都是指向头结点的非空指针。
Warning
头结点在统计表长的时候不计入,不属于数据域。
链表的储存结构特点
- 顺序存取法(对比随机存取法):只能从前往后找,越往后访问越慢。
- 数据域+指针域
单链表的类型定义和变量定义
类型定义
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}Lnode,*LinkList;
Note
嵌套定义:自己的定义包含了自己。自己:
struct Lnode
typedef
: 重新起名字
变量定义
LinkList L; //定义链表L,指向头节点的指针通常用于代表整个链表
LNode *p; //定义节点指针p
//LNode *L; 通常不用
//LinkList p; 通常不用
单链表通常如下定义(具体例子 )
为了统一链表操作,通常如下定义:
typedef Struct{
char num[8]; //数据域
char name[8]; //数据域
int score; //数据域
}ElemType;
typedef struct Lnode{
ElemType data; //数据域
struct Lnode *next; //指针域
}Lnode,*LinkList;
Hint
这样定义的想法是先定义一个
ElemType
,再结合ElemType
加上指针域定义链表,而不是吧二者直接全部写到链表里面。
单链表基本操作的实现
单链表的初始化(C++的写法)
Status InitList_L(LinkList &L){
L=new LNode;//或L=(LinkList)malloc(sizeof(LNode));是c的写法
L->next=NULL;
}
Note
NULL
是需要事先定义过的一些常用常量。
补充算法1-判断链表是否为空
int ListEempty(LinkList L){
if(L->next) return 0;
else return 1;
}
补充算法2-单链表的销毁
Status DestoryList_L(LinkList &L){
Lnode *p;
while(L){
p=L;
L=L->next //把节点指针进行后移
delete p;
}
return OK;
}
Note
->
表示结构体指针。即当有一个指向某个结构体的指针p
后,想要把结构体中的a
赋值给变量x
,则可以写:x=p->a
Note
L=L->next //把节点指针进行后移 p=L->next //让指针p指向首元节点 p=L //让指针p指向头节点
上面三个有注释的操作非常重要
补充算法3-清空链表
Status ClearList(Linklist &L){
Lnode *p,*q;
p=L->next;
while(p){
q=p->next;
delete p;
p=q;
}
L->next=NULL;
return OK;
}
Note
需要用到两个指针,在其中一个指针删除对应的内容之前,要让这个指针指向的节点的next部分存出来,不然执行删除之后就找不到下一个节点了。
补充算法4-求单链表表长
int ListLength_L(LinkList &L){
LinkList p;
p=L->next;
i=0;
while(p){
i++;
p=p->next;
}
return i;
}
取值-取单链表中第 i 个元素
Status GetElem_L(LinkList L, int i, ElemType &e){
p=L->next;
j=1;
while(p&&j<i){
p=p->next;
++j;
}
if(!p||j>i) //第i个元素不存在时
return ERROR;
e=p->data;
return OK;
}
按值查找-返回地址
Lnode *LocateElem_L(LinkList L,Elemtype e){
p=L->next;
while(p&&p->data!=e)
p=p->next;
return p;
}
按值查找-返回位置序号
int LocateElem_L(LinkList L, Elemtype e){
p=L->next;
j=1;
while(p&&p->data!=e)
p=p->next;
if(p) return j;
else return 0; //表示查找失败
}
插入-在指定标号节点前
Status ListInsert_L(LinkList &L, int i, ElemType e){
p=L;
j=0;
while(p&&j<i-1){p=p->next;++j;}//find the i-1th node 第二要点
if(!p||j>i-1) return ERROR;//whether the i-1th node do not exist 第三要点
s=new LNode;
s->next=p->next; //insert the new node.注意赋值顺序不能换,先接上后边的表,再接上前面。第一要点
p->next=s;
return OK;
}
Warning
注意insert时候赋值顺序不能换,先接上后边的表,再接上前面。如果调换顺序需要多定义一个变量作为中间变量。
Warning
注意三大要点,详见上面注释。
删除-删除指定节点
Status ListDelete_L(LinkList &L, int i, ElemType &e){
p=L;
j=0;
while(p->next&&j<i-1){p=p->next;++j;} //寻找第i个节点,第二要点
if(!(p->next)||j>i-1) return ERROR; //判断删除位置是否合理,第四要点
q=p->next;
p->next=q->next; //改变删除节点的前驱节点的指针域为下个节点,第一要点
e=q->data;
delete q; //释放删除节点的储存空间,第三要点
return OK;
}
Warning
注意四大要点,已经在注释中标出。
单链表查找,插入,删除的时间效率分析
操作 | 时间复杂度 |
---|---|
查找 | |
插入和删除 |
单链表的建立-头插法
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; //输入元素值 或 scanf(&p->data);
p->next=L->next; //插入到表头
L->next=p;
}
}
Hint
此处重要!(关于插入到表头的代码的解释)
p->next=L->next; L->next=p;
含义解释:先把上一个节点的指针域赋值给新节点的指针域;
再将上一个节点的指针域写入为新节点的地址。
单链表的建立-尾插法
void CreateList_R(LinkList &L, int n){
L=new LNode;
L->next=NULL;
r=L;
for(i=0;i<n;++i){
p=new LNode;
cin>>p->data; //生成新节点,输入元素值
p->next=NULL; //令新节点的指针域为空,重点
r->next=p; //令新节点的地址存在旧表末尾节点的指针域中,重点
r=p; //将r指向新表的末尾节点,重点
}
}
时间复杂度,关于重点详见注释,注意操作顺序。