一、单链表的基本概念
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
图示
二、单链表的定义和初始化
1.单链表的定义
//定义单链表
typedef struct Linknode
{
Book data;
struct Linknode* next; //指向结点类型的指针
}LinkList,*LNode;
注:
struct Linknode* next;为指向该结点类型的指针
其单链表的数据类型为Book型,定义如下
//定义数据类型
typedef struct
{
char id[N];
char name[N];
double peice;
}Book;
2、单链表的初始化
//初始化单链表
LNode InitList()
{
LNode L; //创建一个结点指针
L = (LNode)malloc(sizeof(LinkList)); //动态分配一个内存空间
L->next = NULL; //将头结点的指针域设为空
return L; //返回该指针
}
单链表的初始化既构造一个仅包含头结点的空单链表。其过程是首先申请一个结点并让指针指向该结点,然后将指针域设为空,最后放回指针。
图示
三、单链表的建立
1.头插法
//头插法
void CreateList(LNode L, int n)
{
LNode p;
int i = 0;
for (i = 0; i < n; i++) //循环输入数据
{
printf("请输入第%d个数据:", i + 1);
p = (LNode)malloc(sizeof(LinkList)); //分配内存空间
scanf("%s %s %lf", &p->data.id,&p->data.name,&p->data.peice);
p->next = L->next; //连接结点
L->next = p;
}
printf("建立成功!\n");
}
单链表与顺序表不同,要事先分配内存空间,再将结点连接起来。
图示
头插法每一次都是在头结点后插入,若插入的顺序为1234,则实际单链表上的顺序为4321
2.尾插法
//尾插法
void CreateList2(LNode L, int n)
{
LNode p;
int i = 0;
for (i = 0; i < n; i++)
{
printf("请输入第%d个数据:", i + 1);
p = (LNode)malloc(sizeof(LinkList));
scanf("%s %s %lf", &p->data.id, &p->data.name, &p->data.peice);
p->next = NULL; //将结点的指针域置为空
L->next = p; //将结点接在后面
L = p; //指针后移,保证在最后一位
}
printf("建立成功!\n");
}
图示
头插法每一次都是在最后一个结点后插入,若插入的顺序为1234,则实际单链表上的顺序还是1234
四、单链表的输出
//输出单链表中的数据
void printfList(LNode L)
{
LNode p;
p = L->next;
while (p != NULL)
{
printf("id:%s name:%s peice:%lf\n", p->data.id, p->data.name, p->data.peice);
p = p->next;
}
}
先将一个指针指向单链表的首元结点,判断是否为空,是则输出,再将指针后移,直到将链表全部输出。
五、顺序表的插入
//插入数据
int InputData(LNode L)
{
LNode p;
LNode s;
p = L;
Book x;
int i = 0;
int j = 0;
printf("input data");
scanf("%s %s %lf", &x.id, &x.name, &x.peice);
printf("input i");
scanf("%d", &i);
if (i<0 || i>LengthList(L) + 1)
{
printf("插入位置错误\n");
return 0;
}
for (j = 1; j < i; j++)
{
p = p->next;
}
if (j == i)
{
s = (LNode)malloc(sizeof(LinkList));
s->data = x;
s->next = p->next;
p->next = s;
printf("插入成功\n");
return 1;
}
printf("插入失败\n");
return 0;
}
顺序表的插入的方法是建立一个结点,然后输入插入的位置,找到插入位置的前一个结点之后将结点插入
图示
六、单链表的删除
//删除
int DelList(LNode L)
{
LNode p;
LNode s;
p = L;
int i=0;
int j = 0;
printf("input i");
scanf("%d", &i);
if (i<1 || i>LengthList(L))
{
printf("删除位置错误\n");
return 0;
}
for (j = 1; j < i; j++)
{
p = p->next;
}
if (i == j)
{
s = p->next;
p->next = s->next;
free(s);
printf("删除成功\n");
return 1;
}
printf("删除失败\n");
return 0;
}
单链表的删除也是要找到删除位置的前一个结点,再进行删除操作即可
七、单链表的查找
1.按位置求值
//按位置求值
int GetData1(LNode L,Book *x)
{
int i=0;
int j = 0;
LNode p;
p = L;
printf("input i:");
scanf("%d", &i);
if (i<1 ||i>LengthList(L))
{
printf("输入位置不合法!\n");
return 0;
}
for (j = 0; j < i; j++)
{
p = p->next;
}
*x = p->data;
return 1;
}
先找到该位置,再用*x接收数据即可
2.按值求位置
//按值找位置
int GetData2(LNode L)
{
Book x;
LNode p;
int i = 1;
p = L->next;
printf("input data");
scanf("%s %s %lf", &x.id,&x.name,&x.peice);
while (p!= NULL)
{
if (strcmp(x.id, p->data.id) == 0 && strcmp(x.name, p->data.name) == 0 && x.peice == p->data.peice)
{
return i;
}
p = p->next;
i++;
}
printf("没有该数据\n");
return 0;
}
从首元结点开始遍历比对,若遍历到空结点还没找到就是没有该数据
八、代码总览
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<malloc.h>
#include<string.h>
#define N 20
//定义数据类型
typedef struct
{
char id[N];
char name[N];
double peice;
}Book;
//定义单链表
typedef struct Linknode
{
Book data;
struct Linknode* next; //指向结点类型的指针
}LinkList,*LNode;
//初始化单链表
LNode InitList()
{
LNode L; //创建一个结点指针
L = (LNode)malloc(sizeof(LinkList)); //动态分配一个内存空间
L->next = NULL; //将头结点的指针域设为空
return L; //返回该指针
}
//头插法
void CreateList(LNode L, int n)
{
LNode p;
int i = 0;
for (i = 0; i < n; i++) //循环输入数据
{
printf("请输入第%d个数据:", i + 1);
p = (LNode)malloc(sizeof(LinkList)); //分配内存空间
scanf("%s %s %lf", &p->data.id,&p->data.name,&p->data.peice);
p->next = L->next; //连接结点
L->next = p;
}
printf("建立成功!\n");
}
//尾插法
void CreateList2(LNode L, int n)
{
LNode p;
int i = 0;
for (i = 0; i < n; i++)
{
printf("请输入第%d个数据:", i + 1);
p = (LNode)malloc(sizeof(LinkList));
scanf("%s %s %lf", &p->data.id, &p->data.name, &p->data.peice);
p->next = NULL; //将结点的指针域置为空
L->next = p; //将结点接在后面
L = p; //指针后移,保证在最后一位
}
printf("建立成功!\n");
}
//输出单链表中的数据
void printfList(LNode L)
{
LNode p;
p = L->next;
while (p != NULL)
{
printf("id:%s name:%s peice:%lf\n", p->data.id, p->data.name, p->data.peice);
p = p->next;
}
}
//求表长
int LengthList(LNode L)
{
LNode p;
p = L->next;
int i = 0;
while (p != NULL)
{
i++;
p = p->next;
}
return i;
}
//按位置求值
int GetData1(LNode L,Book *x)
{
int i=0;
int j = 0;
LNode p;
p = L;
printf("input i:");
scanf("%d", &i);
if (i<1 ||i>LengthList(L))
{
printf("输入位置不合法!\n");
return 0;
}
for (j = 0; j < i; j++)
{
p = p->next;
}
*x = p->data;
return 1;
}
//按值找位置
int GetData2(LNode L)
{
Book x;
LNode p;
int i = 1;
p = L->next;
printf("input data");
scanf("%s %s %lf", &x.id,&x.name,&x.peice);
while (p!= NULL)
{
if (strcmp(x.id, p->data.id) == 0 && strcmp(x.name, p->data.name) == 0 && x.peice == p->data.peice)
{
return i;
}
p = p->next;
i++;
}
printf("没有该数据\n");
return 0;
}
//插入数据
int InputData(LNode L)
{
LNode p;
LNode s;
p = L;
Book x;
int i = 0;
int j = 0;
printf("input data");
scanf("%s %s %lf", &x.id, &x.name, &x.peice);
printf("input i");
scanf("%d", &i);
if (i<0 || i>LengthList(L) + 1)
{
printf("插入位置错误\n");
return 0;
}
for (j = 1; j < i; j++)
{
p = p->next;
}
if (j == i)
{
s = (LNode)malloc(sizeof(LinkList));
s->data = x;
s->next = p->next;
p->next = s;
printf("插入成功\n");
return 1;
}
printf("插入失败\n");
return 0;
}//删除
int DelList(LNode L)
{
LNode p;
LNode s;
p = L;
int i=0;
int j = 0;
printf("input i");
scanf("%d", &i);
if (i<1 || i>LengthList(L))
{
printf("删除位置错误\n");
return 0;
}
for (j = 1; j < i; j++)
{
p = p->next;
}
if (i == j)
{
s = p->next;
p->next = s->next;
free(s);
printf("删除成功\n");
return 1;
}
printf("删除失败\n");
return 0;
}
int main()
{
LNode L;
Book x;
L = InitList();
int n;
int leng = 0;
int i=0;
printf("input n");
scanf("%d", &n);
//CreateList(L, n);
CreateList2(L, n);
printfList(L);
leng=LengthList(L);
printf("leng=%d\n", leng);
if(GetData1(L, &x))
printf("%s %s %lf\n", x.id,x.name,x.peice);
if (i = GetData2(L))
{
printf("位置为:%d\n", i);
}
InputData(L);
printfList(L);
DelList(L);
printfList(L);
return 0;
}
九、总结
单链表采用动态分配内存空间,不会出现内存闲置或溢出的情况,采用顺序存取,存取时间复杂度为o(n),但插入和删除只用进行一步操作,时间复杂度为n(1)。
适用情况:1.长度变化较大
2.频繁进行插入或删除操作