线性表
线性表
知识框架
No.1
线性表定义
- 线性表(List):零个或多个数据元素的有限序列。
- 线性表的数据集合为
{a1,a2,…,an}
,假设每个元素的类型均为DataType
。其中,除第一个元素a1
外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an
外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
- 线性表中的数据元素的类型可以为简单类型或复杂类型。
- 同一线性表中的元素必定具有相同特性,数据元素间的关系是线性关系。
- 在较复杂的线性表中,一个数据元素可以由若干个数据项组成。在这种情况下,常把数据元素称为记录,含有大量记录的线性表又称为文件
No.2
线性表顺序存储结构
一、顺序表
1.顺序表基本概念
-
概念:用一组地址连续的存储单元依次存储线性表的数据元素,这种存储结构的线性表称为顺序表。
-
把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构上。(简而言之,逻辑上相邻,物理上也相邻)
-
顺序表又称为顺序映像。
-
顺序表占用一片连续的存储空间,若知道某个元素的存储位置便可以计算其它元素的存储位置。
-
地址连续——中间没有空出存储单元。(是一个顺序表)
-
地址不连续——中间存在空的存储单元。(不是一个顺序表)
-
顺序表中元素存储位置的计算,每个元素需占L个存储单元:
-
LOC(a i) = LOC(a 1) + (i - 1) * L
-
LOC(a i + 1) = LOC(a i) + L
-
-
逻辑位序和物理位序相差1。
-
顺序表存取元素的方式称为随机存取法。
2.顺序表存储结构
//头文件
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 20 //线性表存储空间的初始分配量
#define OK 1 //成功标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
// 定义顺序表
// 数组形式
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
typedef struct {
ElemType elem[MAXSIZE]; //存储数据
int length; //当前元素个数
}SqList;
// 指针形式
typedef struct{
ElemType *elem;
int length; //当前元素个数
}SqList;
二、静态顺序表分配实现
1.初始化
Status InitList(SqList* L) {
//构造一个空的线性表L
for (int i = 0; i < MAXSIZE; i++) {
L->elem[i] = 0; //将所有数据元素设置为默认初始值
}
L->length = 0;
return OK;
}
2.插入
/*
插入操作
初始条件:顺序表L已存在
操作结果:在L中的第i个位置之前插入新的数据元素e,L的长度加1
*/
Status ListInsert(SqList* L, int i, ElemType e) {
if (L->length >= MAXSIZE) { //线性表已满
return ERROR;
}
if (i < 1 || i > L->length + 1) { //当i不在范围内时
return ERROR;
}
if (i <= L->length) { //若插入位置不在表尾
for (int j = L->length - 1; j >= i - 1; j--) {
L->elem[j + 1] = L->elem[j]; // 被插入元素之后的元素向后移
}
}
L->elem[i - 1] = e; //将新元素插入
L->length++; //长度加1
return OK;
}
3.删除
/*
删除操作
初始条件:顺序表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(SqList* L, int i, ElemType* e) {
if (L->length == 0) { //线性表为空
return ERROR;
}
if (i < 1 || i > L->length) { //删除位置不正确
return ERROR;
}
*e = L->elem[i - 1]; // 删除的元素用e返回值
if (i < L->length) { //如果删除位置不在最后位置
for (int j = i; j < L->length; j++) {
L->elem[j - 1] = L->elem[j]; // 被删除元素之后的元素向前移
}
}
L->length--; //长度减1
return OK;
}
4.取值
/*
获取元素操作
初始条件:顺序表L已存在
操作结果:用e返回L中第i个数据元素的值
*/
Status GetElem(SqList L, int i, ElemType* e) {
if (L.length == 0 || i<1 || i>L.length) {
return ERROR;
}
*e = L.elem[i - 1];
return OK;
}
5.输出
/*打印线性表中的所有元素*/
void OutPut(SqList L) {
printf("当前顺序表的长度:%d\n", L.length);
for (int i = 0; i < L.length; i++) {
printf("%d ", L.elem[i]);
}
printf("\n");
}
6.查找
/*根据给出的元素,查找到相应结点的地址*/
int LocateElem(SqList L,ElemType e)
{
for(int i = 0;i < L.length; i++)
if (L.elem[i] == e)
return i+1; // 查找成功,返回序号i - 1
return 0; // 查找失败,返回0
}
7.完整代码
//头文件
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 20 //线性表存储空间的初始分配量
#define OK 1 //成功标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
// 定义顺序表
// 数组形式
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
typedef struct {
ElemType elem[MAXSIZE]; //存储数据
int length; //当前元素个数
}SqList;
Status InitList(SqList* L) {
//构造一个空的线性表L
for (int i = 0; i < MAXSIZE; i++) {
L->elem[i] = 0; //将所有数据元素设置为默认初始值
}
L->length = 0;
return OK;
}
/*
插入操作
初始条件:顺序表L已存在
操作结果:在L中的第i个位置之前插入新的数据元素e,L的长度加1
*/
Status ListInsert(SqList* L, int i, ElemType e) {
if (L->length >= MAXSIZE) { //线性表已满
return ERROR;
}
if (i < 1 || i > L->length + 1) { //当i不在范围内时
return ERROR;
}
if (i <= L->length) { //若插入位置不在表尾
for (int j = L->length - 1; j >= i - 1; j--) {
L->elem[j + 1] = L->elem[j];
}
}
L->elem[i - 1] = e; //将新元素插入
L->length++; //长度加1
return OK;
}
/*
删除操作
初始条件:顺序表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(SqList* L, int i, ElemType* e) {
if (L->length == 0) { //线性表为空
return ERROR;
}
if (i < 1 || i > L->length) { //删除位置不正确
return ERROR;
}
*e = L->elem[i - 1]; // 删除的元素用e返回值
if (i < L->length) { //如果删除位置不在最后位置
for (int j = i; j < L->length; j++) {
L->elem[j - 1] = L->elem[j];
}
}
L->length--; //长度减1
return OK;
}
/*
获取元素操作
初始条件:顺序表L已存在
操作结果:用e返回L中第i个数据元素的值
*/
Status GetElem(SqList L, int i, ElemType* e) {
if (L.length == 0 || i<1 || i>L.length) {
return ERROR;
}
*e = L.elem[i - 1];
return OK;
}
/*打印线性表中的所有元素*/
void OutPut(SqList L) {
printf("当前顺序表的长度:%d\n", L.length);
for (int i = 0; i < L.length; i++) {
printf("%d ", L.elem[i]);
}
printf("\n");
}
/*根据所给元素,查找对应元素位置*/
int LocateElem(SqList L, ElemType e)
{
for (int i = 0; i < L.length; i++)
if (L.elem[i] == e)
return i + 1; // 查找成功,返回序号i - 1
return 0; // 查找失败,返回0
}
int main()
{
SqList L;
printf("------构造一个空的线性表L------\n");
InitList(&L);
OutPut(L); //打印结果
printf("------测试插入10个数------\n");
for (int i = 1; i <= 10; i++) {
ListInsert(&L, i, i);
}
OutPut(L); //打印结果
printf("------在第3位之前插入0------\n");
ListInsert(&L, 3, 0);
OutPut(L); //打印结果
printf("------删除第6位的数据------\n");
ElemType e;
ListDelete(&L, 6, &e);
printf("删除的数据为:%d\n", e);
OutPut(L); //打印结果
printf("------获取元素操作------\n");
GetElem(L, 5, &e);
printf("得到第5个元素:%d", e);
}
8.运行结果
三、动态顺序表分配实现
1.初始化
Status InitList(SqList* L) {
//构造一个空的线性表L
L->elem = (ElemType*)malloc(MAXSIZE * sizeof(ElemType));
if (!L->elem) {
return ERROR;
}
L->length = 0;
return OK;
}
2.插入
/*
插入操作
初始条件:顺序表L已存在
操作结果:在L中的第i个位置之前插入新的数据元素e,L的长度加1
*/
Status ListInsert(SqList* L, int i, ElemType e) {
if (L->length >= MAXSIZE) { //线性表已满
return ERROR;
}
if (i < 1 || i > L->length + 1) { //当i不在范围内时
return ERROR;
}
if (i <= L->length) { //若插入位置不在表尾
for (int j = L->length - 1; j >= i - 1; j--) {
L->elem[j + 1] = L->elem[j];
}
}
L->elem[i - 1] = e; //将新元素插入
L->length++; //长度加1
return OK;
}
3.删除
/*
删除操作
初始条件:顺序表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(SqList* L, int i, ElemType* e) {
if (L->length == 0) { //线性表为空
return ERROR;
}
if (i < 1 || i > L->length) { //删除位置不正确
return ERROR;
}
*e = L->elem[i - 1]; // 删除的元素用e返回值
if (i < L->length) { //如果删除位置不在最后位置
for (int j = i; j < L->length; j++) {
L->elem[j - 1] = L->elem[j];
}
}
L->length--; //长度减1
return OK;
}
4.取值
/*
获取元素操作
初始条件:顺序表L已存在
操作结果:用e返回L中第i个数据元素的值
*/
Status GetElem(SqList L, int i, ElemType* e) {
if (L.length == 0 || i<1 || i>L.length) {
return ERROR;
}
*e = L.elem[i - 1];
return OK;
}
5.输出
/*打印线性表中的所有元素*/
void OutPut(SqList L) {
printf("当前顺序表的长度:%d\n", L.length);
for (int i = 0; i < L.length; i++) {
printf("%d ", L.elem[i]);
}
printf("\n");
}
6.完整代码
//头文件
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 20 //线性表存储空间的初始分配量
#define OK 1 //成功标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
// 定义顺序表
// 指针形式
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
typedef struct {
ElemType* elem;
int length; //当前元素个数
}SqList;
Status InitList(SqList* L) {
//构造一个空的线性表L
L->elem = (ElemType*)malloc(MAXSIZE * sizeof(ElemType));
if (!L->elem) {
return ERROR;
}
L->length = 0;
return OK;
}
/*
插入操作
初始条件:顺序表L已存在
操作结果:在L中的第i个位置之前插入新的数据元素e,L的长度加1
*/
Status ListInsert(SqList* L, int i, ElemType e) {
if (L->length >= MAXSIZE) { //线性表已满
return ERROR;
}
if (i < 1 || i > L->length + 1) { //当i不在范围内时
return ERROR;
}
if (i <= L->length) { //若插入位置不在表尾
for (int j = L->length - 1; j >= i - 1; j--) {
L->elem[j + 1] = L->elem[j];
}
}
L->elem[i - 1] = e; //将新元素插入
L->length++; //长度加1
return OK;
}
/*
删除操作
初始条件:顺序表L已存在
操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(SqList* L, int i, ElemType* e) {
if (L->length == 0) { //线性表为空
return ERROR;
}
if (i < 1 || i > L->length) { //删除位置不正确
return ERROR;
}
*e = L->elem[i - 1]; // 删除的元素用e返回值
if (i < L->length) { //如果删除位置不在最后位置
for (int j = i; j < L->length; j++) {
L->elem[j - 1] = L->elem[j];
}
}
L->length--; //长度减1
return OK;
}
/*
获取元素操作
初始条件:顺序表L已存在
操作结果:用e返回L中第i个数据元素的值
*/
Status GetElem(SqList L, int i, ElemType* e) {
if (L.length == 0 || i<1 || i>L.length) {
return ERROR;
}
*e = L.elem[i - 1];
return OK;
}
/*打印线性表中的所有元素*/
void OutPut(SqList L) {
printf("当前顺序表的长度:%d\n", L.length);
for (int i = 0; i < L.length; i++) {
printf("%d ", L.elem[i]);
}
printf("\n");
}
int main()
{
SqList L;
printf("------构造一个空的线性表L------\n");
InitList(&L);
OutPut(L); //打印结果
printf("------测试插入10个数------\n");
for (int i = 1; i <= 10; i++) {
ListInsert(&L, i, i);
}
OutPut(L); //打印结果
printf("------在第3位之前插入0------\n");
ListInsert(&L, 3, 0);
OutPut(L); //打印结果
printf("------删除第6位的数据------\n");
ElemType e;
ListDelete(&L, 6, &e);
printf("删除的数据为:%d\n", e);
OutPut(L); //打印结果
printf("------获取元素操作------\n");
GetElem(L, 5, &e);
printf("得到第5个元素:%d", e);
}
7.运行结果
四、顺序存储结构优缺点
1.优点
- 存储密度大。(结点本身所占存储量 / 结点结构所占存储量)
- 无须为表中元素之间的逻辑关系而增加额外的存储空间。
- 可以快速的存取表中任一位置的元素。
2.缺点
- 在插入、删除某一元素时,需要移动大量元素。
- 浪费存储空间。
- 拓展容量不方便。
五、顺序表操作算法分析
1.时间复杂度
- 顺序表的时间复杂度取决于操作的种类和顺序表的长度。
- 线性表的顺序存储结果在读、写数据是的时间复杂度是O(1),插入、删除操作的时间复杂度是O(n)。
- 插入:T(n) = O(n)
- 取值:T(n) = O(1)
- 删除:T(n) = O(n)
- 按位查找:T(n) = O(1) (一步到位查找)
- 按值查找:T(n) = O(n)
2.空间复杂度
- 顺序表操作算法的空间复杂度S(n) = O(1) (没有占用辅助空间)
- 顺序表的空间复杂度取决于元素的数量和元素类型的大小。
No.3
线性表链式存储结构
一、单链表
1.单链表基本概念
- 在链式结构中,除了要存储数据元素的信息外,还要存储它的后继元素的存储地址。因此,为了表示每个数据元素
ai
与其直接后继元素ai+1
之间的逻辑关系,对数据ai
来说,除了存储其本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。我们吧把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素ai
的存储映像,称为结点(Node)。 - n个结点(
ai
的存储映像)链结成一个链表,即为线性表(a1, a2, …, an)
的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫做单链表。
-
结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
-
线性表的链式表示又称为非顺序映像或链式映像。
-
用一组物理位置任意的存储单元来存放线性表的数据元素。
-
这组存储单元既可以是连续的,也可以是不连续的,甚至零散分布在内存中的任意位置上的。
-
链表中元素的逻辑次序与物理次序不一定相同。
-
结点:数据元素的存储映像,各结点由两个域组成:
- 数据域:存储元素数值数据。
- 指针域:存储直接后继结点的存储位置(地址)。
-
链表:n个结点由指针链组成一个链表。
-
头指针:是指向链表中第一个结点的指针。
- 首元结点:是指链表中存储第一个数据元素的结点。
- 头结点:是在链表的首元结点之前附设的一个结点,头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表的长度值。
-
链表中设置头结点的好处:
- 便于首元结点的处理:
首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理。 - 便于空表和非空表的统一处理:
无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。
- 便于首元结点的处理:
-
如何表示空表?
- 无头结点时,头指针为空时表示空表。
- 有头结点时,当头结点的指针域为空时表示空表。
-
访问时只能通过头指针进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,所以寻找第一个结点和最后一个结点需要花费时间不等。(这种存取元素的方式称为顺序存取法)
2.单链表存储结构
// 头文件
#include<stdio.h>
#include<stdlib.h>
#define OK 1 //正确标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
/*线性表的单链表存储结构*/
//构造结点
typedef struct LNode
{
ElemType elem;
struct LNode* next;
} LNode;
//构造LinkList
typedef struct {
int length;
LNode* next;
}*LinkList;
// 重要操作
p = L; // p指向头结点
p = L->next; // p指向首元结点
p = p->next; // p指向下一个结点
// 变量定义
// 两种方式定义方式都可使用
LinkList L;
LNode *p,*q;
3.初始化
/*构造一个带头结点的单链表*/
Status InitList(LinkList* L) {
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* p = (LNode*)malloc(sizeof(LNode)); //头结点
p->next = NULL; //头结点的后继指向null
(*L)->next = p; //头指针指向头结点
(*L)->length = 0; //初始长度为0
return OK;
}
4.插入
若将结点s插入到结点p和结点p->next之间,如下图所示,其核心操作是:
s->next = p->next;
p->next = s;
/**
* 单链表插入操作
* 初始条件:线性表L已存在
* 操作结果:在L中第pos个位置之前插入新的数据元素e,L的长度增加1
*/
Status ListInsert(LinkList* L, ElemType elem, int pos) {
if (pos<1 || pos >(*L)->length + 1) {
return ERROR;
}
//寻找第pos个结点
LNode* p = (*L)->next; //头结点
for (int i = 1; i < pos; i++) {
p = p->next;
}
//生成一个新结点
LNode* q = (LNode*)malloc(sizeof(LNode));
q->elem = elem;
q->next = p->next; //将p的后继赋值给q的后继
p->next = q; //将q赋值给p的后继
(*L)->length += 1; //链表长度加1
return OK;
}
5.删除
若将结点q删除,如下图所示,其核心操作是:
q = p->next; // 临时保存被删结点的地址以备释放
p->next = q->next; // 改变删除结点的前驱结点的指针域
free(q); // 释放q结点
/**
* 单链表删除操作
* 初始条件:线性表L已存在
* 操作结果:删除L的第pos个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(LinkList* L, ElemType* elem, int pos) {
if (pos < 1 || pos>(*L)->length) {
return ERROR;
}
//寻找到第pos个结点
LNode* p = (*L)->next, * q;
for (int i = 1; i < pos; i++) {
p = p->next;
}
q = p->next; //要删除的结点
p->next = q->next;
free(q);
(*L)->length -= 1;
return OK;
}
6.清空
/**
* 清空单链表
*/
Status Clear(LinkList *L){
Node *p = (*L)->next->next, *q;
while(p != NULL){
q = p;
p = p->next;
free(q);
}
(*L)->next->next = NULL;
(*L)->lenght = 0;
return OK;
}
7.销毁
/**
* 销毁单链表
*/
Status Destory(LinkList *L){
Node *p = (*L)->next, *q;
while(p != NULL){
q = p;
p = p->next;
free(q);
}
free((*L));
(*L) = NULL;
return OK;
}
8.输出
/*打印单链表表中的所有元素*/
void OutPut(LinkList L){
Node *p=L->next->next ;
for(int i=0;i<L->lenght;i++)
{
printf("%d ",p->data );
p=p->next ;
}
printf("\n");
}
9.查找
/*根据给出的元素,查找到相应结点的地址*/
LNode* LocateElem(LinkList L, ElemType e) {
LNode* p = L->next;
while (p && p->elem != e)
p = p->next;
return p;
}
10.取值
/*给出相应值的位置,取出对应的元素值*/
Status GetElem(LinkList L, int i, ElemType* e) {
LNode* p = L->next;
int j = 1;
while (p && j < 1) {
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
*e = p->elem;
return OK;
}
11.前插法
// 前插法创建单链表
void CreateList_H(LinkList* L, int n)
{
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* q = (LNode*)malloc(sizeof(LNode)); //头结点
q->next = NULL; //头结点的后继指向null
(*L)->next = q; //头指针指向头结点
(*L)->length = 0; //初始长度为0
for (int i = 0; i < n; i++)
{
int x;
LNode* p = (LNode*)malloc(sizeof(LNode));
scanf("%d", &x);
p->elem = x;
p->next = (*L)->next;
(*L)->next = p;
(*L)->length++;
}
}
12.后插法
// 后插法创建单链表
void CreateList_R(LinkList* L, int n)
{
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* q = (LNode*)malloc(sizeof(LNode)); //头结点
q->next = NULL; //头结点的后继指向null
(*L)->next = q; //头指针指向头结点
(*L)->length = 0; //初始长度为0
LNode* r = *L;
for (int i = 0; i < n; i++)
{
int x;
LNode* p = (LNode*)malloc(sizeof(LNode));
scanf("%d", &x);
p->elem = x;
p->next = NULL;
r->next = p;
r = p;
(*L)->length++;
}
}
13.完整代码
// 头文件
#include<stdio.h>
#include<stdlib.h>
#define OK 1 //正确标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
/*线性表的单链表存储结构*/
//构造结点
typedef struct LNode
{
ElemType elem;
struct LNode* next;
} LNode;
//构造LinkList
typedef struct {
int length;
LNode* next;
}*LinkList;
/*构造一个带头结点的单链表*/
Status InitList(LinkList* L) {
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* p = (LNode*)malloc(sizeof(LNode)); //头结点
p->next = NULL; //头结点的后继指向null
(*L)->next = p; //头指针指向头结点
(*L)->length = 0; //初始长度为0
return OK;
}
/**
* 单链表插入操作
* 初始条件:线性表L已存在
* 操作结果:在L中第pos个位置之前插入新的数据元素e,L的长度增加1
*/
Status ListInsert(LinkList* L, ElemType elem, int pos) {
if (pos<1 || pos >(*L)->length + 1) {
return ERROR;
}
//寻找第pos个结点
LNode* p = (*L)->next; //头结点
for (int i = 1; i < pos; i++) {
p = p->next;
}
//生成一个新结点
LNode* q = (LNode*)malloc(sizeof(LNode));
q->elem = elem;
q->next = p->next; //将p的后继赋值给q的后继
p->next = q; //将q赋值给p的后继
(*L)->length += 1; //链表长度加1
return OK;
}
/**
* 单链表删除操作
* 初始条件:线性表L已存在
* 操作结果:删除L的第pos个数据元素,并用e返回其值,L的长度减1
*/
Status ListDelete(LinkList* L, ElemType* elem, int pos) {
if (pos < 1 || pos>(*L)->length) {
return ERROR;
}
//寻找到第pos个结点
LNode* p = (*L)->next, * q;
for (int i = 1; i < pos; i++) {
p = p->next;
}
q = p->next; //要删除的结点
p->next = q->next;
free(q);
(*L)->length -= 1;
return OK;
}
/**
* 清空单链表
*/
Status Clear(LinkList* L) {
LNode* p = (*L)->next->next, * q;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
(*L)->next->next = NULL;
(*L)->length = 0;
return OK;
}
/**
* 销毁单链表
*/
Status Destory(LinkList* L) {
LNode* p = (*L)->next, * q;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
free((*L));
(*L) = NULL;
return OK;
}
/*打印单链表表中的所有元素*/
void OutPut(LinkList L) {
LNode* p = L->next->next;
for (int i = 0; i < L->length; i++)
{
printf("%d ", p->elem);
p = p->next;
}
printf("\n");
}
/*根据给出的元素,查找到相应结点的地址*/
LNode* LocateElem(LinkList L, ElemType e) {
LNode* p = L->next;
while (p && p->elem != e)
p = p->next;
return p;
}
/*给出相应值的位置,取出对应的元素值*/
Status GetElem(LinkList L, int i, ElemType* e) {
LNode* p = L->next;
int j = 1;
while (p && j < 1) {
p = p->next;
++j;
}
if (!p || j > i)
return ERROR;
*e = p->elem;
return OK;
}
// 前插法创建单链表
void CreateList_H(LinkList* L, int n)
{
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* q = (LNode*)malloc(sizeof(LNode)); //头结点
q->next = NULL; //头结点的后继指向null
(*L)->next = q; //头指针指向头结点
(*L)->length = 0; //初始长度为0
for (int i = 0; i < n; i++)
{
int x;
LNode* p = (LNode*)malloc(sizeof(LNode));
scanf("%d", &x);
p->elem = x;
p->next = (*L)->next;
(*L)->next = p;
(*L)->length++;
}
}
// 后插法创建单链表
void CreateList_R(LinkList* L, int n)
{
//生成一个空的LinkList和一个新结点
(*L) = (LinkList)malloc(sizeof(LinkList));
LNode* q = (LNode*)malloc(sizeof(LNode)); //头结点
q->next = NULL; //头结点的后继指向null
(*L)->next = q; //头指针指向头结点
(*L)->length = 0; //初始长度为0
LNode* r = *L;
for (int i = 0; i < n; i++)
{
int x;
LNode* p = (LNode*)malloc(sizeof(LNode));
scanf("%d", &x);
p->elem = x;
p->next = NULL;
r->next = p;
r = p;
(*L)->length++;
}
}
int main()
{
LinkList L;
//构造单链表
InitList(&L);
printf("------测试插入10个数------\n");
for (int i = 1; i <= 10; i++) {
ListInsert(&L, i, i);
}
OutPut(L);
printf("------删除第5位的数据------\n");
ElemType e;
ListDelete(&L, &e, 5);
OutPut(L);
GetElem(L, 2, &e);
LocateElem(L, 2);
printf("------清空单链表------\n");
Clear(&L);
OutPut(L);
LinkList List;
//CreateList_H(&List, 5);
CreateList_R(&List, 5);
}
14.运行结果
二、双链表
1.双链表基本概念
- 双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。
- 头结点的前驱指针域与最后一个结点的后继指针域都为空。
- 双向链表中,对于链表中的某一个结点p,它的后继的前驱以及它的前驱的后继都是它自己,即:
p->next-prior = p = p->prior-next
2.双链表存储结构
// 头文件
#include<stdio.h>
#include<stdlib.h>
#define OK 1 //正确标识
#define ERROR 0 //失败标识
typedef int Status; //Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int ElemType; //ElemType的类型根据实际情况而定,这里假定为int
/*双向链表存储结构*/
typedef struct DulNodse{
ElemType elem;
struct DulNode *prior; //直接前驱指针
struct DulNode *next; //直接后继指针
} DuLNode, *DuLinkList; // DLinkList为指向结构体DLNode的指针类型
3.插入
在双链表中p所指的结点之后插入结点*s,其指针的变化过程如下图所示:
//第一步:把p赋值给s的前驱
s->prior = p;
//第二步:把p->next赋值给s的后继
s->next = p->next;
//第三步:把s赋值给p->next的前驱
p->next->prior = s;
//第四步:把s赋值给p的后继
p->next = s;
4.删除
如果要删除q结点,只需下面两步:
//第一步:修改被删结点的前驱结点的后继指针
q->prior->next = q->next;
//第二步:修改被删结点的后继结点的前驱指针
q->next->prior = q->prior;
free(q);
三、循环链表
1.循环链表基本概念
- 将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种首尾相接的单链表称为单循环链表,简称循环链表。
- 由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样结点的指针域是否为空,而是判断它们是否等于头指针。
- 从表中任一节点出发均可找到表中其他结点。
2.仅设尾指针的循环链表
- 上述仅设头指针的循环链表有一个弊端,我们可以用O(1)的时间访问第一个节点,但对于最后一个节点,却需要O(n)的时间,于是就有了仅设尾指针的循环链表。
- 从图中可以看到,终端节点用尾指针rear指示,则查找终端节点是O(1),而开始节点,其实就是rear->next->next,其时间复杂度也是O(1)。
- 举个程序的例子,要将两个循环链表合成一个表时,有了尾指针就非常简单了。比如下面的这两个循环链表,它们的尾指针分别是
rearA
和rearB
,要想将它们合并,只需要以下操作。
//第一步:保存A的头结点
p = rearA->next;
//第二步:将本是指向B表的第一个结点(不是头结点)赋值给rearA->next
rearA->next = rearB->next->next;
//第三步:将原A表的头结点赋值给rearB->next
rearB->next=p;
四、链表操作算法分析
1.时间复杂度
- 单链表的时间复杂度取决于所执行的操作和链表的长度。
-
按位序插入:T(n) = O(n)
-
清空链表:T(n) = O(n)
-
链表销毁:T(n) = O(n)
-
求表长:T(n) = O(n)
-
取值:T(n) = O(n)
-
前插与后插:T(n) = O(1)
-
按位序删除:
- T(n) = O(n) -->最坏
- T(n) = O(1) -->最好
-
按值查找:T(n) = O(n)
-
头插法建立:T(n) = O(n)
-
尾插法建立:T(n) = O(n)
-
指定结点......:T(n) = O(1)
2.空间复杂度
- 单链表操作算法的空间复杂度S(n) = O(n)
- 单链表的空间复杂度取决于链表中元素的数量,即链表的长度。
五、链式存储结构优缺点
1.优点
- 数据元素的逻辑次序靠结点的指针来表示,对结点的操作不需要移动元素。
- 结点空间可以动态申请和释放。
2.缺点
- 存储密度小。(结点本身所占存储量 / 结点结构所占存储量)
- 属于顺序存取,对于任一结点的操作都要从头指针开始,增加了算法的复杂度。
No.4
顺序表与链表比较
1.存取(读写)方式
- 顺序表可以顺序存取,也可以随机存取,链表只能从表头顺序存取元素。例如在第i个位置上执行存或取的操作,顺序表仅需一次访问,而链表则需从表头开始依次访问i次。
2、逻辑结构与物理结构
- 采用顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻。而采用链式存储时,逻辑上相邻的元素,物理存储位置则不一定相邻,对应的逻辑关系是通过指针链接来表示的。
3、查找、插入和删除操作
- 对于按值查找,顺序表无序时,两者的时间复杂度均为O(n);顺序表有序时,可采用折半查找,此时的时间复杂度为O(
log2n
)。 - 对于按序号查找,顺序表支持随机访问,时间复杂度仅为O(1),而链表的平均时间复杂度为O(n)。顺序表的插入、删除操作,平均需要移动半个表长的元素。链表的插入、删除操作,只需修改相关结点的指针域即可。由于链表的每个结点都带有指针域,故而存储密度不够大。
4、空间分配
- 顺序存储在静态存储分配情形下,一旦存储空间装满就不能扩充,若再加入新元素,则会出现内存溢出,因此需要预先分配足够大的存储空间。
- 预先分配过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。动态存储分配虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且若内存中没有更大块的连续存储空间,则会导致分配失败。
- 链式存储的结点空间只在需要时申请分配,只要内存有空间就可以分配,操作灵活、高效。
No.5
线性表应用
一、线性表的合并
1.顺序表实现
# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h>
# define MaxSize 10
/*
求解一般集合的并集问题
A = (7,5,3,11)
B = (2,6,3)
求并集:A = (7,5,3,11,2,6)
*/
// 定义顺序表
typedef int ElemType;
typedef struct {
ElemType data[MaxSize];//存储数据
int length; //当前长度
}SqList;
//初始化顺序表
void InitList(SqList& L) {
L.length = 0;
for (int i = 0; i < MaxSize; i++) {
L.data[i] = 0;
}
}
// 求表长
int ListLength(SqList& L) {
return L.length;
}
// 取值
int GetElem(SqList L, int i, ElemType& e) {
if (i<1 || i>L.length)
return 0;
e = L.data[i - 1];
return e;
}
//在第i个位置插入数据
bool ListInsert(SqList& L, int i, ElemType e) {
//判断插入合法化
if (i<1 || i>L.length + 1) { // 只能插入在1~n+1的位置(下标就是0~n),其余的位置会报错
printf("插入元素位置无效!!!\n");
return false;
}
if (i >= MaxSize) {
printf("插入元素已满!!!\n");
return false;
}
for (int j = L.length; j >= i; j--) { // 从最后一个元素开始进行
L.data[j] = L.data[j - 1]; // 插入位置及之后的元素后移
}
L.data[i - 1] = e; // 第i个位置的下标是 i - 1,刚好空出来可以插入值
L.length++; // 表长增1
return true;
}
//顺序表按值查找并返回位序
int LocateElem(SqList L, ElemType e) {
// 从表的一端开始,逐个进行记录的关键字和给定值比较
for (int i = 0; i < L.length; i++) {
if (e == L.data[i]) {
return i + 1; // 查找到返回位序
}
}
return 0; // 查找不到,返回0
}
void MergeList(SqList& LA, SqList& LB) {
// 将所有在线性表LB中但不在LA中的数据元素插入LA中
int m = ListLength(LA); // 求LA的表长
int n = ListLength(LB); // 求LB的表长
int i;
for ( i = 1; i <=n ; i++)
{
int e;
GetElem(LB, i, e); // 取LB中第 i 个数据元素赋值给e
if (!LocateElem(LA, e)) // LA中不存在和e相同的数据元素
ListInsert(LA, ++m, e); // 将e插在LA的最后
}
}
int main() {
// 初始化LA与LB顺序表
SqList LA;
SqList LB;
InitList(LA);
InitList(LB);
// 为LA与LB顺序表插入值
ListInsert(LA, 1, 7);
ListInsert(LA, 2, 5);
ListInsert(LA, 3, 3);
ListInsert(LA, 4, 11);
ListInsert(LB, 1, 2);
ListInsert(LB, 2, 6);
ListInsert(LB, 3, 3);
// 求表长
ListLength(LA);
ListLength(LB);
// 线性表合并
MergeList(LA, LB);
2.链表实现
# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h>
// 单链表定义
typedef int ElemType; // 数据类型重命名
typedef struct LNode { // 声明结点的类型和指向结点的指针类型
ElemType data; // 结点的数据域
struct LNode* next; // 结点的指针域
}LNode, * LinkList; // LinkList为指向结构体LNode的指针类型
// 初始化
bool InitList(LinkList& L) { // 注意用引用 &
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL) // 判断申请空间是否成功
return false;
L->next = NULL; // 头结点之后的结点暂时不分配
return true;
}
// 求表长
int ListLength(LinkList L) {
LinkList p;
p = L->next; // 指向首元结点
int len = 0; // 定义一个计数器
while (p != NULL) { // 遍历单链表,统计结点数
len++;
p = p->next;
}
return len;
}
// 取值
int GetElem(LinkList L, int i, ElemType& e) {
LNode* p; // 定义一个预存变量
p = L->next; // 指向首元结点
int j = 1; // 定义一个计数器
while (p != NULL && j < i) {
p = p->next; // 依次往后获取下一个结点
++j;
}
if (!p || j > i) // 第i个元素不存在
return false;
e = p->data; // 获取第i个元素
return e; // 返回数据元素值
}
//在第i个位置插入数据
bool ListInsert(LinkList& L, int i, ElemType e) {
LNode* p, * s;
p = L; // 指向头结点
int j = 0;
while (p != NULL && j < i - 1) {
p = p->next;
++j;
}
if (!p || j > i - 1)
return false;
// 在第i-1个结点后插入新结点
s = (LNode*)malloc(sizeof(LNode)); // 申请一个新结点
s->data = e;
// 切记这两步绝对不能颠倒!!!
s->next = p->next;
p->next = s;
return true;
}
//链表按值查找并返回位序
int LocateElem(LinkList L, ElemType e) {
LNode* p; // 定义一个预存变量
p = L->next; // 指向首元结点
int j = 1;
while (p != NULL && p->data != e) {
p = p->next; // 指向下一个的结点
j++;
}
if (p) // 判断元素值是否在表中
return j; // 返回元素的位置序号
return 0;
}
// 线性表合并
void MergeList(LinkList& LA, LinkList& LB) {
// 将所有在线性表LB中但不在LA中的数据元素插入LA中
int m = ListLength(LA);
int n = ListLength(LB);
int i;
for ( i = 1; i <=n ; i++)
{
int e;
GetElem(LB, i, e); // 取LB中第 i 个数据元素赋值给e
if (!LocateElem(LA, e)) // LA中不存在和e相同的数据元素
ListInsert(LA, ++m, e); // 将e插在LA的最后
}
}
int main() {
// 初始化LA与LB顺序表
LinkList LA = (LinkList)malloc(sizeof(LNode));;
LinkList LB = (LinkList)malloc(sizeof(LNode));;
InitList(LA);
InitList(LB);
// 为LA与LB顺序表插入值
ListInsert(LA, 1, 7);
ListInsert(LA, 2, 5);
ListInsert(LA, 3, 3);
ListInsert(LA, 4, 11);
ListInsert(LB, 1, 2);
ListInsert(LB, 2, 6);
ListInsert(LB, 3, 3);
LocateElem(LA,2);
// 求表长
ListLength(LA);
ListLength(LB);
// 线性表合并
MergeList(LA, LB);
}
二、有序表的合并
1.顺序表实现
# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h>
# define MaxSize 10
/*
数据元素相互比较并且按值非递减或非递增的有序排列,称之为有序表。
求解一般集合的并集问题
A = (3,5,8,11)
B = (2,6,8,9,11,15,20)
求并集:C = (2,3,5,6,8,8,9,11,11,15,20)
*/
// 定义顺序表
typedef int ElemType;
typedef struct {
ElemType *data;//存储数据
int length; //当前长度
}SqList;
//初始化顺序表
void InitList(SqList *L) {
L->length = 0;
L->data = (ElemType*)malloc(sizeof(ElemType) * MaxSize);
}
//在第i个位置插入数据
bool ListInsert(SqList *L, int i, ElemType e) {
//判断插入合法化
if (i<1 || i>L->length + 1) { // 只能插入在1~n+1的位置(下标就是0~n),其余的位置会报错
printf("插入元素位置无效!!!\n");
return false;
}
if (i >= MaxSize) {
printf("插入元素已满!!!\n");
return false;
}
for (int j = L->length; j >= i; j--) { // 从最后一个元素开始进行
L->data[j] = L->data[j - 1]; // 插入位置及之后的元素后移
}
L->data[i - 1] = e; // 第i个位置的下标是 i - 1,刚好空出来可以插入值
L->length++; // 表长增1
return true;
}
//输出顺序表中的元素
void DisplayList(SqList L) {
for (int i = 0; i < L.length; i++) {
printf("%d ", L.data[i]);
}
printf("\n");
}
// 有序表合并
void MergeList(SqList LA, SqList LB, SqList* LC) {
int m = LA.length, n = LB.length;
LC->length = m + n; //新表长度为待合并两表的长度之和
LC->data = (ElemType*)malloc(MaxSize * sizeof(ElemType)); //为合并后的新表分配一个数组空间
int* pc = LC->data; //指针 pc 指向新表的第1个元素
//指针 pa 和 pb 的初值分别指向两个表的第1个元素
int* pa = LA.data;
int* pb = LB.data;
int* pa_last = LA.data + m - 1; //指针 pa_last 指向 LA 的最后一个元素
int* pb_last = LB.data + n - 1; //指针 pb_last 指向 LB 的最后一个元素
while ((pa <= pa_last) && (pb <= pb_last)) { // LA 和 LB 均未到达表尾
if (*pa <= *pb) *pc++ = *pa++; //依次“摘取”两表中值较小的结点插入到 LC 的最后
else *pc++ = *pb++;
}
while (pa <= pa_last) *pc++ = *pa++; //LB已到达表尾,依次将 LA 的剩余元素插入 LC 的最后
while (pb <= pb_last) *pc++ = *pb++; //LA已到达表尾,依次将 LB 的剩余元素插入 LC 的最后
}
int main() {
// 初始化LA与LB顺序表
SqList LA;
SqList LB;
InitList(&LA);
InitList(&LB);
// 为LA与LB顺序表插入值
ListInsert(&LA, 1, 3);
ListInsert(&LA, 2, 5);
ListInsert(&LA, 3, 8);
ListInsert(&LA, 4, 11);
ListInsert(&LB, 1, 2);
ListInsert(&LB, 2, 6);
ListInsert(&LB, 3, 8);
ListInsert(&LB, 4, 9);
ListInsert(&LB, 5,11);
ListInsert(&LB, 6, 15);
ListInsert(&LB, 7, 20);
// 线性表合并
SqList LC;
MergeList(LA, LB,&LC);
// 输出表内容
DisplayList(LA);
DisplayList(LB);
DisplayList(LC);
}
2.链表实现
# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h>
/*
数据元素相互比较并且按值非递减或非递增的有序排列,称之为有序表。
求解一般集合的并集问题
A = (3,5,8,11)
B = (2,6,8,9,11,15,20)
求并集:C = (2,3,5,6,8,8,9,11,11,15,20)
*/
// 单链表定义
typedef int ElemType; // 数据类型重命名
typedef struct LNode { // 声明结点的类型和指向结点的指针类型
ElemType data; // 结点的数据域
struct LNode* next; // 结点的指针域
}LNode, * LinkList; // LinkList为指向结构体LNode的指针类型
// 初始化
bool InitList(LinkList& L) { // 注意用引用 &
L = (LNode*)malloc(sizeof(LNode));
if (L == NULL) // 判断申请空间是否成功
return false;
L->next = NULL; // 头结点之后的结点暂时不分配
return true;
}
//在第i个位置插入数据
bool ListInsert(LinkList& L, int i, ElemType e) {
LNode* p, * s;
p = L; // 指向头结点
int j = 0;
while (p != NULL && j < i - 1) {
p = p->next;
++j;
}
if (!p || j > i - 1)
return false;
// 在第i-1个结点后插入新结点
s = (LNode*)malloc(sizeof(LNode)); // 申请一个新结点
s->data = e;
// 切记这两步绝对不能颠倒!!!
s->next = p->next;
p->next = s;
return true;
}
// 有序表合并
void MergeList(LinkList &LA, LinkList &LB, LinkList &LC) {
LNode* pa, * pb, *pc;
// pa与pb的初始值分别指向两个表的首元结点
pa = LA->next;
pb = LB->next;
// 令LA的头结点作为LC的头结点,且pc的初始值指向LC的头结点
LC = LA;
pc = LC;
while (pa && pb) { //LA和LB均未到达表尾,依次“摘取”两表中值较小的结点插入到 LC 的最后
if (pa->data <= pb->data) { //“摘取” pa 所指结点
pc->next = pa; //将 pa 所指结点链接到 pc 所指结点之后
pc = pa; //pc 指向 pa
pa = pa->next; //pa 指向下一结点
}
else { //“摘取” pb 所指结点
pc->next = pb; //将 pb 所指结点链接到 pc 所指结点之后
pc = pb; //pc 指向 pb
pb = pb->next; //pb 指向下一结点
}
}
pc->next = pa ? pa : pb; //将非空表的剩余段插入到 pc 所指结点之后
/*
相当于:
if (pa)
pc->next = pa;
else;
pc->next = pb;
*/
free(LB); // 释放删除
}
int main() {
// 初始化LA与LB顺序表
LinkList LA;
LinkList LB;
InitList(LA);
InitList(LB);
// 为LA与LB顺序表插入值
ListInsert(LA, 1, 3);
ListInsert(LA, 2, 5);
ListInsert(LA, 3, 8);
ListInsert(LA, 4, 11);
ListInsert(LB, 1, 2);
ListInsert(LB, 2, 6);
ListInsert(LB, 3, 8);
ListInsert(LB, 4, 9);
ListInsert(LB, 5,11);
ListInsert(LB, 6, 15);
ListInsert(LB, 7, 20);
// 线性表合并
LinkList LC;
MergeList(LA, LB,LC);
}
No.6
案例分析与实现
一、多项式运算
# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h>
# define MaxSize 100
/*
一元多项式运算
Pa(x) = 10 + 5x + 4x^2 + 3x^3 + 2x^4
Pb(x) = -3 + 8x + 4x^2 - 5x^4 + 7x^5 -2x^6
求出加法结果:Pc(x) = 7 + 13x + 8x^2 + 3x^3 -3x^4 +7x^5 -2x^6
求出减法结果:Pc(x) = 13 + -3x + 3x^3 +7x^4 +7x^5 -2x^6
*/
// 定义顺序表
typedef int ElemType;
typedef struct {
ElemType data[MaxSize];//存储数据
int length; //当前长度
}SqList;
//初始化顺序表
void InitList(SqList& L) {
L.length = 0;
for (int i = 0; i < MaxSize; i++) {
L.data[i] = 0;
}
}
//在第i个位置插入数据
bool ListInsert(SqList& L, int i, ElemType e) {
//判断插入合法化
if (i<1 || i>L.length + 1) { // 只能插入在1~n+1的位置(下标就是0~n),其余的位置会报错
printf("插入元素位置无效!!!\n");
return false;
}
if (i >= MaxSize) {
printf("插入元素已满!!!\n");
return false;
}
for (int j = L.length; j >= i; j--) { // 从最后一个元素开始进行
L.data[j] = L.data[j - 1]; // 插入位置及之后的元素后移
}
L.data[i - 1] = e; // 第i个位置的下标是 i - 1,刚好空出来可以插入值
L.length++; // 表长增1
return true;
}
// 多项式加法
void Add_Poly(SqList LA, SqList LB, SqList& LC) {
if (LA.length > LB.length)
LC.length = LA.length;
else
LC.length = LB.length;
for (int i = 1; i < LC.length; i++) {
int c;
c = LA.data[i - 1]+ LB.data[i - 1];
ListInsert(LC,i,c);
}
}
// 多项式减法
void Sub_Poly(SqList LA, SqList LB, SqList& LC) {
if (LA.length > LB.length)
LC.length = LA.length;
else
LC.length = LB.length;
for (int i = 1; i < LC.length; i++) {
int c;
c = LA.data[i - 1]-LB.data[i - 1];
ListInsert(LC, i, c);
}
}
int main() {
// 初始化LA与LB顺序表
SqList LA;
SqList LB;
InitList(LA);
InitList(LB);
// 为LA与LB顺序表插入值
ListInsert(LA, 1, 10);
ListInsert(LA, 2, 5);
ListInsert(LA, 3, 4);
ListInsert(LA, 4, 3);
ListInsert(LA, 5, 2);
ListInsert(LB, 1, -3);
ListInsert(LB, 2, 8);
ListInsert(LB, 3, 4);
ListInsert(LB, 4, 0);
ListInsert(LB, 5, -5);
ListInsert(LB, 6, 7);
ListInsert(LB, 7, -2);
// 加法与减法
SqList LC;
Add_Poly(LA, LB, LC);
Sub_Poly(LA, LB, LC);
}
二、稀疏多项式运算
# include<stdio.h>
# include<stdlib.h>
# include<stdbool.h>
/*
PA17(x) = 7 + 3x + 9x^8 + 5x^17
PB8(x) = 8x + 22x^7 - 9x^8
结果为:PA17(x) = 7 + 11x + 22x^7 + 5x^17
*/
typedef struct PNode {
float coef; // 系数
int expn; // 指数
struct PNode* next;
}PNode,*Polynomial;
void CreatePoly(Polynomial& P, int n) {
// 建立一个带头结点的单链表
P = (PNode*)malloc(sizeof(PNode));
P->next = NULL;
int i;
for ( i = 1; i <= n; ++i)
{
PNode* s,*pre,*q;
s = (PNode*)malloc(sizeof(PNode)); // 生成新结点
scanf("%f %d", &s->coef, &s->expn); // 输入系数和指数
pre = P; // pre用于保存q结点的前驱,初始值为头结点
q = P->next; // 指向首元结点
while (q !=NULL && q->expn<s->expn) // 找到第一个大于输入项指数的项*q
{
pre = q;
q = q->next;
}
s->next = q;
pre->next = s;
}
}
void Add_Poly(Polynomial& Pa, Polynomial& Pb) {
PNode* pa1, * pb1, * p, * r;
pa1 = Pa->next; // 指向Pa多项式的首元结点
pb1 = Pb->next; // 指向Pb多项式的首元结点
// 由于题给的多项式中,Pa多项式含有最小的指数,因此将Pa作为新链表
p = Pa; // p作为求和之后的多项式输出,并且Pa作为铺垫
while (pa1&&pb1) // pa1与pb1均为空
{
if (pa1->expn == pb1->expn) { // 当指数相等
float sum;
sum = pa1->coef + pb1->coef; // 使用sum变量保存两项的系数和
if (sum != 0) { // 当系数和不为0
pa1->coef = sum; // 修改Pa当前所指向结点的系数为两项系数之和
p->next = pa1; // 将修改后的Pa连接在p的后一项
p = pa1; // p指向pa1
pa1 = pa1->next; // pa1指向后一项
// 删除Pb当前所指向的结点,pb1指向后一项
r = pb1;
pb1 = pb1->next;
free(r);
}
else // 当系数和为0
{
// 删除Pa当前所指向的结点,并使pa1指向后一项
r = pa1;
pa1 = pa1->next;
free(r);
// 删除Pb当前所指向的结点,并使pb1指向后一项
r = pb1;
pb1 = pb1->next;
free(r);
}
}
else if (pa1->expn < pb1->expn) { // Pa当前所指向的结点指数值较小
p->next = pa1; // 将pa1连接在p之后
p = pa1; // p指向pa1
pa1 = pa1 -> next; // pa1指向后一项
}
else // Pb当前所指向的结点指数值较小
{
p->next = pb1; // 将pb1连接在p之后
p = pb1; // p指向pb1
pb1 = pb1->next; // pb1指向后一项
}
}
p->next = pa1 ? pa1 : pb1; // 插入非空多项式的剩余段
/*
相当于:
if (pa1)
p->next = pa1;
else;
p->next = pb1;
*/
free(Pb); // 释放删除Pb原来的数据
}
int main() {
// 创建多项式
Polynomial Pa, Pb;
CreatePoly(Pa, 4);
CreatePoly(Pb, 3);
// 多项式加法
Add_Poly(Pa, Pb);
}
三、图书信息管理
1. 顺序表实现
// 定义
struct Book {
char id[20];
char name[50];
int price;
};
typedef struct {
Book *data;
int length;
}SqList;
/*
......
*/
2.链表实现
// 定义
struct Book {
char id[20];
char name[50];
int price;
};
typedef struct {
Book data;
struct LNode *next;
}LNode,*LinkList;
/*
......
*/