2024年王道数据结构考研复习指导第二章:线性表的链式表示——课后综合应用题个人学习的相关运行代码。
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{ //定义单链表结点类型
int data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
//初始化一个单链表(带头结点)
bool InitHList(LinkList &L){
//分配一个头结点
L=(LNode*)malloc(sizeof(LNode));
if(L==NULL) //内存不足,分配失败
return false;
L->next=NULL; //头结点之后暂时还没有结点
/*
初始化循环单链表
L->next=L;
*/
return true;
}
//初始化一个单链表(不带头结点)
bool InitList(LinkList &L){
L=NULL; //空表,无任何结点
return true;
}
//判断单链表是否为空
bool Empty(LinkList L){
if(L->next==NULL) /*如果NULL改为L,则是为判断循环单链表*/
return true;
else
return false;
}
//判断结点p是否表尾结点
bool isTail(LinkList L, LNode *p){
if(p->next==NULL) /*如果NULL改为L,则是为判断循环单链表*/
return true;
else
return false;
}
//尾插法建立单链表(带头结点)
LinkList List_HTailInsert(LinkList &L){
int x;
L=(LinkList)malloc(sizeof(LNode));
LNode *s, *r=L; //r为表尾指针
scanf("%d", &x); //输入结点的值
while(x!=9999){ //输入9999表示结束
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s; //r指向新的表尾结点
scanf("%d", &x);
}
r->next=NULL; //尾结点指针置空
return L;
}
//尾插法建立单链表(不带头结点)
LinkList List_TailInsert(LinkList &L){
int x;
InitList(L);
LNode *s, *r; //r为表尾指针
scanf("%d", &x); //输入结点的值
while(x!=9999){ //输入9999表示结束
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=NULL; //尾结点指针置空
if(L==NULL){ //链表是否为空
L=s;
r=L;
}
else{
r->next=s;
r=s;
}
scanf("%d", &x);
}
return L;
}
//头插法建立单链表!链表的逆置
LinkList List_HeadInsert(LinkList &L){
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL; //初始为空链表
scanf("%d", &x); //输入结点的值
while(x!=9999){ //输入9999表示结束
s=(LNode*)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s; //将新结点插入表中,L为头指针
scanf("%d", &x);
}
return L;
}
//打印单链表(不带头结点)
void PrintLinkList(LinkList &L){
LNode *p=L;
if(p==NULL){
printf("\n\t链表为空。\n");
}
else{
printf("\n\t链表内容为:");
while(p){
printf("%d ", p->data);
p=p->next;
}
}
printf("\n");
}
//打印单链表(带头结点)
void PrintHLinkList(LinkList &L){
LNode *p=L;
if(p->next==NULL){
printf("\n\t链表为空!\n");
}
else{
printf("\n\t链表内容为:");
while(p->next!=NULL){
printf("%d ", p->next->data);
p=p->next;
}
}
printf("\n");
}
//按序号查找结点
LNode *GetElem(LinkList L, int i){
if(i<0)
return NULL;
/* 需添加代码(不带头结点)
if(i==1){ //插入第1个结点的操作与其它结点操作不同
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data=e;
s->next=L;
L=s; //头指针指向新结点
return true;
}
*/
LNode *p; //指针p指向当前扫描到的结点
int j=0; //当前p指向的是第几个结点(若不带头结点则j=1)
p=L; //L指向头结点,头结点是第0个结点(不存数据)
while(p!=NULL&&j<i){ //循环找到第i个结点
p=p->next;
j++;
}
return p;
}
//按值查找,找到数据域==e的结点
LNode *LocateElem(LinkList L, int e){
LNode *p=L->next;
//从第1个结点开始查找数据域为e的结点
while(p!=NULL&&p->data!=e)
p=p->next;
return p; //找到后返回该结点指针,否则返回NULL
}
//求表的长度
int Length(LinkList L){
int len=0; //统计表长
LNode *p=L->next;
while(p!=NULL){
p=p->next;
len++;
}
return len;
}
//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p, int e){
if(p==NULL)
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data=e; //用结点s保存数据元素e
s->next=p->next;
p->next=s; //将结点s连接到p之后
return true;
}
//前插操作:在p结点之前插入元素e
bool InsertPriorNode(LNode *p, int e){
if(p==NULL)
return false;
LNode *s=(LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->next=p->next;
p->next=s; //将新结点s连接到p之后
s->data=p->data; //将p中元素复制到s中
p->data=e; //p中元素覆盖为e,成功前插
return true;
}
//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, int e){
if(i<1)
return false;
LNode *p;
p=GetElem(L, i-1);
return InsertNextNode(p, e);
}
//删除指定结点p
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
p->data=p->next->data;
p->next=q->next;
free(q);
return true;
}
//按位序删除(带头结点)
bool ListDelete(LinkList &L, int i, int &e){
if(i<1)
return false;
LNode *p;
p=GetElem(L, i-1);
if(p==NULL) //i值不合法
return false;
if(p->next==NULL) //第i-1个结点之后已无其他结点
return false;
LNode *q=p->next; //令q指向被删除的结点
e=q->data; //用e返回元素的值
p->next=q->next; //将*q结点从链中“断开”
free(q); //释放结点的存储空间
return true; //删除成功
}
//递归删除带头H中所有x值
void Del_HX(LinkList &H, int num){
LNode *p=H->next; //从链表的第一个节点开始查找
LNode *prev=H; //用于记录前一个节点
while(p!=NULL){
if(p->data==num){
LNode *temp=p; //用于暂存要删除的节点
prev->next=p->next; //将前一个节点的next指针指向当前节点的下一个节点
p=p->next; //将p指针移动到下一个节点
free(temp); //释放暂存的节点内存
}
else{
prev=p; //更新前一个节点指针
p=p->next; //移动到下一个节点
}
}
}
//递归删除不带头结点的单链表L中所有x值
void Del_X(LinkList &L, int x){
LNode *p; //p指向待删除结点
if(L==NULL) //递归出口
return;
if(L->data==x){ //若L所指结点的值为x
p=L; //删除*L,并让L指向下一结点
L=L->next;
free(p);
Del_X(L, x); //递归调用
}
else //若L所指结点的值不为x
Del_X(L->next, x); //递归调用
}
//递归删除带头结点的单链表L中所有a-b之间值
void Del_HX_ab(LinkList &H, int a, int b){
LNode *p; //p指向待删除结点
if(H->next==NULL) //递归出口
return;
//若L所指结点的值为x
if(H->next->data>=a&&H->next->data<=b){
p=H->next; //删除*H,并让H指向下一结点
H->next=H->next->next;
free(p);
Del_HX_ab(H, a, b); //递归调用
}
else //若L所指结点的值不为x
Del_HX_ab(H->next, a, b);
}
//逐点检查带头H中a-b之间的值并删除
void RangeDelete(LinkList &H, int a, int b){
LNode *pr=H, *p=H->next;
while(p!=NULL){
if(p->data>=a&&p->data<=b){
pr->next=p->next;
free(p);
p=pr->next;
}
else{
pr=p;
p=p->next;
}
}
}
//摘下头结点依次头插法实现逆置
LinkList Reverse_1(LinkList &H){
LNode *p, *r; //p为工作指针,r为p的后继以防断链
p=H->next; //从第一个元素结点开始
H->next=NULL; //先将头结点L的next域置为NULL
while(p!=NULL){ //依次将元素结点摘下
r=p->next; //暂存p的后继
p->next=H->next; //将p结点插入到头结点之后
H->next=p;
p=r;
}
return H;
}
//在带头单链表H中删除最小值并打印表
LinkList Delete_Min(LinkList &H){
if(!Empty(H)){
LNode *pre=H;
LNode *p=pre->next; //p为工作指针,pre指向其前驱
LNode *minpre=pre;
LNode *minp=p; //保存最小值结点及其前驱
while(p!=NULL){
if(p->data<minp->data){
minp=p;
minpre=pre;
}
pre=p; //继续扫描下一个结点
p=p->next;
}
minpre->next=minp->next;//删除最小值结点
free(minp);
PrintHLinkList(H);
}
else{
printf("\n\t链表为空,无法删除!\n");
}
}
//空间复杂O(1)的单链表就地逆置
LinkList Reverse_2(LinkList &H){
//只使用额外的指针变量pre,p,r来完成逆置,即常数级空间复杂O(1)
LNode *pre, *p=H->next, *r=p->next;
p->next=NULL; //处理出最后结点
while(r!=NULL){ //直到r空,说明p为原表最后一个结点
pre=p;
p=r;
r=r->next;
p->next=pre; //指针反转
}
H->next=p;
return H;
}
//递增排序带头H表
void SortAscend(LinkList &H){
LNode *p=H->next, *pre;
LNode *r=p->next; //r保持*p后继结点指针保证不断链
p->next=NULL; //构造只含一个数据结点的有序表
p=r;
while(p!=NULL){
r=p->next; //保存*p的后继结点指针
pre=H;
while(pre->next!=NULL&&pre->next->data<p->data)
pre=pre->next; //在有序表中查找插入*p的前驱结点*pre
p->next=pre->next; //把*p插入到pre之后
pre->next=p;
p=r; //继续扫描原表剩余结点
}
}
//给定两个链表并输出公共结点
LinkList List_Same(LinkList &A, LinkList &B, LinkList &C){
LNode *p=A->next;
LNode *q=B->next;
LNode *s, *r=C; //r为表尾指针
while(p!=NULL&&q!=NULL){
if(p->data==q->data){
s=(LNode*)malloc(sizeof(LNode));
s->data=p->data;
r->next=s;
r=s; //r指向新的表尾结点
p=p->next;
q=q->next; //移动q指针
}
else if(p->data<q->data){
p=p->next;
}
else{
q=q->next;
}
}
r->next=NULL; //尾结点指针置空
return C;
}
//给定两个单链表找第一个公共结点
LinkList Search_1st_Common(LinkList &H1, LinkList &H2){
int dist;
int len1=Length(H1), len2=Length(H2);
LinkList longList, shortList;
if(len1>len2){
longList=H1->next;
shortList=H2->next;
dist=len1-len2;
}
else{
longList=H2->next;
shortList=H1->next;
dist=len2-len1;
}
while(dist--)
longList=longList->next;
while(longList!=NULL){
if(longList==shortList)
return longList;
else{
longList=longList->next;
shortList=shortList->next;
}
}
return NULL;
}
//去重递增带头H
void Del_Same(LinkList &H){
LNode *p=H->next, *q;
if(p==NULL)
return;
while(p->next!=NULL){
q=p->next;
if(p->data==q->data){
p->next=q->next;
free(q);
}
else
p=p->next;
}
}
int main(){
LinkList L; //声明一个单链表 (不带头结点)
InitList(L); //初始化单链表(不带头结点)
LinkList H, A, B; //声明三个单链表 (带头结点)
InitHList(H); //初始化单链表(带头结点)
InitHList(A);
InitHList(B);
int i=1, choice;
int num, a, b;
while(i)
{
printf("\n\t****************单 链 表*****************");
printf("\n\t* *");
printf("\n\t* 0--创建一个不带头单链表L *");
printf("\n\t* 1--创建一个带头单链表H *");
printf("\n\t* 2--递归删除不带头L中所有x值 *");
printf("\n\t* 3--删除带头H中所有x值 *");
printf("\n\t* 4--反向输出带头H的各结点值 *");
printf("\n\t* 5--在带头H中删除最小值并打印表 *");
printf("\n\t* 6--就地逆置带头H的结点 *");
printf("\n\t* 7--递增排序带头H表 *");
printf("\n\t* 8--删除带头H中a-b之间的值 *");
printf("\n\t* 9--给定两个链表并输出公共结点 *");
printf("\n\t* 10--递增输出带头H各结点并free *");
printf("\n\t* 11--分解带头A为奇序A和偶序B表 *");
printf("\n\t* 12--去重递增带头H *");
printf("\n\t* -1-- 返 回 *");
printf("\n\t* *");
printf("\n\t*******************************************");
printf("\n\t请选择菜单号(0--10):");
scanf("%d",&choice);
switch(choice)
{
case 0:
{
printf("\n\t请开始建立不带头单链表并输入9999结束:");
List_TailInsert(L);
PrintLinkList(L);
break;
}
case 1:
{
printf("\n\t请开始建立带头单链表并输入9999结束:");
List_HTailInsert(H);
PrintHLinkList(H);
break;
}
case 2:
{
printf("\n\t请输入需要输出的x的值:");
scanf("%d", &num);
Del_X(L, num);
printf("\n\t成功删除不带头L中所有x值!\n");
PrintLinkList(L);
break;
}
case 3:
{
printf("\n\t请输入需要删除的x的值:");
scanf("%d", &num);
Del_HX(H, num);
printf("\n\t成功删除带头H中所有x值!\n");
PrintHLinkList(H);
break;
}
case 4:
{
Reverse_1(H); //空间复杂O(1)
if(!Empty(H)){
printf("\n\t成功反向输出各结点!\n");
PrintHLinkList(H);
}
else{
printf("\n\t链表为空,无法反向输出!\n");
}
break;
}
case 5:
{
Delete_Min(H);
break;
}
case 6:
{
Reverse_2(H); //空间复杂O(1)
if(!Empty(H)){
printf("\n\t成功就地逆置该链表各结点!\n");
PrintHLinkList(H);
}
else{
printf("\n\t链表为空,无法反向输出!\n");
}
break;
}
case 7:
{
SortAscend(H);
printf("\n\t成功递增排序带头单链表H!\n");
PrintHLinkList(H);
break;
}
case 8:
{
printf("\n\t请输入需要删除的值的范围a-b:");
scanf("%d %d", &a, &b);
//递归删除:Del_HX_ab(H, a, b);
RangeDelete(H, a, b);
printf("\n\t成功删除a-b之间的结点!\n");
PrintHLinkList(H);
break;
}
case 9:
{
printf("\n\t请开始建立带头单链表A并输入9999结束:");
List_HTailInsert(A);
PrintHLinkList(A);
printf("\n\t请开始建立带头单链表B并输入9999结束:");
List_HTailInsert(B);
PrintHLinkList(B);
List_Same(A, B, H);
printf("\n\t表A和表B的共同结点如下已找到!");
PrintHLinkList(H);
//Search_1st_Common(A, B)
break;
}
case 10:
{
SortAscend(H);
PrintHLinkList(H);
num=Length(H);
printf("\n\t递增输出带头H中各结点数据:");
for(i=1; i<=num; i++){
if(ListDelete(H, 1, a)){
printf("%d ", a);
}
}
free(H->next);
printf("\n\n\t带头H存储空间释放完毕");
PrintHLinkList(H);
break;
}
case 11:
{
printf("\n\t原表A的内容如下");
PrintHLinkList(H);
LNode *s, *r=B;
num=Length(H);
for(i=2; i<=num; i++){
if(ListDelete(H, i, a)){
s=(LNode*)malloc(sizeof(LNode));
s->data=a;
r->next=s;
r=s;
}
}
r->next=NULL;
printf("\n\t原表A序号为奇数的链表保留在A表");
PrintHLinkList(H);
printf("\n\t原表A序号为偶数的链表保留在B表");
PrintHLinkList(B);
break;
}
case 12:
{
Del_Same(H);
printf("\n\t成功去重带头H");
PrintHLinkList(H);
break;
}
case -1:
i=0;
break;
}
}
return 0;
}