系列文章目录
文章目录
目录
前言
本文仅简单介绍单链表这种数据结构
以下是本篇文章正文内容,下面案例可供参考
一、顺序结构的链式表示——单链表
1.链表的相关介绍
1.1链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,而链表的结构其实就像一辆火车一样。
在链表(SListl)当中,每个节点(SListNode)都存储着一个数据(data),还有一个指向下个节点的指针(next)
1.2、单链表
单链表用结点存储了数据以及下一个结点的地址,因此结点一般分为多个部分,即数据域与指针域,数据域存储有效数据,指针域存储下一个结点的地址。同时单链表有只有一个指针域,双链表有两个指针域。同时其最后一个节点是指向一个空指针。
- 离散的数据分布在存储空间内
- 非随机存取
- 头指针表示单链表【指向链表的第一个节点】
- 头结点——带头结点的链表中的第一个结点,且结点不存储信息
1.3、单链表的优缺点
- 1.优点:可以按照实际所需创建结点增减链表的长度,更大程度地使用内存。
- 2.缺点:进行尾部或者任意位置上插入或删除时时间复杂度和空间复杂度较大,每次都需要通过指针的移动找到所需要的位置,相对于顺序表查找而言效率较低。
1.4、补充:顺序表的优缺点
- 1.优点:可以通过下标直接访问所需要的数据
- 2.缺点:不能按实际所需分配内存,只能使用malloc或者realloc函数进行扩容,容易实现频繁扩容,容易导致内存浪费与数据泄露等问题。
二、单链表的一些基本操作与结构定义
1、单链表的实现
下面我们实现的单链表是很多数据结构的子结构,也就是单向不带头非循环链表。
2、单链表的结构定义
单链表的结构与顺序表是完全不同的,分为俩个部分数据域和指针域
typedef struct SListNode
{
int data;
struct SListNode* next;//存储下一个节点的地址
}SListNode;
我们也可以使用typedef操作符重命名,方便后面接口操作。
3、单链表结点的创建
单链表的创建,不用像顺序表一样需要初始化的,是由一个个节点组成,需要多少个结点就创建多少个结点,我们可以单独写出一个测试函数测试单链表的创建,方便调试,下面我们利用上面这个结构创建一个单链表。
4、单链表的基本操作接口
typedef int SLTDataType;//统一数据类型
SLTDataType* BuySListNode(SLTDataType x);//申请结点
void SListPrint(SListNode* phead);//打印链表
void SListPushBack(SListNode** pphead, SLTDataType x);//尾插
void SListPushFront(SListNode** pphead, SLTDataType x);//头插
void SListPopBack(SListNode** pphead);//尾删
void SListPopFront(SListNode** pphead);//头删
SLTDataType *SListFind(SListNode* pphead, SLTDataType x);//查找
//在pos位置之前插入(pos第几个元素的地址)
//和顺序表的下标越界不同,链表不存在下标,pos地址必须为find函数找到
void SListInsert(SListNode** pphead, SListNode*pos, SLTDataType x);
//在pos位置之后插入
void SListInsertAfter(SListNode* pos, SLTDataType x);
//删除pos位置
void SListErase(SListNode** pphead, SListNode* pos);
//删除pos下一位的位置
void SListEraseAfter( SListNode* pos);
void SListDestroy(SListNode** pphead);//摧毁链表
5、主要的几种操作介绍
5.1、头插法建立单链表
#include<stdio.h>
#include<stdlib.h>
//单链表的结构定义
typedef struct LNode
{
int data; //data存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//头插法建立单链表
void createlistF(LNode *&L, int a[], int n)
{
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));
L ->next = NULL;
for(i=0;i<n;++i)
{
s = (LNode*)malloc(sizeof(LNode));
s ->data = a[i];
//下面两句为头插法的关键步骤
s ->next = L ->next; //s所指新结点的指针域next指向L中的开始结点
L ->next = s; //头结点的指针域next指向s结点,使得s成为新的开始结点
}
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0;
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0)
{
printf("\n");
}
}
printf("\n");
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
//测试头插法建立链表
createlistF(L,a,n);
//查看建立后的链表
printfList(L);
return 0;
}
5.2尾插法建立单链表
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode
{
int data; //data中存放结点的数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//尾插法建立链表
void createlistR(LNode *&L, int a[], int n) //要改变的变量用引用型
{
LNode *s,*r; //s用来指向新申请的结点,r之中指向L的终端结点
int i;
L = (LNode *)malloc(sizeof(LNode)); //申请L的头结点空间
L -> next = NULL;
r = L; // r指向头结点,因为此时头结点就是终端结点
for(i=0;i<n;++i) // 循环申请n个结点,来接受数组a中的元素
{
s = (LNode*)malloc(sizeof(LNode)); // s指新申请的结点
s ->data = a[i];// 用新申请的结点来接受a中的一个元素
r ->next = s; // 用来接纳新结点
r = r ->next; // r指向终端结点,以便于接纳下一个到来的结点
}
r ->next = NULL; // 数组a中所有的元素都已经装入链表L中,L的终端结点的指针域置为NULL,L建立完成
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0; //计数器
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0) //每5个元素作为一行
{
printf("\n");
}
}
printf("\n");
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
//测试尾插法建立链表
createlistR(L,a,n);
//查看建立后的链表
printfList(L);
return 0;
}
5.3、查找结点
#include<stdio.h>
#include<stdlib.h>
//单链表的结构定义
typedef struct LNode
{
int data; //data存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//头插法建立单链表
void createlistF(LNode *&L, int a[], int n)
{
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));
L ->next = NULL;
for(i=0;i<n;++i)
{
s = (LNode*)malloc(sizeof(LNode));
s ->data = a[i];
s ->next = L ->next;
L ->next = s;
}
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0; //计数器
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0) //每5个元素作为一行
{
printf("\n");
}
}
printf("\n");
}
//在链表L中查找与e相等的元素,找到的话返回其位置, 否则返回未找到。
int searchElem(LNode *L, int e)
{
LNode *temp = L;
int i = 1;
int p = 0;
while(temp ->next)
{
temp = temp ->next;
if(e == temp->data)
{
p = i;
printf("找到了与%d相等元素位置为%d\n",e,p);
return 1;
}
i++;
}
printf("抱歉,没有找到!");
return -1;
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
createlistF(L,a,n);
printfList(L);
//测试查找结点
int e;
printf("请输入要查找的结点:");
scanf("%d",&e);
searchElem(L,e);
return 0;
}
5.4、修改结点
#include<stdio.h>
#include<stdlib.h>
//单链表的结构定义
typedef struct LNode
{
int data; //data存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//头插法建立单链表
void createlistF(LNode *&L, int a[], int n)
{
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));
L ->next = NULL;
for(i=0;i<n;++i)
{
s = (LNode*)malloc(sizeof(LNode));
s ->data = a[i];
s ->next = L ->next;
L ->next = s;
}
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0; //计数器
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0) //每5个元素作为一行
{
printf("\n");
}
}
printf("\n");
}
// 修改单链表L的第p个位置上的结点
int replace(LNode *L, int p, int e)
{
LNode *temp = L;
temp = temp ->next;
for(int i=1;i<p;++i)
{
temp = temp ->next;
}
temp ->data = e;
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
createlistF(L,a,n);
printfList(L);
//测试修改结点
int p,e;
printf("请输入要修改的位置和更改后的元素(用空格分开):\n");
scanf("%d %d",&p,&e);
replace(L,p,e);
//修改后打印链表数据
printfList(L);
return 0;
}
5.5、插入结点
#include<stdio.h>
#include<stdlib.h>
//单链表的结构定义
typedef struct LNode
{
int data; //data存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//头插法建立单链表
void createlistF(LNode *&L, int a[], int n)
{
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));
L ->next = NULL;
for(i=0;i<n;++i)
{
s = (LNode*)malloc(sizeof(LNode));
s ->data = a[i];
s ->next = L ->next;
L ->next = s;
}
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0; //计数器
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0) //每5个元素作为一行
{
printf("\n");
}
}
printf("\n");
}
//在链表L的第p个位置上插入元素e
void insertElem(LNode *L, int p, int e)
{
LNode *temp = L;
int i = 0;
while(i<p-1)
{
temp = temp ->next;
++i;
}
LNode *s = (LNode*)malloc(sizeof(LNode)); //创建新结点
s ->data = e;
s->next = temp ->next;
temp ->next = s;
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
createlistF(L,a,n);
printfList(L);
//测试插入结点
int p,e;
printf("请输入要插入的位置和要插入的元素(用空格分开):\n");
scanf("%d %d",&p,&e);
insertElem(L,p,e);
//插入后打印链表数据
printfList(L);
return 0;
}
5.6、删除结点
#include<stdio.h>
#include<stdlib.h>
//单链表的结构定义
typedef struct LNode
{
int data; //data存放结点数据域
struct LNode *next; //指向后继结点的指针
}LNode; //定义单链表结点类型
//头插法建立单链表
void createlistF(LNode *&L, int a[], int n)
{
LNode *s;
int i;
L = (LNode*)malloc(sizeof(LNode));
L ->next = NULL;
for(i=0;i<n;++i)
{
s = (LNode*)malloc(sizeof(LNode));
s ->data = a[i];
s ->next = L ->next;
L ->next = s;
}
}
//打印链表数据
void printfList(LNode *L)
{
LNode *temp = L;
int count = 0; //计数器
printf("表中的元素为:\n");
while(temp->next)
{
temp = temp->next;
printf("%d\t",temp->data);
count++;
if(count%5==0) //每5个元素作为一行
{
printf("\n");
}
}
printf("\n");
}
//删除单链表L中第p位置上的结点
void deleteElem(LNode *L, int p, int *e)
{
LNode *temp = L;
int i = 0;
//找到删除结点的上一结点,即第p-1个结点
while(i<p-1)
{
temp = temp ->next;
++i;
}
LNode *q = temp->next; //定义一个q指针指向被删除结点
*e = q->data; //保存被删除的结点的数据域
temp->next = q->next; //删除结点的上一个结点的指针域指向删除结点的下一个结点
free(q); //释放q所指结点的内存空间
}
int main()
{
LNode *L;
int n;
printf("请输入数组的个数:");
scanf("%d",&n);
int a[n];
printf("请输入数组中的数(用空格分开):\n");
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
createlistF(L,a,n);
printfList(L);
//测试删除结点
int p,e;
e = NULL;
printf("请输入要删除结点的位置:\n");
scanf("%d",&p);
deleteElem(L,p,&e);
//插入后打印链表数据
printfList(L);
return 0;
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了单链表的一些基本操作与创建方法,以及相关的特点,希望对你们有所帮助,也欢迎大佬的指正。