作业题目:线性表的基本存储结构的实现与应用
顺序表与单链表是线性表的两种最基本的存储结构,而静态链表是两者的 完美结合,是系统进行动态存储分配的方法基础。线性表的这三种存储结构不但是其他数据结构(如树形结构、图型结构、集合等)存储方法的重要基础,同时本身也有广泛的应用。
作业要求
1. 实现线性表的顺序存储结构(SeqList)和链式存储结构(LinkList)。
2. 在上述存储结构的基础上,分别实现以下算法:
① 删除给定元素的算法。
② 对于已排好序的线性表,删除所有重复元素的算法。
③ 线性表“逆置”算法。
④ 线性表循环左移/右移 k 位的算法。
⑤ 合并两个已排好序的线性表的算法。
3. 选做:(可以不做,供学有余力、有兴趣的同学探索)
① 你能实现线性表的静态链表存储结构吗?
② 你能在静态链表上实现线性表的“逆置”算法吗?
存储结构
#define maxlength 100
#define ElemType int
typedef struct
{
ElemType elem[maxlength];//下标为0的单元不存放线性表中元素
int last;//最后一个元素在数组中位置
}SeqList;
部分基本操作
初始化线性表
读取初始化时元素的个数,依次将各个元素加入到线性表中,当输入元素个数大于等于maxlength时提示"Too many elements !!"。
void Assign_Initial_Value(SeqList *L)
{
int num=maxlength+1,i;
printf("Please input the number of elements:");
while(num>=maxlength)//元素个数最多为maxlength-1
{
scanf("%d",&num);
if(num>=maxlength)
{
printf("Too many elements !!\n");
}
}
printf("Input them:");
for(i=1;i<=num;i++)
{
scanf(" %d",&L->elem[i]);
}
L->last=num;
printf("success !!\n");
}
End(L) 返回最后一个元素的后一位置
给int定义别名position便于区分是否用于表示元素位置。
typedef int position
position End(SeqList L)
{
return (L.last+1);
}
插入(在p位置插入元素x)
课本(数据结构与算法(第五版)高等教育出版社 张岩等主编)中代码使用C++语法,用'&'进行引用传递。如果使用C语言,可以考虑把'&'使用'*'替换,函数调用时,用'&'传址。
插入新元素,对插入元素位置及其后面元素依次后移即可。
不要忘记对L->last进行加1操作。
void Insert (ElemType x ,position p ,SeqList *L)
{
position q;
if(L->last>=maxlength-1)//x插入前表已满
{
printf("List is full !!");
}
else if((p>L->last+1)||p<1)//x插入位置错误
{
printf("position does not exist !!");
}
else
{
for(q=L->last;q>=p;q--)
{
L->elem[q+1]=L->elem[q];//位置p后元素逐个后移
}
L->elem[p]=x;
L->last++;//勿忘
}
}
删除给定位置的元素
勿忘对L->last进行减1操作
void Delete(position p,SeqList *L)
{
position q;
if((p>L->last)||(p<1))
{
printf("position does not exist !!");
}
else
{
for(q=p;q<=(L->last);q++)
{
L->elem[q]=L->elem[q+1];
}
L->last--;
}
}
打印线性表
void Print(SeqList*L)
{
int i;
for(i=1;i<=L->last;i++)
{
printf("%d ",L->elem[i]);
}
printf("\n");
}
查找某个元素(查找某个元素第一次出现的位置)
position Locate(ElemType x,SeqList *L)
{
printf("Input the element you want to find:");
scanf(" %d",&x);
position q;
for(q=1;q<=(L->last);q++)
{
if(L->elem[q]==x)
return q;
}
return (L->last+1);//找不到则返回最后元素后一位置
}
作业中算法
1.删除给定元素的算法
该函数中调用了基本操作中的删除某一位置元素函数
void Delete_Element(ElemType x,SeqList *L)
{
int i=1;
while(i<=L->last)
{
if(L->elem[i]==x)
{
Delete(i,L);
}
else
i++;
}
}
2.删除重复元素算法(对于排好序的数组)
对于排好序的数组,重复元素必定相邻,只需要对相邻元素进行依次判断即可。
void Delete_Duplicate_Orderly(SeqList *L)
{
int i=1;
while(i<=L->last)
{
if(L->elem[i]==L->elem[i+1])
{
Delete(i+1,L);
}
else
{
i++;
}
}
}
3.逆置算法
依次交换第一和最后一个元素,第二和倒数第二个元素...
void Inverse(SeqList *L)
{
int temp,i;
for(i=1;i<=(L->last)/2;i++)
{
temp=L->elem[i];
L->elem[i]=L->elem[End(*L)-i];
L->elem[End(*L)-i]=temp;
}
}
4.循环移动算法
如图,将如下线性表循环左移2(unit)个单位,申请一块临时空间用于存储前2(unit)个元素,再将后面元素依次前移两(unit)个单位,最后再将临时空间中元素放到后面。
循环左移同理。
void Cyclic_Movement(SeqList *L)
{
int judge;
int i,k=0;
int unit=L->last+1;
printf("left or right?(1 for left and 2 for right)\n");
scanf(" %d",&judge);
if(judge!=1&&judge!=2)
{
printf("Please input a proper number!");
scanf(" %d",&judge);
}
int *s,*q;
while(unit>L->last)
{
printf("How many units to move?");
scanf(" %d",&unit);
}
s=(ElemType *)malloc(unit*sizeof(ElemType));//申请临时s存放左移前的元素
q=s;
if(judge==1)//循环左移
{
for(i=1;i<=unit;i++)
{
s[i-1]=L->elem[i];
}
//printf("s[0] is %d",s[0]);
for(i=unit+1;i<=L->last;i++)
{
L->elem[i-unit]=L->elem[i];
}
for(i=End(*L)-unit;i<End(*L);i++)
{
L->elem[i]=s[k];
k++;
}
}
else if(judge==2)//循环右移
{
for(i=L->last;i>=End(*L)-unit;i--)
{
s[k]=L->elem[i];
k++;
}
for(i=L->last-unit;i>=1;i--)
{
L->elem[i+unit]=L->elem[i];
}
k=0;
for(i=1;i<=unit;i++)
{
L->elem[i]=s[k];
k++;
}
}
free(q);
}
5.合并线性表算法
合并两个有序表需要依次比较两个表中元素大小,将较小元素放入到Lc中。然后再对La中元素已经全部放入和Lb中元素已经全部放入分别进行考虑。
void Merge(SeqList *La,SeqList *Lb,SeqList *Lc)
{
int i=0;
position a=1;//标记数组a移动情况
position b=1;//标记数组b移动情况
Lc->last=La->last+Lb->last;
while(a<=La->last||b<=Lb->last)
{
i++;
if(La->elem[a]<=Lb->elem[b])
{
Lc->elem[i]=La->elem[a];
a++;
}
else
{
Lc->elem[i]=Lb->elem[b];
b++;
}
}
if(a==La->last)//La中全部元素已经进入数组
{
while(b<=Lb->last)
{
i++;
Lc->elem[i+1]=Lb->elem[b];
b++;
}
}
else//Lb中全部元素已经进入数组
{
while(a<=La->last)
{
i++;
Lc->elem[i+1]=La->elem[a];
a++;
}
}
for(i=1;i<=Lc->last;i++)
{
printf("%d ",Lc->elem[i]);
}
}
测试结果
Code::Blocks下测试结果:
未将Merge函数写入menu中,单独测试。
不足与反思
1.在函数设计前未规划Menu函数,导致各个函数的形参数量设计十分混乱(在作业中未明确要设计一个菜单界面,主要侧重算法设计,可以不设计menu)。
2.输入提示信息中英文混杂,未提前规划。
3.部分变量名的命名未能体现其具体作用。
4.输入提示信息时而放入算法函数中,时而放入menu函数中,十分混乱。
5.因为怕麻烦未将合并线性表操作加入到menu中。
6.L->last和End(L)函数得到的值相差1,编程过程中将两者混用,最好将两者统一。
7.注释不规范,不能清晰地反映代码用途。
8.部分for循环换成while循环会更加简洁,尤其是在某些对错误输入进行处理的地方。
9.合并算法仅考虑了升序排列,未考虑降序的情况。
PS:小白一枚,C语言基础薄弱,以此博客记录自己学习过程,分享自己的心得,恳请批评指正。
完整代码
#include <stdio.h>
#include <stdlib.h>
#define maxlength 100
#define ElemType int
/*存储结构*/
typedef struct
{
ElemType elem[maxlength];//下标为0的单元不存放线性表中元素
int last;//最后一个元素在数组中位置
}SeqList;
/*打印线性表中所有元素*/
void Print(SeqList*L)
{
int i;
for(i=1;i<=L->last;i++)
{
printf("%d ",L->elem[i]);
}
printf("\n");
}
typedef int position;//便于区分哪些变量表示元素位置
/*函数End()L返回表中最后一个元素的后一位置*/
position End(SeqList L)
{
return (L.last+1);
}
/*把x插入表中位置p处*/
void Insert (ElemType x ,position p ,SeqList *L)
{
position q;
if(L->last>=maxlength-1)//x插入前表已满
{
printf("List is full !!");
}
else if((p>L->last+1)||p<1)//x插入位置错误
{
printf("position does not exist !!");
}
else
{
for(q=L->last;q>=p;q--)
{
L->elem[q+1]=L->elem[q];//位置p后元素逐个后移
}
L->elem[p]=x;
L->last++;//勿忘
}
}
/*删除L中位置p的元素*/
void Delete(position p,SeqList *L)
{
position q;
if((p>L->last)||(p<1))//删除元素位置不存在
{
printf("position does not exist !!");
}
else
{
for(q=p;q<=(L->last);q++)
{
L->elem[q]=L->elem[q+1];
}
L->last--;
}
}
/*删除给定元素*/
void Delete_Element(ElemType x,SeqList *L)
{
int i=1;
while(i<=L->last)
{
if(L->elem[i]==x)
{
Delete(i,L);
}
else
i++;
}
}
/*返回表L中元素x的位置*/
position Locate(ElemType x,SeqList *L)
{
printf("Input the element you want to find:");
scanf(" %d",&x);
position q;
for(q=1;q<=(L->last);q++)
{
if(L->elem[q]==x)
return q;
}
return (L->last+1);//找不到则返回最后元素后一位置
}
/*对已排好序的线性表删除所有重复元素*/
void Delete_Duplicate_Orderly(SeqList *L)
{
int i=1;
while(i<L->last)
{
if(L->elem[i]==L->elem[i+1])
{
Delete(i+1,L);
}
else
{
i++;
}
}
}
/*初始化线性表*/
void Assign_Initial_Value(SeqList *L)
{
int num=maxlength+1,i;
printf("Please input the number of elements:");
while(num>=maxlength)//元素个数最多为maxlength-1
{
scanf("%d",&num);
if(num>=maxlength)
{
printf("Too many elements !!\n");
}
}
printf("Input them:");
for(i=1;i<=num;i++)
{
scanf(" %d",&L->elem[i]);
}
L->last=num;
printf("success !!\n");
}
/*逆置线性表*/
void Inverse(SeqList *L)
{
int temp,i;//temp用于暂时存放交换元素
for(i=1;i<=(L->last)/2;i++)
{
temp=L->elem[i];
L->elem[i]=L->elem[End(*L)-i];
L->elem[End(*L)-i]=temp;
}
}
/*循环移动*/
void Cyclic_Movement(SeqList *L)
{
int judge;
int i,k=0;
int unit=L->last+1;
printf("left or right?(1 for left and 2 for right)\n");
scanf(" %d",&judge);
while(judge!=1&&judge!=2)
{
printf("Please input a proper number!");
scanf(" %d",&judge);
}
int *s,*q;
while(unit>L->last)
{
printf("How many units to move?");
scanf(" %d",&unit);
}
s=(ElemType *)malloc(unit*sizeof(ElemType));//申请临时s存放左移前的元素
q=s;
if(judge==1)//循环左移
{
for(i=1;i<=unit;i++)
{
s[i-1]=L->elem[i];
}
for(i=unit+1;i<=L->last;i++)
{
L->elem[i-unit]=L->elem[i];
}
for(i=End(*L)-unit;i<End(*L);i++)
{
L->elem[i]=s[k];
k++;
}
}
else//循环右移
{
for(i=L->last;i>=End(*L)-unit;i--)
{
s[k]=L->elem[i];
k++;
}
for(i=L->last-unit;i>=1;i--)
{
L->elem[i+unit]=L->elem[i];
}
k=0;
for(i=1;i<=unit;i++)
{
L->elem[i]=s[k];
k++;
}
}
free(q);
}
/*逆置线性表*/
void Merge(SeqList *La,SeqList *Lb,SeqList *Lc)
{
int i=0;
position a=1;//标记数组a移动情况
position b=1;//标记数组b移动情况
Lc->last=La->last+Lb->last;
while(a<=La->last||b<=Lb->last)
{
i++;
if(La->elem[a]<=Lb->elem[b])
{
Lc->elem[i]=La->elem[a];
a++;
}
else
{
Lc->elem[i]=Lb->elem[b];
b++;
}
}
if(a==La->last)//La中全部元素已经进入数组
{
while(b<=Lb->last)
{
i++;
Lc->elem[i+1]=Lb->elem[b];
b++;
}
}
else//Lb中全部元素已经进入数组
{
while(a<=La->last)
{
i++;
Lc->elem[i+1]=La->elem[a];
a++;
}
}
for(i=1;i<=Lc->last;i++)
{
printf("%d ",Lc->elem[i]);
}
}
/*菜单*/
void Menu(SeqList*L)
{
printf("************************菜单************************\n");
printf("*1.初始化一个数组************2.删除某个位置的元素***\n");
printf("*3.删除给定元素**************4.删除所有重复元素*****\n");
printf("*5.逆置线性表****************6.循环左移/右移k个单位*\n");
printf("*7.查找元素位置**************8.插入某一元素*********\n");
printf("*9.打印线性表***************10.退出*****************\n");
int i=0;
int choose=0;
int x=0;
position p=0;
for(i=0;;i++)
{
printf("你想要的操作是?");
scanf(" %d",&choose);
switch(choose)
{
case 1:Assign_Initial_Value(L);break;
case 2:printf("Input the position you want to delete:");
scanf(" %d",&p);
Delete(p,L);
Print(L);
break;
case 3:printf("Input the element you want to delete:");
scanf(" %d",&x);
Delete_Element(x,L);
Print(L);
break;
case 4:Delete_Duplicate_Orderly(L);
Print(L);
break;
case 5:Inverse(L);
Print(L);
break;
case 6:Cyclic_Movement(L);
Print(L);
break;
case 7:printf("Input the element you want to search\n");
scanf(" %d",&x);
printf("poisition is %d",Locate(x,L));
break;
case 8:printf("where to insert the element?\n");
scanf(" %d",&p);
printf("the element is ? \n");
scanf(" %d",&x);
Insert(x,p,L);
Print(L);
break;
case 9:Print(L);
case 10:exit(0);
default:printf("Wrong!!Please input a proper number!!\n");
}
}
}
int main()
{
SeqList L;
Menu(&L);
/*测试Merge算法代码*/
/*SeqList La;
Assign_Initial_Value(&La);
SeqList Lb;
Assign_Initial_Value(&Lb);
SeqList Lc;
Merge(&La,&Lb,&Lc);*/
return 0;
}