3.6.1 线性表链式存储结构定义
目录
定义:n个结点链结成一个链表,即为线性表()的链式存储结构。
数据域:存储数据元素信息; 指针域:存储直接后继位置的地址。
结点:数据元素的存储映像,由数据域和指针域两部分组成。
链表:n个结点由指针链成的一个链表。
头指针、头结点和首元结点:
头指针:是指向链表中的第一个结点的指针
首元结点:是指链表中存储第一个数据元素 的结点。
头结点:是在链表的首元结点之前附设的一个结点。
空表:无头结点时,头指针为空时为空表;有头结点时,头结点的指针域为空时为空表。
3.6.2 单链表的存储结构
单链表:
带有头结点的单链表:
空链表:
单链表的存储结构:
3.6.3 单链表基本操作的实现
(一)单链表的初始化
Status lnitList_L(LinkList& L)
{
L = (Lnode*)malloc(sizeof(Lnode));
//两种初始化是一样的,都是指针类型
//L = (LinkList)malloc(sizeof(Lnode));
L->next = NULL;
return OK;
}
(二)判断链表是否为空
算法思路:判断头结点指针域是否为空
int ListEmpty(LinkList L)
{
if (L->next)
{
return 0;
}
else return 1;
}
(三)销毁单链表
算法思路:从头指针开始,依次释放所有结点
Status DestroyList_L(LinkList &L)
{
LinkList p;
//Lnode *p;
while (L)
{
p = L;//从头结点开始,L存放着头结点的地址
L = L->next;//指向下一个结点
free(p);//删除
}
return OK;
}
(四)清空单链表
算法思路:依次释放所有的结点,并将头指针域设置为空
Status ClearList(LinkList &L)
{
Lnode* p, * q;//或者LinkList p, q;
p = L->next;//头结点的next域存放着首元结点的地址
while (p)
{
q = p->next;//q存放了要删除元素的下一个结点的地址
free(p);
p = q;
}
L->next = NULL;//头结点指针域为空
return OK;
}
(五)求单链表的表长
算法思路:从首元结点开始,依次计数所有结点
Status ListLength_L(LinkList L)
{
Lnode* p;//LinkList p
p = L->next;//p指向第一个结点
int count = 0;
while (p)//遍历单链表,统计结点数
{
count++;
p = p->next;
}
return count;
}
(六)取单链表中的第i个元素的内容
算法步骤:
1.从第1个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p=L->next。
2.j做计数器,累计当前扫描过的结点数,j初值为1;
3.当p指向扫描到的下一个结点时,计数器j加1;
4.当j==i时,p所指向的结点就是要找的第i个结点。
Status GetElem_L(LinkList L, int i, ElemType &e)//由变量e返回
{
Lnode* p;
int j = 0;
p = L->next; j = 1;//初始化
while (p && j < i)//向后扫描,直到p指向第i个元素或p为空
{
p = p->next;
++j;
}
if (!p || j > i)//第i个元素不存在
return ERROR;
e = p->data;// 取第i个元素
return OK;
}
(七)按值查找——根据指定数据获取该数据所在的位置(地址)
算法步骤:
1.从第一个结点起,依次和e相比较。
2.如果找到一个其值与e相等的数据元素,则返回其在链表中的“位置” 或地址;
3.如果查遍整个链表都没有找到其值和e相等的元素,则返回0或"NULL”
Lnode* LocateElem_L(LinkList L, ElemType e)
{
Lnode* p;//LinkList p
p = L->next;
while (p && p->data != e)
{
p = p->next;
}
return p;//返回地址
}
(八)按值查找--根据指定数据获取该数据位置序号
int LocateElem_L1(LinkList L, ElemType e)
{
Lnode* p; int j = 0;
p = L->next; j = 1;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p)
return j;
else
return 0;
}
(九)在L中第i个元素之前插入数据元素e
算法步骤:
Status Listlnsert_L(LinkList& L, int i, ElemType e)
{
Lnode* p;
Lnode* s;
int j = 0;
p = L;
j = 0;
while (p && j < i - 1)//寻找第i-1个结点,p指向i-1结点
{
p = p->next;
++j;
}
if (!p || j > i - 1)//i大于表长+1或小于1,插入的位置非法
return ERROR;
s = (Lnode*)malloc(sizeof(Lnode));//或s=new LNode,生成新节点s
s->data = e;//生成新结点s,将结点s的数据置为e
s->next = p->next;//将保存的下一个结点的地址赋给新结点
p->next = s;//把新结点的地址赋给前一个元素的指针域
}
(十)删除--删除第i个结点
算法步骤:
Status ListDelete_L(LinkList& L, int i, ElemType& e)
{
Lnode* p;
Lnode* q;
int 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 = p->next->next;//==q->next,指向被删除结点的下一个结点
e = q->data;//保存删除结点的数据域
free(q);//释放删除结点的空间
return OK;
}
单链表的查找、插入、删除算法时间效率分析:
1.查找:
- 因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为O(n)。
2.插入和删除:
- 因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为O(1)。
- 但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n)。
(十一)头插法
1.从一个空表开始,重复读入数据;
2.生成新结点,将读入数据存放到新结点的数据域中
3.从最后一个结点开始,依次将各结点插入到链表的前端
void CreateList_H(LinkList &L, int n)
{
Lnode* p;
L = (Lnode*)malloc(sizeof(Lnode));// L = new Lnode;
L->next = NULL;//先建立一个代头结点的单链表
int i = 0;
for (i = n; i > 0; --i)
{
p = (Lnode*)malloc(sizeof(Lnode));//生成一个新结点
scanf("%d", &p->data);//输入元素值,根据要元素的类型来
p->next = L->next;//插入到表头
L->next = p;
}
}
(十二)尾插法
void CreateList_R(LinkList& L, int n)
{
Lnode* p;
Lnode* r;
L = (Lnode*)malloc(sizeof(Lnode));// L = new Lnode;
L->next = NULL;//先建立一个代头结点的单链表
r = L;//尾指针r指向头结点
int i = 0;
for (i = 0; i < n; ++i)
{
p = (Lnode*)malloc(sizeof(Lnode));//生成一个新结点
scanf("%d", &p->data);//输入元素值,根据要元素的类型来
r->next = p;//插入到表尾
r = p;//r指向新的尾结点
}
}
3.6.3 全部代码
#include<stdio.h>
#include <malloc.h>
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;//ElemType取决于我们要存储元素的类型
typedef struct Lnode {
ElemType data;//结点的数据域
struct Lnode* next;//结点的指针域
}Lnode,*LinkList;//LinkList为指向结构体Lnode的指针类型
//定义链表L:LinkList L==Lnode* L;
//定义结点指针p:LNode *p==LinkList p;
//单链表的初始化
Status lnitList_L(LinkList &L)
{
L = (Lnode*)malloc(sizeof(Lnode));
//两种初始化是一样的,都是指针类型
L = (LinkList)malloc(sizeof(Lnode));
L->next = NULL;
return OK;
}
//判断链表是否为空
int ListEmpty(LinkList L)
{
if (L->next)
{
return 0;
}
else return 1;
}
//单链表的销毁,销毁后不存在
Status DestroyList_L(LinkList &L)
{
LinkList p;
//Lnode *p;
while (L)
{
p = L;//从头结点开始,L存放着头结点的地址
L = L->next;//指向下一个结点
free(p);//删除
}
return OK;
}
//清空单链表(头指针和头结点都存在)
Status ClearList(LinkList &L)
{
Lnode* p, * q;//或者LinkList p, q;
p = L->next;//头结点的next域存放着首元结点的地址
while (p)
{
q = p->next;//q存放了要删除元素的下一个结点的地址
free(p);
p = q;
}
L->next = NULL;//头结点指针域为空
return OK;
}
//求单链表的表长
Status ListLength_L(LinkList L)
{
Lnode* p;//LinkList p
p = L->next;//p指向第一个结点
int count = 0;
while (p)//遍历单链表,统计结点数
{
count++;
p = p->next;
}
return count;
}
//取单链表中第i个元素的内容
Status GetElem_L(LinkList L, int i, ElemType &e)//由变量e返回
{
Lnode* p;
int j = 0;
p = L->next; j = 1;//初始化
while (p && j < i)//向后扫描,直到p指向第i个元素或p为空
{
p = p->next;
++j;
}
if (!p || j > i)//第i个元素不存在
return ERROR;
e = p->data;// 取第i个元素
return OK;
}
//按值查找--根据指定数据获取该数据所在的位置(地址)
Lnode* LocateElem_L(LinkList L, ElemType e)
{
Lnode* p;//LinkList p
p = L->next;
while (p && p->data != e)
{
p = p->next;
}
return p;//返回地址
}
//按值查找--根据指定数据获取该数据位置序号
int LocateElem_L1(LinkList L, ElemType e)
{
Lnode* p; int j = 0;
p = L->next; j = 1;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p)
return j;
else
return 0;
}
//在L中第i个元素之前插入数据元素e
Status Listlnsert_L(LinkList& L, int i, ElemType e)
{
Lnode* p;
Lnode* s;
int j = 0;
p = L;
j = 0;
while (p && j < i - 1)//寻找第i-1个结点,p指向i-1结点
{
p = p->next;
++j;
}
if (!p || j > i - 1)//i大于表长+1或小于1,插入的位置非法
return ERROR;
s = (Lnode*)malloc(sizeof(Lnode));//或s=new LNode,生成新节点s
s->data = e;//生成新结点s,将结点s的数据置为e
s->next = p->next;//将保存的下一个结点的地址赋给新结点
p->next = s;//把新结点的地址赋给前一个元素的指针域
}
//删除--删除第i个结点
Status ListDelete_L(LinkList& L, int i, ElemType& e)
{
Lnode* p;
Lnode* q;
p = L->next;
int 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 = p->next->next;//==q->next,指向被删除结点的下一个结点
e = q->data;//保存删除结点的数据域
free(q);//释放删除结点的空间
return OK;
}
//打印单链表
void printLinkList(LinkList L)
{
Lnode* p;
p = L->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//头插法
void CreateList_H(LinkList& L, int n)
{
Lnode* p;
L = (Lnode*)malloc(sizeof(Lnode));// L = new Lnode;
L->next = NULL;//先建立一个代头结点的单链表
int i = 0;
for (i = n; i > 0; --i)
{
p = (Lnode*)malloc(sizeof(Lnode));//生成一个新结点
scanf("%d", &p->data);//输入元素值,根据要元素的类型来
p->next = L->next;//插入到表头
L->next = p;
}
}
//尾插法
void CreateList_R(LinkList& L, int n)
{
Lnode* p;
Lnode* r;
L = (Lnode*)malloc(sizeof(Lnode));// L = new Lnode;
L->next = NULL;//先建立一个代头结点的单链表
r = L;//尾指针r指向头结点
int i = 0;
for (i = 0; i < n; ++i)
{
p = (Lnode*)malloc(sizeof(Lnode));//生成一个新结点
scanf("%d", &p->data);//输入元素值,根据要元素的类型来
p->next = NULL;
r->next = p;//插入到表尾
r = p;//r指向新的尾结点
}
}
int main()
{
ElemType e;
LinkList L;
lnitList_L(L);
CreateList_R(L, 6);
printLinkList(L);
printf("%d\n", ListLength_L(L));
ListDelete_L(L, 2, e);
printf("e的值为 % d\n", e);
printf("%d\n", ListLength_L(L));
Listlnsert_L(L, 2, 10);
printLinkList(L);
}