目录
单链表
线性表的链式存储结构
-
链式存储结构
-
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻
-
结点中有 数据域(存放数据)和 指针域(存放地址),n个结点由指针链组成链表。
-
-
线性表的链式表示又称为 非顺序映像 或 链式映像。
链表特点:
-
用一组 物理位置任意的存储单元 来存放线性表的数据元素。
-
这组存储单元既可以是 连续 的,也可以是 不连续 的,甚至是零散分布在内存中的任意位置上的。
-
链表中元素的 逻辑次序 和 物理次序 不一定相同。
-
访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。因此,这种存取元素的方法被称为 顺序存取法。(顺序表是随机存取)
链表有单链表,双链表,循环链表(之后再细说):
-
单链表:结点只有一个指针域的链表,称为单链表。
-
双链表:结点有两个指针域的链表,称为双链表。
-
首尾相接的链表称为循环链表。
头指针、头结点、首元结点:
-
头指针:是指向链表中第一个结点的指针。
-
首元结点:是指链表中存储第一个数据元素a1的结点。
-
头结点:是在链表的首元结点之前附设的一个结点。(一般都带头结点,因为方便操作,之后细说)
所以链表可分为 带头结点的链表 和 不带头结点 的链表。
头结点不计入表长!
头结点的好处:
-
便于 首元结点 的处理
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其他位置一致,无须进行特殊处理。
-
便于 空表和非空表 的统一处理
当没有头结点 且 为空表时,头指针指向NULL,有头结点时无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
单链表(带头结点)
-
单链表是由表头唯一确定,因此单链表通常用头指针的名字来命名,若头指针名为L,则称链表为表L。
定义结点
typedef struct Lnode { //声明结点的类型和指向结点的指针类型
ElemType data; //结点的数据域
struct Lnode *next; //结点的指针域
}Lnode, *LinkList; //LinkList为指向结构体Lnode的指针类型
定义链表L:LinkList L;
定义结点指针:LNode *p;(虽然LinkList p和LNode *p一样,但为了语义,还是分开定义)
单链表的初始化
-
即构造一个空表
算法思路:
-
生成新结点作头结点,用头指针L指向头结点。
-
将头结点的指针域置空。
算法描述:
Status InitList_L(LinkList &L) {
L = new LNode; //c语言描述为 L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
return 0;
}
判断链表是否为空
空表:链表中无元素,头结点指针域指向NULL(头指针和头结点仍然在)
int ListEmpty(LinkList L) { //若L为空表,则返回1,否则返回0
if(L->next) //非空
return 0;
else
return 1;
}
单链表的销毁
算法思路:
-
从头指针开始,依次释放所有结点。
算法描述:
Status DestroyList_L(LinkList &L) {
Lnode *p; //LinkList p;
while(L) {
p = L;
L = L->next;
delete p; //c语言描述:free(p);
}
}
单链表的清空
-
链表仍然存在,但链表中无元素,成为空链表(头指针和头结点仍然存在)
算法思路:
-
依次释放所有结点,并将头结点指针域设置为空。
算法描述:
Status ClearList(LinkList &L) {
LNode *p, *q;
p = L->next;
while(p) {
q = p->next;
delete p;
p = q;
}
L->next = NULL;
return OK;
}
单链表的表长
算法思路:
-
从首元结点开始,依次计数所有结点
算法描述:
int ListLength_L(LinkList L) {
LinkList p;
p = L->next; //p指向第一个结点
i = 0;
while(p) { //遍历单链表计数
i++;
p = p->next;
}
return i;
}
取单链表中第i个元素
算法思路:
-
从链表头指针出发,顺着链域next逐个结点往下搜索,直到搜索到第i个结点为止。(所以链表不是随机存取结构)
-
从第一个结点(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) { //通过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;
}
单链表的查找
算法思路:
-
从第一个结点起,依次和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; }
-
返回位置序号
int LocateElem_L(LinkList L, Elemtype e) { p = L->next;j = 1; while(p && p->data != e) { p = p->next;j++; } if(p) return j; else return 0; //查找失败 }
单链表的插入
算法思路:
-
首先找到插入位置前一个结点的位置p。
-
生成一个数据域为e的新结点s。
-
插入新结点。
算法描述:
Status ListInsert_L(LinkList &L, int i, ElemType e) {
p = L;j = 0;
//寻找第i-1个结点,p指向i-1结点
while(p && j<i-1) {
p = p->next;
++j;
}
if(!p || j>i-1) return ERROR; //i大于表长+1或者小于1,插入位置非法
s = new LNode; //生成新结点s,将结点s的数据域设置为e
s->data = e;
s->next = p->next; //将结点s插入L中
p->next = s;
return OK;
}
删除单链表中第i个值
算法思路:
-
首先找到删除位置前一个结点的位置p,保存要删除的a的值。(也可以不保存)
-
令p->next指向要删除位置的后一个结点。
-
删除
算法描述:
Status ListDelete_L(LinkList &L, int i, ElemType &e) {
p = L; j = 0;
while(p->next && j<i-1) { //寻找第i个结点,并令p指向其前驱
p=p->next; ++j;
}
if(!(p->next) && j>i-1) return ERROR;//删除位置不合理
q = p->next; //临时保存被删结点的地址以备释放
p->next = q->next; //改变删除结点的前驱指针域
e = q->data; //保存删除结点的数据域,也可以不保存
delete q; //释放
return OK;
}
建立单链表
头插法
-
元素插入在链表头部,也叫前插法。
-
时间复杂度 O(n).
算法描述:
//倒位序输入n个值,建立带表头结点的单链表L
void CreateList_H(LinkList &L, int n) {
L = new LNode; //c:(LinkList)malloc(sizeof(LinkList));
L->next = NULL; //线建立一个带头结点的单链表
for(i=n;i>0;i++) {
p = new LNode; //生成新节点
cin>>p->data; //输入元素值 c:scanf(&p->data);
p->next = L->next; //插入到表头
L->next = p;
}
}
尾插法
-
元素插入在链表尾部,也叫后插法。
-
时间复杂度 O(n)。
算法描述:
//正位序输入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指向新的尾结点
}
}