文章目录
线性表的链式存储结构(单链表)
这里是数据结构个人学习的笔记记录,如有问题欢迎指正说明
单链表的定义
n个结点链结成一个链表。每个结点由数据域和指针域两部分组成,在此处我们之研究结点只含一个指针域的链表,即单链表。
我们把链表中的第一个结点的存储位置叫做头指针,整个链表的存取必须从头指针开始。为了方便操作,我们还可以在单链表的第一个结点前附设一个结点,称为头结点。注意头结点不是必不可少的。头结点和头指针的区别如下图:
单链表的相关操作实现
基础宏定义
使用这些宏定义能增强代码的阅读性和通用性
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20
typedef int Status;/*函数结果状态代码*/
typedef int ElemType;/*待存储数据的数据类型*/
基本结构体
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef Node *LinkList;
初始化
/* 初始化 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node));
if(!(*L))
return ERROR;
(*L)->next=NULL;/*指针域为空*/
return OK;
}
依次对单链表中的每个数据元素输出
/* 依次对L的每个数据元素输出 */
Status ListTraverse(LinkList L)
{
LinkList p=L->next;
while(p)
{
visit(p->data);//vist函数为输出值
p=p->next;
}
printf("\n");
return OK;
}
返回单链表中元素的个数
/*返回L中数据元素个数*/
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
p=p->next;
}
return i;
}
在单链表中指定位置之前插入新的数据元素
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j=1;
LinkList p,s;
p=*L;
while(p&&j<i)
{
p=p->next;
++j;
}
if(!p||j>i)
return ERROR;
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
删除线性表的指定位置的数据元素
/* 删除线性表的指定位置的数据元素 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j=1;
LinkList p,q;
p=*L;
while(p->next&&j<i)
{
p=p->next;
++j;
}
if(!(p->next)||j>i)
return ERROR;
q=p->next;
p->next=q->next;
*e=q->data;
free(q);
return OK;
}
返回单链表中第一个与指定数据相符的数据的位序
/* 返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(LinkList L,ElemType e)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
if(p->data==e)
return i;
p=p->next;
}
return 0;
}
返回指定位置的值
/* 用e返回L中第i个数据元素的值 */
Status GetElem(LinkList L,int i,ElemType *e)
{
int j=1;
LinkList p;
p=L->next;
while(p&&j<i)
{
p=p->next;
++j;
}
if(!p||j>i)
return ERROR;
*e=p->data;
return OK;
}
将单链表重置为空表
/* 将L重置为空表 */
Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return OK;
}
头插的实现
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
void CreateListHead(LinkList *L,int n)
{
LinkList p;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
(*L)->next=NULL;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
p->next=(*L)->next;
(*L)->next=p;
}
}
尾插的实现
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
r=*L;
for (i=0; i<n; i++)
{
p = (Node *)malloc(sizeof(Node));
p->data = rand()%100+1;
r->next=p;
r = p;
}
r->next = NULL;
}
链式存储的代码实现
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20
typedef int Status;
typedef int ElemType;
typedef struct Node
{
ElemType data;
struct Node *next;
}Node;
typedef Node *LinkList;
Status visit(ElemType c)
{
printf("%d ",c);
return OK;
}
/* 初始化链式线性表 */
Status InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node));
if(!(*L))
return ERROR;
(*L)->next=NULL;
return OK;
}
/* 初始条件:链式线性表L已存在。操作结果:将L重置为空表 */
Status ClearList(LinkList *L)
{
LinkList p,q;
p=(*L)->next;
while(p)
{
q=p->next;
free(p);
p=q;
}
(*L)->next=NULL;
return OK;
}
/* 初始条件:链式线性表L已存在。操作结果:返回L中数据元素个数 */
int ListLength(LinkList L)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
p=p->next;
}
return i;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值 */
Status GetElem(LinkList L,int i,ElemType *e)
{
int j=1;
LinkList p;
p=L->next;
while(p&&j<i)
{
p=p->next;
++j;
}
if(!p||j>i)
return ERROR;
*e=p->data;
return OK;
}
/* 初始条件:链式线性表L已存在 */
/* 操作结果:返回L中第1个与e满足关系的数据元素的位序。 */
/* 若这样的数据元素不存在,则返回值为0 */
int LocateElem(LinkList L,ElemType e)
{
int i=0;
LinkList p=L->next;
while(p)
{
i++;
if(p->data==e)
return i;
p=p->next;
}
return 0;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
Status ListInsert(LinkList *L,int i,ElemType e)
{
int j=1;
LinkList p,s;
p=*L;
while(p&&j<i)
{
p=p->next;
++j;
}
if(!p||j>i)
return ERROR;
s=(LinkList)malloc(sizeof(Node));
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
/* 初始条件:链式线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(LinkList *L,int i,ElemType *e)
{
int j=1;
LinkList p,q;
p=*L;
while(p->next&&j<i)
{
p=p->next;
++j;
}
if(!(p->next)||j>i)
return ERROR;
q=p->next;
p->next=q->next;
*e=q->data;
free(q);
return OK;
}
/* 初始条件:链式线性表L已存在 */
/* 操作结果:依次对L的每个数据元素输出 */
Status ListTraverse(LinkList L)
{
LinkList p=L->next;
while(p)
{
visit(p->data);
p=p->next;
}
printf("\n");
return OK;
}
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(头插法) */
void CreateListHead(LinkList *L,int n)
{
LinkList p;
int i;
srand(time(0));
*L=(LinkList)malloc(sizeof(Node));
(*L)->next=NULL;
for(i=0;i<n;i++)
{
p=(LinkList)malloc(sizeof(Node));
p->data=rand()%100+1;
p->next=(*L)->next;
(*L)->next=p;
}
}
/* 随机产生n个元素的值,建立带表头结点的单链线性表L(尾插法) */
void CreateListTail(LinkList *L, int n)
{
LinkList p,r;
int i;
srand(time(0));
*L = (LinkList)malloc(sizeof(Node));
r=*L;
for (i=0; i<n; i++)
{
p = (Node *)malloc(sizeof(Node));
p->data = rand()%100+1;
r->next=p;
r = p;
}
r->next = NULL;
}
void ListTest()
{
LinkList L;
ElemType e;
Status i;
int j,k;
i=InitList(&L);
printf("初始化L后:ListLength(L)=%d\n",ListLength(L));
for(j=1;j<=5;j++)
i=ListInsert(&L,1,j);
printf("在L的表头依次插入1~5后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n",ListLength(L));
i=ClearList(&L);
printf("清空L后:ListLength(L)=%d\n",ListLength(L));
for(j=1;j<=10;j++)
ListInsert(&L,j,j);
printf("在L的表尾依次插入1:10后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n",ListLength(L));
ListInsert(&L,1,0);
printf("在L的表头插入0后:L.data=");
ListTraverse(L);
printf("ListLength(L)=%d \n",ListLength(L));
GetElem(L,5,&e);
printf("第5个元素的值为:%d\n",e);
for(j=3;j<=4;j++)
{
k=LocateElem(L,j);
if(k)
printf("第%d个元素的值为%d\n",k,j);
else
printf("没有值为%d的元素\n",j);
}
k=ListLength(L); /* k为表长 */
for(j=k+1;j>=k;j--)
{
i=ListDelete(&L,j,&e); /* 删除第j个数据 */
if(i==ERROR)
printf("删除第%d个数据失败\n",j);
else
printf("删除第%d个的元素值为:%d\n",j,e);
}
printf("依次输出L的元素:");
ListTraverse(L);
j=5;
ListDelete(&L,j,&e); /* 删除第5个数据 */
printf("删除第%d个的元素值为:%d\n",j,e);
printf("依次输出L的元素:");
ListTraverse(L);
i=ClearList(&L);
printf("\n清空L后:ListLength(L)=%d\n",ListLength(L));
CreateListHead(&L,20);
printf("整体创建L的元素(头插法):");
ListTraverse(L);
i=ClearList(&L);
printf("\n删除L后:ListLength(L)=%d\n",ListLength(L));
CreateListTail(&L,20);
printf("整体创建L的元素(尾插法):");
ListTraverse(L);
}
int main()
{
ListTest();
return 0;
}
样例测试输出
初始化L后:ListLength(L)=0
在L的表头依次插入1~5后:L.data=5 4 3 2 1
ListLength(L)=5
清空L后:ListLength(L)=0
在L的表尾依次插入1:10后:L.data=1 2 3 4 5 6 7 8 9 10
ListLength(L)=10
在L的表头插入0后:L.data=0 1 2 3 4 5 6 7 8 9 10
ListLength(L)=11
第5个元素的值为:4
第4个元素的值为3
第5个元素的值为4
删除第12个数据失败
删除第11个的元素值为:10
依次输出L的元素:0 1 2 3 4 5 6 7 8 9
删除第5个的元素值为:4
依次输出L的元素:0 1 2 3 5 6 7 8 9
清空L后:ListLength(L)=0
整体创建L的元素(头插法):87 50 42 93 84 66 26 28 64 76 46 68 59 87 22 2 25 39 66 33
删除L后:ListLength(L)=0
整体创建L的元素(尾插法):33 66 39 25 2 22 87 59 68 46 76 64 28 26 66 84 93 42 50 87
链式存储的优缺点
优点:存储空间可自由扩充
插入、删除等操作不必移动数据,修改效率较高
缺点:读写效率不高,必须采用顺序读取