实验3、链表的基本操作
(1)实验目的
通过该实验,深入理解链表的逻辑结构、物理结构等概念,掌握链表基本操作的编程实现,熟练掌握C语言中指针的操作。和实验3对比,掌握线性结构两种不同存储方式的区别。
(2)实验内容
编程实现链表下教材第二章定义的线性表的基本操作,最好用菜单形式对应各个操作,使其编程一个完整的小软件。
(3)参考界面
(4)验收/测试用例
没有初始化前进行其他操作,程序是否能控制住;
初始化一个顺序表;
插入数据(位置, 数据),要测插入位置不合法的情况(0,1)、(2,1),正确插入4个数据(1,2)、(1,1)、(3,3);
显示顺序表中的数据,屏幕输出1, 2, 3;
判空,屏幕输出顺便表非空;
顺便表长度,屏幕输出3;
获取指定位置元素,要测指定位置在【1,3】范围之外的情况和之内的情况;
定位,输入:4, 输出:不存在,输入2,输出位置为2;
求直接前驱,要测求第一个元素的前驱、不存在顺序表中的元素的直接前驱,其他元素的直接前驱;
求直接后继,要测最后一个元素的后继、不存在顺序表中的元素的直接后继,其他元素的直接后继;
删除,要测位置在【1,3】范围之外的情况和之内的情况;
清空操作后再测长度;
销毁顺序表
个人感觉这个实验是所有数据结构里面最难的一个实验,主要难在函数的构造和里面用到了大量的指针,初学数据结构感觉指针就是一生之敌。有的时候很容易忘了加*号。一定要仔细检查有没有添加。不然就是疯狂报错,如下是我第一次将程序完整写出来的时候:
不用怀疑了,电脑下方的黄皮桌子正是华苑桌子经典皮肤
然后就会因为找不到错误,而陷入无尽的找bug环节
这个程序我写了整整一个星期,先搞懂链表基本操作的理论原理,然后再去读代码看怎么去实现它,坚持住,过来链表实验后,你会发现之后的栈、队列和链表都不在一个层次,程序可以运行!这个代码写出来实属不易,如果帮助到你请点一个免费的赞👍吧
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
#define ok 1
#define error -4
#define false -1
#define overflow -2
#define yuejie -3
typedef int Status;
typedef int ElemType;
typedef struct Lnode{
ElemType data;
struct Lnode *next;
}Lnode,*LinkList;
//初始化函数
Status InitList(LinkList &L)
{
L = (LinkList)malloc(sizeof(Lnode));//申请空间
if(L == 0)
{
exit(overflow);
}
L->next = NULL;//建立一个头结点
return ok;
}
//销毁链表
Status DestoryList(LinkList &L)
{
if(L == NULL)
{
return error;
}
Lnode *p;
p = L; //可以把P = L,放到循环中去
while(L->next != NULL)
{
L = L->next;
free(p);
p = L;
}
L = NULL;
return ok;
}
//清空线性表
Status ClearList(LinkList &L)
{
if(L == NULL)
{
return error;
}
Lnode *p;
Lnode *q;
p = L->next;
//q = p -> next;
while(q == NULL)
{
q = p -> next;
free(p);
p = q;//每一次循环过后,pq都在同一个位置,当q == NULL时, 其上一个循环中pq已经到达最后一个元素了
}
L->next = NULL;//将头结点置空
return ok;
}
//链表长度
//Status ListLength(LinkList &L,int *count)
Status ListLength(LinkList &L)
{
int count;
if(L == NULL)
{
return error;
}
count = 0;
//Londe *p;
LinkList p;
p = L->next;
while(p != NULL)
{
p = p->next;
count++;
}
return count;
}
//指定位置的元素值
Status LocalElem(LinkList &L,int i,int *e)
{
if(L == NULL)
{
return error;//链表未初始化
}
int j = 1;
//Londe *p;
LinkList p;
p = L->next;
while((p != NULL)&&(j < i))
{
p = p->next;
j++;
}
if((p == NULL)||(j > i))
{
return false;//链表中不存在该位置
}
*e = p->data;
return ok;
}
//链表已存在元素的位序
Status Local_Elem(LinkList &L,int e,int *i)
{
if(L == NULL)
{
return error;//链表未初始化
}
int j = 1;
//Londe *p;
LinkList p;
p = L->next;
while((p != NULL)&&(p->data != e))
{
p = p->next;
j++;
}
if(p != NULL)
{
*i = j;
return ok;
}
else
{
return false;//链表中没有该元素e
}
}
// 求输入元素的直接前驱
Status PriorElem(LinkList &L,int e,int *pri_e)
{
if(L == NULL)
{
return error;//链表未初始化
}
/*Londe *p;
Londe *q;*/
LinkList p;
LinkList q;
p = L->next;
while(p != NULL)
{
//p = p->next;
if(p->data == e)//只在第一次循环中起作用,其他循环中结果均与下方的q循环结果相同不再起作用
{
return false;
}
if(p->next != NULL)
{
q = p->next;
if(q->data == e)//如果不加判断可能会导致 q指向空值,直接调用q->data就会导致程序崩溃
{
*pri_e = p->data;
return ok;
}
}
p = p->next;
}
if(p == NULL)
{
return yuejie;//所求元素不在该链表中
}
}
//求输入元素的直接后继
Status nextElem(LinkList &L,int e,int *next_e)
{
if(L == NULL)
{
return error;//链表未初始化
}
//Londe *p;
LinkList p;
p = L;
while(p != NULL)
{
p = p->next;
if(p != NULL)
{
if(p->data == e)
{
if(p->next != NULL)
{
*next_e = p->next->data;
return ok;
}
else
{
return false;//最后一位元素没有后继
}
}
}
else{
break;
}
}
if(p == NULL)
{
return yuejie;//所求元素不在该链表中
}
}
//在第i个位置插入一个元素
Status InserElem(LinkList &L,int i,int e)
{
if(L == NULL)
{
return error;//链表未初始化
}
/*Londe *p;
Londe *s;*/
LinkList p;
LinkList s;
int j = 0;
p = L;
while((p != NULL)&&(j < i - 1))//遍历的目的是为了找到第i-1个位置便于插入
{
p = p->next;
j++;
}
if((p == NULL)||(j > i - 1))
{
return false;//i值不合理,大于链表长度加一或者小于1
}
s = (LinkList)malloc(sizeof(Lnode));
s->data = e;
s->next = p->next;
p->next = s;
return ok;
}
//删除第i个元素
Status DeleteElem(LinkList &L,int i)
{
if(L == NULL)
{
return error;//链表未初始化
}
/*Londe *p;
Londe *q;*/
LinkList p;
LinkList q;
int j = 0;
p = L;
while((p->next != NULL)&&(j < i - 1))//让 p->next != NULL保证p后面还有元素可以删除
{
p = p->next;
j++;
}
if((p->next == NULL)||(j > i -1))
{
return false;//i值不合法,或者p已经指向最后一个元素已经无法再调用指针删除一个元素
}
q = p->next;
p->next = q->next;
//free(q);
return ok;
}
//输出有的链表元素
Status VisitElem(LinkList L)
{
if(L == NULL)
{
return error;//链表未初始化
}
//Londe *p;
LinkList p;
p = L->next;
while(p != NULL)
{
cout<<p->data<<" ";
p = p->next;
}
return ok;
}
//初始化并用头插法(或尾插法)输入元素
Status CreatList(LinkList &L,int n)
{
if(L == NULL)
{
return error;//链表未初始化
}
//Londe *p;
LinkList p;
int i;
for(i = n;i > 0;i--)
{
p = (LinkList)malloc(sizeof(Lnode));
cin>>p->data;
p->next = L->next;
L->next = p;
}
return ok;
}
//实现单链表的逆序存放
Status NiList(LinkList &L,int n)
{
int i;
if(L == NULL)
{
return error;//链表未初始化
}
Lnode *p;
Lnode *q;
LinkList L1;
InitList(L1);
p = L;
for(i = n;i > 0;i--)
{
p = p->next;
q = (LinkList)malloc(sizeof(Lnode));
q->data = p->data;
q->next = L1->next;
L1->next = q;
}
VisitElem(L1);
return ok;
}
//两个非递减有序表的去重合并
void MergeList(LinkList &LA,LinkList &LB,LinkList &LC)
{
Lnode *pa;
Lnode *pb;
Lnode *pc;
pa = LA->next;
pb = LB->next;
LC = LA;
pc = LC;
while((pa != NULL)&&(pb != NULL))
{
if((pa->data) <= (pb->data))
{
pc->next = pa;
pc = pa;
pa = pa->next;
}
else
{
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
if(pa != NULL)
{
pc->next = pa;
}
if(pb != NULL)
{
pc->next = pb;
}
free(LB);
//链表查重功能
Lnode *p;
Lnode *q;
Lnode *r;//保留删除后指针p的地址
int count1 = 1;
//int i = false;
p = LC -> next;
q = LC -> next;
while(p != NULL)//p遍历LC
{
while(q != p)//p = q 时不查重,第一次循环不进入二层循环,剩下的查重q运行到p的前一个元素
{
if(p -> data == q -> data)
{
r = p;//r保留p的地址
DeleteElem(LC,count1);//相同则删除该元素p -> data退出内层循环
p = r;//把r的地址重新传给p
break;
}
q = q -> next;//否则q运行到p的前一个元素
}
p = p -> next;//每一次循环p前移
count1++;
q = LC -> next;//q 回到 第一个结点位置再重新运行到p的前一个元素进行查重
/*if(i == false)
{
p = p -> next;//每一次循环p前移
count1++;
q = LC -> next;//q 回到 第一个结点位置再重新运行到p的前一个元素进行查重
}
if(i == ok)
{
p = r->next;//r在删除之前保留p的地址
count1++;
q = LC -> next;//q 回到 第一个结点位置再重新运行到p的前一个元素进行查重
}
} */
}
VisitElem(LC);
}
//主函数输出
int main()
{
int i,j,n,e,pri_e,next_e,A,B,C;
int m;
//int count;
LinkList L;
LinkList LA;
LinkList LB;
LinkList LC;
L = NULL;
cout<<"*********************"<<endl;
cout<<"h e n u - 于 笨 笨"<<endl;
cout<<"1.初始化链表"<<endl;
cout<<"2.销毁链表"<<endl;
cout<<"3.清空链表"<<endl;
cout<<"4.求链表长度"<<endl;
cout<<"5.指定位置的元素值"<<endl;
cout<<"6.链表已存在元素的位序"<<endl;
cout<<"7.求输入元素的直接前驱"<<endl;
cout<<"8.求输入元素的直接后继"<<endl;
cout<<"9.在第i个位置插入一个元素"<<endl;
cout<<"10.删除第i个元素"<<endl;
cout<<"11.输出有的链表元素"<<endl;
cout<<"12.头插法创建链表"<<endl;
cout<<"13.逆序输出链表"<<endl;
cout<<"14.初始化线性表LA,LB,LC"<<endl;
cout<<"15.插入构建线性表LA"<<endl;
cout<<"16.插入构建线性表LB"<<endl;
cout<<"17.合并两个非递减链表"<<endl;
cout<<"输入一个负数退出"<<endl;
cout<<"**********************"<<endl;
do{
//cout<<"输出空值:"<<NULL<<endl;
cout<<"请输入你的选择:"<<endl;
cin>>n;
switch(n)
{
case 1:
m = InitList(L);
if(m == overflow)
{
cout<<"链表初始化失败!"<<endl;
}
if(m == ok)
{
cout<<"链表初始化成功!"<<endl;
}
break;
case 2:
m = DestoryList(L);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"线性表销毁成功!"<<endl;
}
break;
case 3:
m = ClearList(L);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"线性表清空成功!"<<endl;
}
break;
case 4:
m = ListLength(L);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
//if(m == ok)
else
{
cout<<"线性表的长度为:"<<m<<endl;
}
break;
case 5:
cout<<"请输入你要查询的位置:"<<endl;
cin>>i;
m = LocalElem(L,i,&e);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == false)
{
cout<<"链表中不存在该位置!"<<endl;
//cout<<"链表中不存在该元素!"<<endl;
}
if(m == ok)
{
cout<<"该位置的元素为:"<<e<<endl;
}
break;
case 6:
cout<<"请输入你要查询的元素:"<<endl;
cin>>e;
m = Local_Elem(L,e,&i);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == false)
{
cout<<"链表中不存在该元素!"<<endl;
}
if(m == ok)
{
cout<<"该元素的位置为:"<<i<<endl;
}
break;
case 7:
cout<<"请输入你要求前驱的元素:"<<endl;
cin>>e;
m = PriorElem(L,e,&pri_e);
cout<<"输出此时的m值:"<<m<<endl;
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
else if(m == false)
{
cout<<"该元素没有前驱!"<<endl;
}
else if(m == ok)
{
cout<<"该元素的前驱为:"<<pri_e<<endl;
}
//if(m == yuejie)
else
{
cout<<"链表中不存在该元素!"<<endl;
}
break;
case 8:
cout<<"请输入你要求后继的元素:"<<endl;
cin>>e;
m = nextElem(L,e,&next_e);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == false)
{
cout<<"该元素没有后继!"<<endl;
}
if(m == yuejie)
{
cout<<"链表中不存在该元素!"<<endl;
}
if(m == ok)
{
cout<<"该元素的后继为:"<<next_e<<endl;
}
break;
case 9:
cout<<"请输入要插入的位置:"<<endl;
cin>>i;
cout<<"请输入要插入的元素:"<<endl;
cin>>e;
m = InserElem(L,i,e);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == false)
{
cout<<"在链表中的插入位置不合理!"<<endl;
}
if(m == ok)
{
cout<<"该元素插入成功!"<<endl;
}
break;
case 10:
cout<<"请输入要删除元素的位置:"<<endl;
cin>>i;
m = DeleteElem(L,i);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == false)
{
cout<<"在链表中的删除位置不合理!"<<endl;
}
if(m == ok)
{
cout<<"该元素删除成功!"<<endl;
}
break;
case 11:
m = VisitElem(L);//函数里面有输出会不会干扰return的返回??? 不行就直接调用函数不再进行if语句判断!!
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"该链表的元素为:"<<endl;
VisitElem(L);
}
cout<<endl;
break;
case 12:
cout<<"请输入要创建链表中元素的个数:"<<endl;
cin>>n;
m = CreatList(L,n);//
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"链表创建成功"<<endl;
}
break;
case 13:
n = ListLength(L);
cout<<"输出此时链表的长度为:"<<n<<endl;
m = NiList(L,n);
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"该链表逆序后的元素为:"<<endl;
NiList(L,n);
}
cout<<endl;
break;
case 14:
A = InitList(LA);
if(A == overflow)
{
cout<<"链表初始化失败!"<<endl;
}
if(A == ok)
{
cout<<"链表初始化成功!"<<endl;
}
B = InitList(LB);
if(B == overflow)
{
cout<<"链表初始化失败!"<<endl;
}
if(B == ok)
{
cout<<"链表初始化成功!"<<endl;
}
C = InitList(LC);
if(C == overflow)
{
cout<<"链表初始化失败!"<<endl;
}
if(C == ok)
{
cout<<"链表初始化成功!"<<endl;
}
break;
case 15:
cout<<"请输入要创建链表中元素的个数:"<<endl;
cin>>n;
m = CreatList(LA,n);//
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"链表创建成功"<<endl;
}
break;
case 16:
cout<<"请输入要创建链表中元素的个数:"<<endl;
cin>>n;
m = CreatList(LB,n);//
if(m == error)
{
cout<<"请先初始化链表!"<<endl;
}
if(m == ok)
{
cout<<"链表创建成功"<<endl;
}
break;
case 17:
cout<<"输出合并后的链表:"<<endl;
MergeList(LA,LB,LC);
cout<<endl;
break;
default:
cout<<"请输入1~17内的数"<<endl;
break;
}
}while(n > 0);
}
下面手把手展示一下程序中数据如何输入的问题:
这个代码写出来实属不易,如果帮助到你请点一个免费的赞👍吧