DS博客作业02--线性表
1.本周学习总结
1.1思维导图
注:如果图片字太小看不清,可以拖动图片后单独查看,或者按住Ctrl并滑鼠标滚轮放大页面查看。
1.2.谈谈你对线性表的认识及学习体会。
1.对于线性表中链表的学习,从上学期就有所接触,但是那时候并不懂创建和使用链表的真正原理,只能参照课本上一遍遍对照来写线性表的代码;
而又通过这学期更系统的学习,慢慢懂得其真正的创建原理,其链表指向思维对我刚开始来说是非常陌生且难以接受的,但是书上的注释很详细,
只要肯花时间去看,都可以理解和接受的;
2.而线性表的学习也为数据结构打下基础,接下来的学习中也会非常受用;再者对时间复杂度和空间复杂度的掌握还不足,稍微比较复杂的情况就无法
完成对他们的求解,所以在这方面上,我觉得可以通过写代码并计算自己代码时间复杂度和空间复杂度,或者在找例题进行求解来提高这方面的能力;
2.PTA实验作业
2.1.题目1:6-4 顺序表操作集 (20 分)(顺序表)
- 本题要求实现顺序表的操作集。
2.1.1设计思路(伪代码)
- 设计思路:在查找,插入和删除函数中,首先都要考虑到是否为非法位置的情况;在插入函数中,从L->Last起到插入位置,每个元素向后移动一个位置;
在删除函数中,从删除位置后面一个位置起,依次覆盖前一个位置的元素。
List MakeEmpty()//建顺序表
定义链表L
给L动态申请内存
给L->Last赋值-1
再将L返回
Position Find( List L, ElementType X )//找顺序表中X位置
定义整型变量i既做循环变量也做返回值
for i=0 to L->Last do i++ //遍历数组找到X位置
if L->Data[i]==x do 返回i的值
end for
返回ERROR //当遍历过程中没有发现X的存在则返回ERROR
bool Insert( List L, ElementType X, Position P )//将整数X插入P位置
定义整型变量i
判断空间是否已满,若满则进行提示并返回false
再判断参数P是否在非法位置,若是则进行提示并返回false
for i=L->Last+1 to P do i--
L->Data[i]=L->Data[i-1]//将P位置后数据依次往后移动
end for
L->Data[i]=X
L->Last++ //将数据X赋值在P位置,线性表最后一个元素的位置加1
返回true
bool Delete( List L, Position P )//输出P位置的数据
定义整型变量i
判断P是否为合法位置。若是则进行相应提示并返回false
for i=P to i<L->Last do i++
L->Data[i]=L->Data[i+1]//用P+1位置数据将P数据覆盖掉,其后位置元素依次赋值给前一个元素
end for
L->Last-- //线性表最后一个元素的位置减1
返回true
2.1.2代码截图
2.1.3本题PTA提交列表说明。
- Q1:一直出现编译错误;
- A1:没有看清楚题目的代码是用C语言的头文件,而我一直使用c++输入输出等语法来写代码,导致编译错误,更改过来后即可;
- Q2:答案出现错误,但Dvc中编译运行是输出答案和示例相同;
- A2:经过对代码的反复检查发现有两个方面的原因,原因如下:
- 原因一:这题与前几题的在定义结构体上有所不同的是,其他题是定义顺序表的长度,而他是定义最后一个元素的位置Last,这一点要多加注意;
- 原因二:对c的动态申请空间语法不熟悉,导致其语法错误进行了如下就可以了
2.2.题目2:7-2 一元多项式的乘法与加法运算 (20 分)(有序表)
- 设计函数分别求两个一元多项式的乘积与和。
输入格式:
输入分2行,每行分别先给出多项式非零项的个数,再以指数递降方式输入一个多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。
输出格式:
输出分2行,分别以指数递降方式输出乘积多项式以及和多项式非零项的系数和指数。数字间以空格分隔,但结尾不能有多余空格。零多项式应输出0 0。
2.2.1设计思路(伪代码)
- 结构体定义
注:这里重点说明删除,插入,多项式乘积和多项式和的函数
- 插入函数:void ListInsert(LinkList &L,ElemType coef,ElemType index)//有序链表插入元素e
- 设计思路:遍历链表,找到要插入的位置,将元素插入链表中,当指数相同时则将系数相加后即可退出循环,若系数相加为0则进行删除结点操作;
if coef==0 do return;//若系数coefficient为零,无需插入直接返回
定义三个指针p,pre,str
p=L->next;
pre=L; //pre为p之前的结点
定义flag用于判断元素是否插入到链表中,0表示未插入
while p!=NULL do
if p->index==index do
p->coef=p->coef+coef
if p->coef==0 则进行删除操作,将系数coef为0的结点删除掉
end if
/*若不为零则将数据插入到链表中,如下操作*/
str=new LNode;
str->coef=coef;
str->index=index;
str->next=pre->next;
pre->next=str;
flag=1
退出循环
end if
if p->index<index do
/*将数据如上操作插入链表中*/
flag=1
退出循环
end if
/*两个指针同时向后移动一个结点位置*/
end while
if flag==0 do
/*将数据如上操作插入链表中*/
end if
- 删除结点函数:void ListDelete(LinkList &L)//链表删除系数coef为0的结点
- 设计思路:为了删除系数为零结点的函数,遍历链表找到系数为零的结点,删除并释放该结点即可;
//判断链表是否为空,若空则直接返回
定义两个指针分别为p和pre
pre为p前驱结点
while p!=NULL do
/*删除系数为零的结点*/
if p->coef==0 do
pre->next=p->next;
delete p//将p结点删除并释放p结点
退出循环
end if
pre=p;
p=p->next;//两个指针同时后移一个结点
- 多项式乘积函数:LinkList PolynomialProduct(LinkList L1,LinkList L2)//多项式乘积
- 设计思路:嵌套循环,遍历两个链表L1和L2,将两个链表中各个元素相乘并插入重新建立的新链表L中;
定义三个指针p,q,L
给L头指针动态申请内存,且L->=NULL
p指向L1链表,q指向L2链表
/*嵌套循环遍历两个链表,计算链表中各元素的乘积*/
coef=p->coef*q->coef;
if coef==0 do continue //即系数为0直接跳过该次循环
index=p->index+q->index;//多项式相乘,即指数相加
/*调用ListInsert函数将数据coef和index插入到链表L中*/
最后返回链表L
- 多项式和函数:LinkList PolynomialSum(LinkList L1,LinkList L2)//多项式和
- 设计思路:分别遍历两个链表,将其结点依次通过插入函数插入到新建链表中,最后返回新建链表;
定义三个指针p,q,L
给L头指针动态申请内存,且L->=NULL
/*分别遍历两条链表,并运用ListInsert函数将数据存入L链表中*/
for(p=L1->next;p;p=p->next)
{
ListInsert(L,p->coef,p->index);
}
for(q=L2->next;q;q=q->next)
{
ListInsert(L,q->coef,q->index);
}
返回链表L
2.2.2代码截图
2.2.3本题PTA提交列表说明。
- Q1:出现编译错误;
- A1:原因在提交时将编译器类型调成了C(gcc),导致好几次尝试都是编译错误,换成c++即可;
- Q2:前面编译运行了很多次,输出数据与例子中的一样,但是一直都是显示全部错误;
- A2:在空格问题上找原因,将ends改成号后发现并没有末尾有空格现象;后来某个偶然机会(好像有点玄学,但是错误太多次后,只好在自认为的可疑位置进行更改)让我把
原来误以为是输出空格的ends改成了直接的“ ”空格输出,发现pta提交能过一半了;后来查找资料(参照此博客https://www.cnblogs.com/MrLJC/p/3749782.html),ends原来输出的是'\0',
才导致提交一直错误; - Q3:这两个错误一直无法解决;
- A3:即要将相乘或相加后多项式中系数为零的结点删除掉,对此特地写了一个删除函数进行操作,最后成功通过pta检测;
- Q4:当测试数据与例子中的结果相同时,未能完成全对;
- A4:测试数据太少且没有细节的情况说明,所以导致很难发现自己算法的问题,后来想到系数相加等于零的结点需要删除的情况,进行修改后才通过;
2.3.题目3:6-8 jmu-ds-链表倒数第m个数 (20 分)(单链表)
已知一个带有表头节点的单链表,查找链表中倒数第m个位置上的节点。
输入要求:先输入链表结点个数,再输入链表数据,再输入m表示倒数第m个位置。
输出要求,若能找到则输出相应位置,要是输入无效位置,则输出-1。
方法一
2.3.1设计思路(伪代码)
- 设计思路:先将单链表逆置,在遍历链表到第m个结点,即是原来链表的倒数第m个位置上的结点;若遍历链表到到尾指针还未到m位置,即为无效位置。
定义两个指针p,q;
定义整型变量i,用于判断遍历链表时的位置;
将L链表存入p指针
在重构链表L
while p!=NULL do
q=p
p=p->next//两个指针同时向后挪动一个位置
q->next指向L->next;
L->next指向q; //头插法将q指针插入重构的链表L中
end while
i=0;//从零开始计数
L=L->next
while L!=NULL do /*遍历链表找数据*/
i++;//链表每移动一个位置,i计数加一
if i等于m 返回L->data //若i到达m位置,即返回此时结点的数据
L=L->next
end while
返回-1//未能在遍历时返回,即为非法位置;
2.3.2代码截图
方法二
2.3.1设计思路(伪代码)
- 设计思路:两个指针都指向链表的头指针,其中一个指针先遍历到m个结点的位置后,两个指针开始同步进行遍历,
直到先遍历那个结点到达尾节点,则后遍历那个指针指向的结点位置即是要找的位置。
定义两个指针p,q并指向L的头指针;
for i=0 to i<m do i++
p=p->next //遍历p链表,到第m个位置
if p==NULL 返回-1
end for
for p!=NULL to p!=NULL do/*同时遍历两个链表,直到p到达尾指针*/
p=p->next
q=q->next
end for/*如果遍历到尾指针q存在,则为要找的位置*/
if q!=NULL 返回q指针的data值
else 返回-1 //若q也只指向空,即该位置为非法位置
2.3.2代码截图
2.3.3本题PTA提交列表说明。
- Q1:提示位置错误;
- A1:认真看了题目后发现,没有考虑非法位置返回-1值的情况;
- Q2:更改后仍然有错误;
- A2:链表遍历时判断结点是否为空很重要,这将决定遍历时是否为非法位置的情况;
3、阅读代码
3.1 题目
3.2 解题思路
- 方法一:借助辅助数组v_temp存储原表的前p个元素,并把原顺序表中p之后的元素顺序前移,然后将v_temp中暂存的p个数的元素依次放回到原顺序表的后续单元。
- 方法二:现将原来的数组中的n个数全部进行逆置,然后再将逆置后的数组中的前p个数和(n-p)个数分别逆置,即得到的新数组为所要的数组;
3.3 代码截图
方法一
方法二
3.4 学习体会
1.两个算法都很高效,时间复杂度上都为O(n),但方法二在空间复杂度上更小为O(1),其不需要再单独创建顺序表来存储前p个数组元素;
2.方法二相对于方法一显得更新奇,在代码实现上只要完成数组逆置函数,接下来的三次逆置都可以调用来使用,代码显得更加的简洁明了,在
有相关题目上可以学习其算法思想,再者可以学习其函数封装;
3.而方法一则是将数组中一些元素暂时存储到另一个数组中,再调用放回的方法,思路也非常清晰;其在命名上显得更好,值得学习和借鉴;
4.而我自己思考本题,我给出的思路是用到嵌套循环,将数组中的所有元素一次依次向后移一个位置,将数组最后的元素单独存储然后最后赋值给
第一个元素,这样一直重复移p次,即可完成;但是这个思路的时间复杂度为O(n平方),所以比较不好,看到别人思路和代码,觉得在一道题目
上就还有很多需要学习的地方。