因为第一次数据结构阶段考手写代码又想规范写又写不出规范的可读性强的代码,让我第一次意识到自己应该在这方面下一些功夫。这篇文章是记录自己把书上写的算法打到电脑上遇到的问题。
将书中代码复刻如下:
#define MAXSIZE 100//顺序表可能达到的最大长度
typedef struct
{
ElemType *elem;//储存空间的基地址
int length;//当前长度
}SqList;//顺序表的结构类型为SqList
这敲到电脑上当然不能直接运行啦,哈哈,ElemType只是任意的元素类型,自己改一下就好。我这里就直接用int了。
改后如下:
#define MAXSIZE 100//顺序表可能达到的最大长度
typedef struct
{
int *elem;//储存空间的基地址
int length;//当前长度
}SqList;//顺序表的结构类型为SqList
将书中代码复刻如下:
Status InitList(SqList &L)
{//构造一个空的顺序表L
L.elem=new ElemType[MAXSIZE];//为顺序表分配一个大小为MAXSIZE的数组空间
if(!L.elem)
exit(overflow_error);//存储分配失败退出
L.length=0;//空表长度为0
return OK;
}
这里遇到了几个问题:
第一,c语言是没有new的用法的
第二,c语言中没有按引用传参&,应改为地址引用*
第三,“.”和“->”是有区别的,如果L是一个结构体指针就用“->”,如果是正常结构体就用“.”
第四,exit(OVERFLOW)这个OVERFLOW应该是得提前定义个数值的。应该有很多小伙伴不知道exit跟return有啥区别,我就附在这后面了,exit用于在程序运行的过程中随时结束程序,return( )是当前函数的返回,直接说就是exit运行整个程序就结束了,return运行return所在函数结束。
改后如下:
#define OVERFLOW 0
#define OK 1
int InitList(SqList *L)
{//构造一个空的顺序表L
L->elem=(int *)malloc(MAXSIZE*sizeof(int));//为顺序表分配一个大小为MAXSIZE的数组空间
if(!L->elem)
exit(OVERFLOW);//存储分配失败退出
L->length=0;//空表长度为0
return OK;
}
将书中代码复刻如下:
Status GetElem(SqList L,int i,int &e)
{
if(i<1||i>L.length)return ERROR;//判断i值是否合理
e=L.elem[i-1];//elem[i-1]单元存储第i个数据元素
return OK;
}
这里基本上没咋改,就把ERROR换成了OVERFLOW省得再define一次,其实直接return 0也一样。
改后如下:
int GetElem(SqList L,int i,int *e)
{
if(i<1||i>L.length)return OVERFLOW;//判断i值是否合理
*e=L.elem[i-1];//elem[i-1]单元存储第i个数据元素
return OK;
}
将书中代码复刻如下:
int LocateElem(SqList L,ElemType e)
{//在顺序表L中查找值为e的数据元素,返回其序号
for(i=0;i<L.length;i++)
if(L.elem[i]==e)return i+1;//查找成功,返回序号i+1
return 0;//查找失败,返回0
}
这里就 i 没定义
改后如下:
int LocateElem(SqList L,int e)
{//在顺序表L中查找值为e的数据元素,返回其序号
int i;
for(i=0;i<L.length;i++)
if(L.elem[i]==e)return i+1;//查找成功,返回序号i+1
return 0;//查找失败,返回0
}
将书中代码复刻如下:
Status ListInsert(SqList &L,int i,ElemType e)
{//在顺序表L中第i个位置插入新的元素e,i的值的合法范围是1<=i<=L.length+1
if((i<1)||(i>L.length+1))return ERROR;//i值不合法
if(L.length==MAXSIZE)return ERROR;//当前存储空间已满
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j];//插入位置之后的元素后移
L.elem[i-1]=e;//将新元素e放入第i个位置
++L.length;//表长加1
return OK;
}
改后如下:
int ListInsert(SqList *L,int i,int e)
{//在顺序表L中第i个位置插入新的元素e,i的值的合法范围是1<=i<=L.length+1
if((i<1)||(i>L->length+1))return OVERFLOW;//i值不合法
if(L->length==MAXSIZE)return OVERFLOW;//当前存储空间已满
int j;
for(j=L->length-1;j>=i-1;j--)
L->elem[j+1]=L->elem[j];//插入位置之后的元素后移
L->elem[i-1]=e;//将新元素e放入第i个位置
++L->length;//表长加1
return OK;
}
Status ListDelete(SqList &L,int i)
{//在顺序表L中删除第i个元素,i值的合法范围是1<=i<=L.length
if((i<1)||(i>L.length))return ERROR;//i值不合法
for(j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j];//被删除元素之后的元素前移
--L.length;//表长减1
return OK;
}
改后如下:
int ListDelete(SqList *L,int i)
{//在顺序表L中删除第i个元素,i值的合法范围是1<=i<=L.length
if((i<1)||(i>L->length))return OVERFLOW;//i值不合法
int j;
for(j=i;j<=L->length-1;j++)
L->elem[j-1]=L->elem[j];//被删除元素之后的元素前移
--L->length;//表长减1
return OK;
}
主函数传参如下:
注:主函数我用的&大家不要混淆,这里只是单纯的表示取地址操作。
int main()
{
int i,e;
SqList L;
InitList(&L);
GetElem(L,i,&e);
LocateElem(L,e);
ListInsert(&L,i,e);
ListDelete(&L,i);
return 0;
}
将书上代码复刻如下:
typedef struct LNode
{
ElemType data;//结点的数据域
struct LNode *next;//结点的指针域
}LNode,*LinkList;//LinkList为指向结构体LNode的指针类型
改后如下:
typedef struct LNode
{
int data;//结点的数据域
struct LNode *next;//结点的指针域
}LNode,*LinkList;//LinkList为指向结构体LNode的指针类型
将书上代码复刻如下:
Status InitList(LinkList &L)
{//构造一个空的单链表L
L=new LNode;//生成新结点作为头结点,用头指针L指向头结点
L->next=NULL;//头结点的指针域置空
return OK;
}
前面有提到c语言不能用引用传参&和new,这大家应该能理解了,然后为什么不加星呢?因为LinkList为指向结构体LNode的指针类型,用LNode*也是可以的。
改后如下:
int InitList(LinkList L)
{//构造一个空的单链表L
L=(LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点
L->next=NULL;//头结点的指针域置空
return OK;
}
将书中代码复刻如下:
Status GetElem(LinkList L,int i,ElemType &e)
{//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
p=L->next;j=1;//初始化,p指向首元结点,计数器j初值为1
while(p&&j<i)//顺链域向后扫描,直到p为空或p指向第i个元素
{
p=p->next;//p指向下一个结点
++j;//计数器j相应加1
}
if(!p||j>i)return ERROR;//i值不合法i>n或i<=0
e=p->data;//取第i个结点的数据域
return OK;
}
改后如下:
int GetElem(LinkList L,int i,int *e)
{//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
LinkList p;
int j;
p=L->next;j=1;//初始化,p指向首元结点,计数器j初值为1
while(p&&j<i)//顺链域向后扫描,直到p为空或p指向第i个元素
{
p=p->next;//p指向下一个结点
++j;//计数器j相应加1
}
if(!p||j>i)return ERROR;//i值不合法i>n或i<=0
e=p->data;//取第i个结点的数据域
return OK;
}
将书上代码复刻如下:
LNode* LocateElem(LinkList L,ElemType e)
{//在带头结点的单链表L中查找值为e的元素
p=L->next;//初始化,p指向首元结点
while(p&&p->data!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
p=p->next;//p指向下一个结点
return p;//查找成功则返回值为e的结点地址p,查找失败p为NULL
}
改后如下:
LNode* LocateElem(LinkList L,int e)
{//在带头结点的单链表L中查找值为e的元素
LinkList p;
p=L->next;//初始化,p指向首元结点
while(p&&p->data!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
p=p->next;//p指向下一个结点
return p;//查找成功则返回值为e的结点地址p,查找失败p为NULL
}
将书中代码复刻如下:
Status ListInsert(LinkList &L,int i,ElemType e)
{//在带头结点的单链表L中第i个位置插入值为e的新结点
p=L;j=0;
while(p&&(j<i-1))
{
p=p->next;++j;//查找第i-1个结点,p指向该结点
}
if(!p||j>i-1)return ERROR;//i>n+1或者i<1
s=new LNode;
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
改后如下:
int ListInsert(LinkList L,int i,int e)
{//在带头结点的单链表L中第i个位置插入值为e的新结点
LinkList p;
int j;
p=L;j=0;
while(p&&(j<i-1))
{
p=p->next;++j;//查找第i-1个结点,p指向该结点
}
if(!p||j>i-1)return ERROR;//i>n+1或者i<1
LinkList s;
s=(LNode*)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return OK;
}
将书中代码复刻如下:
Status ListDelete(LinkList &L,int i)
{//带头结点的单链表L中,删除第i个元素
p=L;j=0;
while((p->next)&&(j<i-1))//查找第i-1个结点,p指向该结点
{
p=p->next;++j;
}
if(!(p->next)||(j>i-1))return ERROR;//当i>n或i<1时,删除位置不合理
q=p->next;//临时保存被删结点的地址以备释放
p->next=q->next;//改变删除结点前驱结点的指针域
delete q;//释放删除结点的空间
return OK;
}
改后如下:
注:C语言没有delete,这是C++中的释放空间的方法,C语言中是用free()函数释放空间的,free()在stdlib.h中声明
int ListDelete(LinkList L,int i)
{//带头结点的单链表L中,删除第i个元素
LinkList p,q;
int j;
p=L;j=0;
while((p->next)&&(j<i-1))//查找第i-1个结点,p指向该结点
{
p=p->next;++j;
}
if(!(p->next)||(j>i-1))return ERROR;//当i>n或i<1时,删除位置不合理
q=p->next;//临时保存被删结点的地址以备释放
p->next=q->next;//改变删除结点前驱结点的指针域
free(q);//释放删除结点的空间
return OK;
}
将书中代码复刻如下:
void CreateList_H(LinkList &L,int n)
{//逆位序输入n个元素的值,建立带表头结点的单链表L
L=new LNode;
L->next=NULL;//先建立一个带头结点的空链表
for(i=0;i<n;i++)
{
p=new LNode;
scanf("%d",&p->data);
p->next=L->next;L->next=p;//将新结点插入到头结点之后
}
}
改后如下:
void CreateList_H(LinkList L,int n)
{//逆位序输入n个元素的值,建立带表头结点的单链表L
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;//先建立一个带头结点的空链表
int i;
for(i=0;i<n;i++)
{
LinkList p;
p=(LNode*)malloc(sizeof(LNode));
scanf("%d",&p->data);
p->next=L->next;L->next=p;//将新结点插入到头结点之后
}
}
将书中代码复刻如下:
void CreateList_R(LinkList &L,int n)
{//正位序输入n个元素的值,建立带头结点的单链表L
L=new LNode;
L->next=NULL;//先建立一个带头结点的空链表
r=L;//尾指针r指向头结点
for(i=0;i<n;i++)
{
p=new LNode;//生成新结点
scanf("%d",&p->data);
p->next=NULL;r->next=p;//将新结点插入到尾结点之后
r=p;//指向新的尾结点
}
}
改后如下:
void CreateList_R(LinkList L,int n)
{//正位序输入n个元素的值,建立带头结点的单链表L
L=(LNode*)malloc(sizeof(LNode));
L->next=NULL;//先建立一个带头结点的空链表
LinkList r;
r=L;//尾指针r指向头结点
int i;
for(i=0;i<n;i++)
{
LinkList p;
p=(LNode*)malloc(sizeof(LNode));//生成新结点
scanf("%d",&p->data);
p->next=NULL;r->next=p;//将新结点插入到尾结点之后
r=p;//指向新的尾结点
}
}
int ListLength(LNode L)
{
int i=0;
LinkList p;
p=L.next;
while(p)
{
i++;
p=p->next;
}
return i;
}
void MergeList(LinkList LA,LNode LB)
{
int m,n,i,e;
m=ListLength(*LA);n=ListLength(LB);//求线性表的长度
for(i=1;i<=n;i++)
{
GetElem(&LB,i,e);//取LB中第i个数据元素赋给e
if(!LocateElem(LA,e))//LA中不存在和e相同的数据元素
ListInsert(LA,++m,e);//将e插在LA的最后
}
}
void MergeList_Sq(SqList LA,SqList LB,SqList *LC)
{//已知顺序有序表LA和LB的元素按值非递减排列
//归并LA和LB得到新的顺序有序表LC,LC的元素也按值非递减排列
LC->length=LA.length+LB.length;//新表长度为待合并的两表长度之和
LC->elem=(int *)malloc(LC->length*sizeof(int));//为合并后的新表分配一个数组空间
int *pa,*pb,*pc,*pa_last,*pb_last;
pc=LC->elem;//指针pc指向新表的第一个元素
pa=LA.elem;pb=LB.elem;//指针pa,pb的初值分别指向两个表的第一个元素
pa_last=LA.elem+LA.length-1;//指针pa_last指向LA的最后一个元素
pb_last=LB.elem+LB.length-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的最后
}
void MergeList_L(LinkList LA,LinkList LB,LinkList LC)
{//已知单链表LA和LB的元素按值非递减排列
//归并LA和LB得到新的单链表LC,LC的元素也按值非递减排列
LinkList pa,pb,pc;
pa=LA->next;pb=LB->next;//pa,pb的初值分别指向两个表的第一个元素
LC=LA;//LA的头结点作为LC的头结点
pc=LC;//pc的初值指向LC的头结点
while(pa&&pb)
{//LA和LB均未达表尾,依此“摘取”两表中值较小的结点插入到LC的最后
if(pa->data<=pb->data)//“摘取”pa所指结点
{
pc->next=pa;
pc=pa;
pa=pa->next;
}
else//“摘取”pb所指结点
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc->next=pa?pa:pb;//将非空表的剩余段插入到pc所指的结点之后
free(LB);
}
第一次写博客,感觉还不错,对知识的一个熟悉有很大帮助。如果大家也是学生,推荐把书上的算法描述自己照着打一遍,当然打完一遍只是对这些算法有个初步的理解,自己还得多看看。另外,如果有什么建议或者我有什么错误随时欢迎各位大佬批评指正,非常感谢。