2023年8月2日18:39:09
目的:学习测试
作者:wHappy
代码所需头文件define.h
# include <stdio.h>
# include <stdlib.h>
# include <malloc.h>
# include "define.h"
/*
时间:2023年7月27日21:56:31
作者:wHappy
目的:研究链式存储(考研打基础)
内容:单链表 ,循环链表,双向链表
链表(链式存储结构)的特点
(1)结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
(2)访问时只能通过头指针进入链表)并通过每个结点的指针域依次向后顺序扫描其余结点,
所以寻找第一个结点和最后一个结点所花费的时间不等
线性表的链式存储结构
·线性表中数据元素(结点)在存储器中的位置是任意的,
即逻辑上相邻的数据元素在物理位置上不一定相邻。
循环链表:首位相连构成的链表,从任意结点出都能找到表里的其他结点(从任意结点循环访问所有元素)
双链表:有两个指针域,一个指向直接前驱,另一个指向指直接后继
*/
// #ifndef # define #endif 避免头文件重复利用
# ifndef _LINKLIST_H
# define _LINKLIST_H
//全局变量定义区
typedef int LIElemtype; //这里以最简单的数据域存储为整形int 为数据类型,也可以是很复杂的数据类型
//单链表结点
typedef struct _LNode //定义单链表结点类型
{
LIElemtype data; //数据域
struct _LNode* next; //指针域
}LNode, *LinkList; //LNode普通类型(.操作元素) *LinkList指针类型(->操作元素)
//双链表结点
typedef int DouLElemtype; //数据域复合数据类型,这里以简单数据类型int为例
typedef struct _DouLinkNode //定义一个双链表结点类型
{
DouLElemtype data; //数据域
struct _DouLinkNode* prior; //指向前驱结点指针域
struct _DouLinkNode* next; //指向后继结点指针域
} DouLinkNode, *DouLinkList;
//多项式结点类型
typedef struct _PolyNode
{
int coeffcient ;//系数
int exponent ;//指数
struct _PolyNode* next; //指向后继结点指针
}PolyNode, *Polynomial;
//多项式基本操作
void InitPolynomial(Polynomial* p, int length); //初始化
void Traverse_P(Polynomial p);
void Print(Polynomial head); //输出多项式单链表
void AddPloynomial(Polynomial pa, Polynomial pb); //函数式相加
void AddPloynomial_1(Polynomial pa, Polynomial pb);//pa,pb分别为多项式一和多项式二的头指针
//单链表的基本操作
Status InitLL(LinkList* L); //初始化
LinkList CreateLL_H(LinkList &L, int n);//头插法创建链表
void CreateLL_R(LinkList L, int n);//尾插法创建链表
void Traverse(LinkList L);//遍历
Status Is_empty(LinkList L); //判空
Status ClearLL(LinkList* L);//清空单链表
Status Length_L(LinkList L); //单链表长度
Status GetElem(LinkList L, int position, LIElemtype* e); //获取元素(时间复杂度O(n))
Status LIInsertNode(LinkList L, int position, LIElemtype e);//插入 (时间复杂度O(n))
Status LIDeleteNode(LinkList L, int position, LIElemtype* e);//删除(时间复杂度O(n))
LinkList LocateElem_add(LinkList L, LIElemtype e);//按值查找 返回要查找的地址(时间复杂度O(n))
Status LocateElem_serial(LinkList L, LIElemtype e);//按值查找 返回计数器的值(时间复杂度O(n))
Status LIDestroy(LinkList *L);//销毁
LinkList BubbleSort(LinkList L);//冒泡排序
void Merge_LinkedList(LinkList *La, LinkList *Lb, LinkList *Lc);//有序合并三个链表
LinkList Create_list(void); //创建链表
//循环链表的操作
Status InitLL_cir(LinkList* L); //初始化循环链表
LinkList CreateLL_RC(LinkList L, int n); //尾插法创建循环单链表 王卓版
LinkList Connect_LC(LinkList La, LinkList Lb); //合并两个循环链表
//双向链表操作 两个指针域一个数据域,除了插入和删除区别于单链表其他都差不多
void InitDL(DouLinkList* L); //双链表的初始化
void CreateDL_H(DouLinkList L, int length);//创建双向链表 头插法
DouLinkList CreateDL_R(DouLinkList L, int length); //创建双向链表 尾插法
void TraverseDL_H(DouLinkList L);//前驱遍历双向链表
void TraverseDL_R(DouLinkList L);//后继遍历双向链表
DouLinkList GetElemDL(DouLinkList L, int position); //获取position-1位置(时间复杂度O(n))
Status InsertDL(DouLinkList L, int position, DouLElemtype e);//插入
void DeleteDL(DouLinkList L, int position,DouLElemtype* e);//删除 e:保留删除的元素
#endif
int main(void)
{
/*
//双链表验证
int n,data,length;
int len;
DouLinkList L,T;
InitDL(&L);//初始化双链表
CreateDL_R(L, 5);//创建双向链表 头插法
//InsertDL(L, 2, 99);//插入
DeleteDL(L, 2,&data);//删除 e:保留删除的元素
printf("删除的元素为%d\n",data);
TraverseDL_R(L);//前驱遍历双向链表
*/
/*
单链表操作验证
LinkList L;
LinkList La,Lb; //定义一个单链表
InitLL_cir(&La);
InitLL_cir(&Lb);
La = CreateLL_RC(La, 3);
Lb = CreateLL_RC(Lb, 4);
L = Connect_LC(La, Lb);
*/
/*
//验证单链表有序合并两链表
LinkList La, Lb, Lc;
//初始化三个链表
InitLL(&La);
InitLL(&Lb);
InitLL(&Lc);
//尾插法创建两个单链表
CreateLL_R(La,4);
CreateLL_R(Lb,7);
//有序合并La和Lb
Merge_LinkedList(&La, &Lb, &Lc);
Traverse(Lc);
system("pause");
*/
//多项式相加算法验算
Polynomial L1,L2;
InitPolynomial(&L1, 2);
InitPolynomial(&L2, 1);
AddPloynomial(L1, L2);
Print(L1);
//InitPolynomial(&L2, 1);
//Traverse_P(L2);
//L3 = AddPloynomial(L1, L2);
//Traverse_P(L3);
//Traverse(L);
//Traverse(Lb);
//Traverse(Lb);
//L = create_list();
//CreateLL_R(L, 5);
//GetElem(L, 2,&data);//获取的的是position-1位置的data
//printf("获取的元素:data = %d\n",data);
//LIInsertNode(L, 2, 99);//插入
//LIDeleteNode(L, 2, &data);//删除
//printf("删除的元素:data = %d\n",data);
//BubbleSort(L);//排序
//len = Length_L(L);//链表长度
//printf("len = %d\n",len);
//ClearLL(&L);
//CreateLL_H(L, 5);
return 0;
}
//双链表基本操作
void InitDL(DouLinkList* L) // 双链表的初始化
{
(*L) = (DouLinkList)malloc(sizeof(DouLinkNode)); //动态分配结点空间
(*L)->next = NULL; //后继指针置空
(*L)->prior =NULL; //前驱指针置空
}
void TraverseDL_H(DouLinkList L)//前驱遍历双向链表
{
DouLinkList p = L;
while(p)
{
printf("%d ",p->data);
p = p->prior;
}
printf("\n");
}
void TraverseDL_R(DouLinkList L) //后继遍历双向链表
{
DouLinkList p = L->next;
while(p)
{
printf("%d ",p->data);
p = p->next;
}
printf("\n");
}
void CreateDL_H(DouLinkList L, int length) //创建双向链表 头插法
{
for (int i=0; i<length; i++)
{
DouLinkList pNew = (DouLinkList)malloc(sizeof(DouLinkNode)); //为新结点分配一块内存,pNew指针指向这块内存
int data; //定义一个临时存放数据的变量
printf("(for %d) Please input the data:",i+1);//提示用户输入第几个数据
scanf("%d",&data); //输入数据
pNew->data = data; //数据存入结点的数据域
pNew->next = L->next; //新结点后继指针指向空
pNew->prior = L; //新结点前驱指针指向头结点
L->next = pNew; //头结点后继指向新结点
}
}
DouLinkList CreateDL_R(DouLinkList L, int length) //创建双向链表 尾插法
{
DouLinkList pTail = L ; //定义一个始终指向尾结点的指针pTail
for (int i=0; i<length; i++)
{
DouLinkList pNew = (DouLinkList)malloc(sizeof(DouLinkNode)); //尾指针指向头结点
int data;
printf("(for %d) Please input the number:",i+1);
scanf(" %d",&data);
pNew->data = data; //第一步将数据存入数据域
pNew->next = NULL; //第二步新结点指针域置空
pNew->prior = pTail;//第三步新结点前驱指针指向尾指针指向的结点
pTail->next = pNew;//第四步尾指针前驱指针指向新结点
pTail = pNew;//第五步最后尾指针再指向最后一个结点
}
return pTail;
}
DouLinkList GetElemDL(DouLinkList L, int position) //获取元素(时间复杂度O(n))
{
int i=0;
while (L->next && i<position-1)
{
i++;
L = L->next;
}
return L;
}
Status InsertDL(DouLinkList L, int position, DouLElemtype e)//插入
{
DouLinkList p = L;
p = GetElemDL(p, position); //找到position-1位置的p节点
if( !p ) //没有找到异常处理
return ERROR;
else //找到进行插入操作
{
DouLinkList pNew = (DouLinkList)malloc(sizeof(DouLinkNode)); //pNew指向生成的新结点
pNew->data = e; //数据存入数据域
pNew->next = p->next; // 第一步 新结点的后继指向p结点的后继
pNew->prior = p;//第二步 新结点的前驱指向p结点
p->next = pNew; //p结点的后继结点指向新结点
p->next->prior = pNew;//最后 p结点的后继结点的前驱指针指向新结点
}
return OK;
}
void DeleteDL(DouLinkList L, int position,DouLElemtype* e)//删除 e:保留删除的元素
{
DouLinkList p=L; //p指向头结点
p = GetElemDL(p,position); //函数作用找到position-1个结点,并将该节点返回
if(!p)
{
printf("位置不存在!\n");
exit(-1);
}
else
{
//删除操作
DouLinkList pfree = p->next; //指向position位置的结点
*e = pfree->data; //将删除结点的值保存下来
p->next = p->next->next; //p结点的后继指针指向position+1位置的结点
p->next->next->prior = p; //position+1位置的结点的前驱指针指向p结点
free(pfree); //释放position位置的结点
pfree = NULL; //释放的结点置空
}
}
//单链表基本操作
Status InitLL(LinkList* L) //初始化单链表
{
(*L) = (LinkList)malloc(sizeof(LNode)); //为链表动态分配空间 L是二级指针 *L是一级指针
if((*L) == NULL) //异常处理
{
printf("分配失败!\n");
exit(-1);
}
(*L)->next = NULL; //指针域置空
return OK;
}
Status InitLL_cir(LinkList* L) //初始化循环链表
{
(*L) = (LinkList)malloc(sizeof(LNode)); //为链表动态分配空间 L是二级指针 *L是一级指针
if((*L) == NULL) //异常处理
{
printf("分配失败!\n");
exit(-1);
}
(*L)->next = NULL; //指针域置空
return OK;
}
Status Is_empty(LinkList L) //判空
{
if(L)
return 0; //非空
else
return 1;
}
Status LIDestroy(LinkList *L)//销毁
{
if(!(*L))
return ERROR;
LinkList p =*L; //指向第一个头结点
while(p)
{
LinkList pfree = p;
p = p->next; //此行和下行不能互换
free(pfree);
pfree = NULL;
}
*L = NULL;
return OK;
}
Status ClearLL(LinkList* L) //清空单链表
{
LinkList p,q; //定义两个指针
p = (*L)->next; //p指向首结点
while(p) //依次释放结点
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL; //链表指针域置空
return OK;
}
Status Length_L(LinkList L) //单链表长度
{
LinkList p;
p = L->next;
int len = 0;
while(p)
{
len++;
p = p->next;
}
return len;
}
void Traverse(LinkList L)//遍历
{
LinkList p = L->next; //定义一个指针p始终首先指向首结点
while(p)
{
printf("%d ",p->data);
p = p->next; //p接着指向下一个结点
}
printf("\n");
}
Status GetElem(LinkList L, int position, LIElemtype* e) //获取元素(时间复杂度O(n))
{
LinkList p = L->next; //指向首结点
int cnt = 1; //计数器记录当前p结点位置
if(!p || position<cnt) //异常处理
return ERROR;
while(p && position>cnt) //当p不为空时,position==cnt说明找到该位置了
{
p->next;
cnt++;
}
*e = p->data;
return OK;
}
LinkList LocateElem_add(LinkList L, LIElemtype e)//按值查找 返回要查找的地址(时间复杂度O(n))
{
LinkList p = L->next;
while(p && (p->data!=e))
p = p->next;
if(!p) //如果p不为空还没找到,p的地址就返回NULL
return NULL;
return p;
}
Status LocateElem_serial(LinkList L, LIElemtype e) //按值查找 返回计数器的值(时间复杂度O(n))
{
LinkList p = L->next;
int s = 1; //定义计数器记录的元素序号s
while(p && (p->data!=e))
{
p = p->next;
s++;
}
//循环结束之后两种情况
if(p)
return s;
else
return 0;
}
Status LIInsertNode(LinkList L, int position, LIElemtype e)//插入 (时间复杂度O(n))
{
LinkList q;
LinkList p = L; //插入操作与GetElem操作不同
int i = 0; //i从0开始,p要从L开始
//因为如果从1到L的话,无法在插入1这个位置
while(p && i<position-1)
{
i++;
p = p->next;
}
//循环结束之后,又分为两种情况找到和没找到
if(!p || i>position-1) //没找到
return ERROR;
//找到
#if 1 //方法1
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //分配新结点
pNew->data = e;
pNew->next = p->next;
p->next = pNew;
#elif 0 //方法2
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //分配新结点
pNew->data = e;
q = p->next;
p->next = pNew;
pNew->next = q;
#endif
return OK;
}
Status LIDeleteNode(LinkList L, int position, LIElemtype* e) //删除(时间复杂度O(n))
{
LinkList p = L; //指向头结点
int i = 0;
while(p && i<position-1)
{ //查找position-1位置结点地址
p = p->next;
i++;
}
//循环结束之后,又分为两种情况找到和没找到
if(!p || !(p->next) || position-1<i)//没找到
return ERROR; //多增加一条语句!(p->next),意思是当节点数为n时,删除n+1个结点会error
//找到 执行删除算法
LinkList pfree = p->next; //定义一个指向要删除结点的指针
*e = pfree->data; //将要删除结点的数据域保存下来
p->next = pfree->next; //其次再将要删除结点的前驱指向其后继
free(pfree); //最后释放其内存
pfree = NULL;
return OK;
}
LinkList CreateLL_H(LinkList &L, int n) //头插法创建链表 时间复杂程度O(n)
{ //此方法创建的链表,遍历的顺序和创建顺序相反
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
printf("Please input %d number:\n",n);
for(int i=0; i<n; i++) //头插法
{
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //分配一个新结点
int data;
printf("请输入第%d数据: ",i+1);
scanf(" %d",&data); //输入一个数据
pNew->data = data; //将数据存入新结点的数据域中
pNew->next = L->next; //再将新结点指针域置空
L->next = pNew; //最后将新结点接到头结点后面
}
return L;
}
LinkList Create_list(void) //创建链表(尾插法1)
{
int len; //结点个数
int i;
int val; //用来临时存放用户输入的结点的值
LinkList L = (LinkList)malloc(sizeof(LNode)); //动态生成一个头结点
if(NULL == L) //判断是否生成头结点
{
printf("分配失败,程序终止!\n");
exit(-1);
}
LinkList pTail = L; //定义一个始终指向尾结点的结构体指针变量
pTail->next = NULL; //清空尾结点指针域使其始终指向链表最后一个结点
printf("请输入你需要生成链表结点的个数:len = ");
scanf("%d",&len);
for(i=0; i<len; i++)//生成链表步骤
{
printf("请输入第%d个结点的值:",i+1);
scanf("%d",&val);
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //生成的新结点
if(NULL == pNew)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
//链表生成算法
pNew->data = val;
pTail->next = pNew;
pNew->next = NULL;
pTail = pNew;
}
return L; //返回链表的首地址(通过首地址能够找到整个链表)
}
LinkList CreateLL_RC(LinkList L, int n) //尾插法创建链表王卓版
{
printf("Please input %d number:\n",n); //接收输入结点个数
LinkList pTail = L; //定义一个尾指针始终指向我们的尾结点
//链表生成算法
for (int i=0; i<n; i++)
{
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //动态分配一个结点
int data; //定义一个存放数据变量
printf("请输如第%d数据: ",i+1);
scanf("%d",&data); //输入数据
pNew->data = data; //存入数据到结点的数据域
pNew->next = L->next; //尾指针指针域置空
pTail->next = pNew; //尾指针的指针域指向新结点
pTail = pNew; //最后移动尾指针也指向尾结点
}
return pTail;
}
void CreateLL_R(LinkList L, int n) //尾插法创建单链表王卓版
{
printf("Please input %d number:",n); //接收输入结点个数
LinkList pTail = L; //定义一个尾指针始终指向我们的尾结点
//链表生成算法
for (int i=0; i<n; i++)
{
LinkList pNew = (LinkList)malloc(sizeof(LNode)); //动态分配一个结点
int data; //定义一个存放数据变量
scanf(" %d",&data); //输入数据
pNew->data = data; //存入数据到结点的数据域
pNew->next = NULL; //尾指针指针域置空
pTail->next = pNew; //尾指针的指针域指向新结点
pTail = pNew; //最后移动尾指针也指向尾结点
}
}
LinkList BubbleSort(LinkList L) //冒泡排序
{
int i,j;
int len = Length_L(L); //求链表的长度
LinkList p, q; //定义两个指针
//冒泡排序核,心算法
for (i=0,p = L->next; i<len-1; i++,p = p->next) //n个数总体只需要比较n-1次
{
for (j=i+1,q=p->next; j<len; j++,q = q->next) //内部要逐个比较也就是n次
{
if(p->data > q->data)//升序
{
int temp; //用于交换的临时变量
temp = p->data;
p->data = q->data;
q->data = temp;
}
}
}
return L;
}
LinkList Connect_LC(LinkList La, LinkList Lb) //合并两个循环链表
{
LinkList p = (LinkList)malloc(sizeof(LNode));
//四步
p = La->next; //第一步p指向链表La的头结点
La->next = Lb->next->next; //尾结点指向Lb的首结点
free(Lb->next); //释放Lb的头结点
Lb->next = p;
return Lb;
}
void Merge_LinkedList(LinkList *La, LinkList *Lb, LinkList *Lc)//有序合并三个链表
{
*Lc = *La; //Lc指向La的头结点,并使用La的头结点进行合并
LinkList pa = (*La)->next; //定义两个指针变量pa,pb分别指向La和Lb的有首结点
LinkList pb = (*Lb)->next;
LinkList pc = (*Lc); //pc指向Lc头结点
while (pa && pb) //pa和pb 不为空
{
if(pa->data < pb->data) //数据域比大小,将最小的连接到pc链表中
{
pc->next = pa;
pc = pa;
pa = pa->next;
}
else
{
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
//结束之后出现了pa或者pb其中有一个已经为空了,进行接下来操作
pc->next = pa ? pa : pb; //三目运算符 意思就是如果为真将pa后面的所有结点介入pc后面,否则把pb后面的结点街道pc后面
free(*Lb); //释放链表Lb
(*La) = (*Lb) = NULL; //最后将两个用过的链表置空
}
//多项式
void InitPolynomial(Polynomial* p, int length) //初始化
{
(*p) = (Polynomial)malloc(sizeof(PolyNode)); //初始化头结点
(*p)->next = NULL;
printf("Please input the cofficient and exponent:"); //输入系数和指数
for(int i=0; i<length; i++)
{
Polynomial pNew = (Polynomial)malloc(sizeof(PolyNode)); //先初始化一个新结点
scanf(" %d", &(pNew->coeffcient)); //接收输入系数
scanf(" %d", &(pNew->exponent)); //接收输入指数
Polynomial q = (*p)->next; //q为指向比pNew->exponent大的结点
Polynomial pre = (*p); //pre只想q的直接前驱结点
while(q && q->exponent < pNew->exponent)
{ //第一次的for循环不会执行
//直到找到一个结点的exponent大于pNew->exponent,如果没有找到q = NULL
pre = q;
q = q->next;
}
//当q->exponent > pNew->exponent,执行下列代码
pNew->next = q;
pre->next = pNew;
}
}
void Traverse_P(Polynomial p) //遍历
{
Polynomial s = p->next; //定义一个指针p始终首先指向首结点
printf("%fX^%d",s->coeffcient,s->exponent); //先输出第一项(不用前置符号)
s=s->next; //temp指针指向下一项
while (s != NULL) //循环输出后续项
{
if (s->coeffcient > 0) //若为正系数
printf(" +%fX^%d",s->coeffcient,s->exponent);
else if (s->coeffcient < 0) //若为负系数
printf("%fX^%d",s->coeffcient,s->exponent);
s = s->next;
}
}
void AddPloynomial(Polynomial pa, Polynomial pb) //函数式相加
{
Polynomial p1 = pa->next; //p1指向首结点
Polynomial p2 = pb->next;
Polynomial temp; //保留要删除的结点
int sum = 0; //存放p1和p2的系数和
while(p1 && p2) //p1和p2均未达到表尾 三种情况
{
if(p1->exponent == p2->exponent) //第一种情况 指数相等,系数相加
{
sum = (p1->coeffcient + p2->coeffcient);
if( sum ) //系数相加和不为零
{
p1->coeffcient = sum;
pa->next = p1;
pa = pa->next;
p1 = p1->next;
temp = p2;
p2 = p2->next;
free(temp);
}
else
{
temp = p1;
p1 = p1->next;
free(temp);
temp = p2;
p2 = p2->next;
free(temp);
}
}
else if(p1->exponent > p2->exponent) //第二种情况
{
pa->next = p2;
pa = pa->next;
p2 = p2->next;
}
else //最后一种情况
{
pa->next = p1;
pa = pa->next;
p1 = p1->next;
}
}
if(p1)
pa->next = p1;
else
pa->next = p2;
}
//多项式的加法计算
void AddPloynomial_1(Polynomial pa, Polynomial pb)//pa,pb分别为多项式一和多项式二的头指针
{
Polynomial p = pa->next; //p为遍历指针,此时指向多项式一的第一项
Polynomial q = pb->next; //q为遍历指针,此时指向多项式二的第一项
Polynomial pre=pa; //pre此刻指向多项式一的头指针,后续作为中间载体
Polynomial u; //u指针做临时指针,用于释放节点
while (p!=NULL && q!=NULL)//若指针指向的内容都不为空
{
if (p->exponent > q->exponent)//若多项式一中的项系数大于对应多项式二中的项
{
pre = p ;
p = p->next;
}
else if(p->exponent == q->exponent)//若两项系数相等则合并同类项
{
int x = p->coeffcient + q->coeffcient;//x为合并后的系数
if (x != 0) //若合并后系数不为零
{
p->coeffcient = x; //将合并后的系数赋给多项式一中对应的项
pre=p; //pre指向p结点
}
else //若合并后系数为零
{
pre->next = p->next;//指向下一个结点
free(p); //释放p销毁结点
}
p = pre->next;
u = q;
q = q->next;
free(u);
}
else //若多项式一中的项系数小于对应多项式二中的项
{
u = q->next;
q->next = p;
pre->next = q;
pre = q ;
q = u;
}
}
if (q)
{
pre->next = q;
}
free(pb);
}
void Print(Polynomial head) /*输出多项式*/
{
head=head->next;
while(head)
{
if(head->exponent)
printf("(%dx^%d)",head->coeffcient,head->exponent);
else
printf("(%d)",head->coeffcient);
if(head->next)
printf("+");
else
break;
head=head->next;
}
printf("\n");
}
# ifndef _DEFINE_H
# define _DEFINE_H
# define TRUE 1
# define FALSE 0
# define OK 1
# define ERROR 0
# define INFEASIBLE -1
# define OVERFLOW -2
typedef int Status;
#endif