1.本周学习总结
1.1思维导图
1.2谈谈你对线性表的认识和学习体会
学线性表这一章的时候,我感觉吧就像是在学加上了结构体可以变化长度的数组和链表 。感觉就是应该和上学期的数组和链表差不多,结果做起PTA的时候,一堆堆的错误。然后上级考的时候,一遍遍的段错误到绝望……然后就感觉自己跟上学期比起来松懈了不少,最基本的代码问题又开始
2.PTA实验作业
2.1题目1:6-3 jmu-ds- 顺序表删除重复元素
设计一个算法,从顺序表中删除重复的元素,并使剩余元素间的相对次序保存不变。
- 输入格式: 第一行输入顺序表长度。 第二行输入顺序表数据元素。中间空格隔开。
- 输出格式:数据之间空格隔开,最后一项尾部不带空格。
- 输出删除重复元素后的顺序表。
2.1.1设计思路(伪代码)
因为顺序表中的数均为正数,构造一个哈希数组赋初值为0,遍历顺序表,将L->data[i]的值作为哈希数组的下标统计顺序表中各个数出现的次数,将哈希数组值为一的数存入重构顺序表中。
- 伪代码
定义整型变量i、j=0,i为原顺序表的下标,j为重构顺序表的下标
定义整型哈希数组hash[100],并赋初值为0
遍历顺序表
将L->data[i]作为哈希数组的下标,统计其出现的次数
if L->data[i]出现的次数为一次 then
将L->data[i]的值赋给L->data[j++] //重构顺序表
将j的值作为顺序表长度赋给L->length
2.1.2代码截图
-
void CreateSqList(List &L,int a[],int n); //创建顺序表
-
void DispSqList(List L);//输出顺序表
-
void DelSameNode(List &L) ;//删除顺序表重复元素
2.1.3本题PTA提交列表说明
Q1:一开始部分正确是因为忽略了空表及全部删除后的输出,然后又认真的看了一下题目,在输出的函数部分用if语句分出空顺序表的情况;
Q2:后来两个间隔长时间的答案正确的提交改进了算法,将时间复杂度为O(n3)改进成了O(n);
A2:一开始做这道题的时候用的是笨办法,用两层for循环查找重复元素,找到后再来一层循环将顺序表往前挪,虽然做法简单易懂,但是时间复杂度特别高,当时抱着做出结果就好的心态,做完就没去管它了。隔了两周多吧,老师上课提到这道题,经过这两周的学习,我又想到了只用两层循环的重构做法,然后老师说可以用哈希数组来做,只要一层循环就能搞定,当时在课上我就自己试着做了一遍。
2.2题目2:6-8 jmu-ds-链表倒数第m个数
已知一个带有表头节点的单链表,查找链表中倒数第m个位置上的节点。
- 输入要求:先输入链表结点个数,再输入链表数据,再输入m表示倒数第m个位置。
- 输出要求,若能找到则输出相应位置,要是输入无效位置,则输出-1。
2.2.1设计思路(伪代码)
利用两个LinkList型指针p,ptr,将p移到第m个位置,让ptr从指向头结点的位置与p同时往下指,当p指向尾结点时,ptr的位置即为倒数第m个数的位置。
- 伪代码
定义LinkList型指针p,ptr,两个指针均指向头结点,ptr表示指向倒数第m个位置的指针
if 头结点为空 或者 m取小等于0的无效位置 then
返回 -1
while 指针p不为空 且 m的值不为0 do
指针p往下移
if m的值大于0 then //即L的长度小于m
返回 -1
else
while 指针p不为空 do
指针p与指针ptr同时向下移动
返回 ptr->data的值
end if
2.2.2代码截图
2.2.3本题PTA提交列表说明
Q1:当时做的时候,是在帮同学改过这题代码的情况下做的,所以无效位置条件的测试点都过了,但是我没想到的是有效位置上出了错
A1:我当时看了一遍又一遍,觉得自己的代码没什么毛病啊,(当时因为题目的各种函数细节不表,懒得自己打出来去dev调试),后面又静下心来慢慢看那,发现我else里头while语句的条件写错了,写成了p->next,这样一来ptr所指向的位置就变成了倒数第m+1个
Q2:当时做这道题的时候不仅是在帮同学改完代码的基础上,更是在老师上课讲完了相关例题之后写的,总觉得没有了自己的思考,下回PTA的作业还是不要拖着了(哭唧唧)
2.3题目3:6-10 jmu-ds-有序链表的插入删除
链表L是一个有序的带头结点链表,实现有序链表插入删除操作。
2.3.1设计思路(伪代码)
插入函数:找到比输入数据大的结点,将数据插入该结点的前面
删除函数:找到所输入的数据,并将该结点释放删除,若找不到则输出X找不到!
- 伪代码
插入函数:
定义LinkList型的变量p、ptr,为p申请空间,将e赋值给p->data,令ptr等于L
while ptr->next不为空 then //保存前驱结点,方便插入操作
if ptr->next->data的值大于e then
将指针p插入在ptr指针后
返回空值
end if
ptr指针往后移
end while
将指针p插入在ptr指针后
删除函数:
定义LinkList类型的指针 ptr、q,令ptr等于L保存前结点
if 头结点为空 then
return
while ptr->next 不为空 do
if ptr->next->data的值与e相等 then
利用指针q删除该结点,释放q
return
end if
ptr指针往后移
end while
输出X找不到! //当输入数据找不到时才会执行该语句
2.3.2代码截图
-
void ListInsert(LinkList &L,ElemType e);//有序链表插入元素e
-
void ListDelete(LinkList &L,ElemType e);//链表删除元素e
2.3.3本题PTA提交列表说明
Q1:这题做完后再看,真的是超简单,但是吧当时不知道在想什么,信心满满的做完,结果编译错误……看了眼PTA的错误提醒,好吧,把指针跟数据进行比较,我也是没谁了
Q2:改完想着好了这题做完了,结果来了个运行超时……(吐血)
A2:检查了两遍,没毛病啊,再提交一下,运行超时……然后问了下同学,一语惊醒梦中人:你指针都没移动。好的吧我的错,继续改。
Q3:改完以后想着这回总该对了吧,结果如上图所见,答案错误
A3:如同上一题一样的原因,没用dev调试,看了n边的代码,始终找不出错误,最后认命的手动补充细节不表的函数,过程中又出现了问题,因为建链的时候忘记让尾结点等于NULL,结果输出的时候陷入了死循环。改完后,在调试过程中发现插入函数在尾部插入的情况不能实现,再瞅了眼代码,好吧,还真没考虑到在末尾插入的情况,终于,答案正确了。
- 写这题呢不是因为难,不会做,而是发现自己又开始犯起了基本的错误,指针跟数据都能比较起来,不知道为啥做这道题的时候,逻辑思维乱乱的,各种出错。当然还有,不要因为题目给的代码细节不表懒得自己写,就不去用dev调试,你以为不表的函数你都会,但是可能也会有不懂的地方。
3.阅读代码
3.1题目:链表的中间结点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
- 示例1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
- 示例2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
提示: 给定链表的结点数介于 1 和 100 之间。
3.2解题思路
解法一:输出到数组
- 解题思路
按顺序将每个结点放入数组 A 中。然后中间结点就是 A[A.Length/2],因为我们可以通过索引检索每个结点。
时间复杂度:O(N),其中 N 是给定列表中的结点数目。
空间复杂度:O(N),A 用去的空间
- 伪代码
1.定义一个数组A
2.利用while遍历链表并将链表赋值给数组
3.返回数组A[n/2]的值
解法二:快慢指针法
- 解题思路
当用慢指针 slow 遍历列表时,让另一个指针 fast 的速度是它的两倍。当 fast 到达列表的末尾时,slow 必然位于中间。
时间复杂度:O(N),其中 N 是给定列表的结点数目
空间复杂度:O(1),slow 和 fast 用去的空间。
- 伪代码
定义两个ListNode类型的指针变量slow、fast,且两个指针变量均指向head
while fast指针不为空 且 fast->next指针不为空 do
slow指针往下移一位
fast指针往下移两位
end while
返回slow指针的值
3.3代码截图
- 解法一
- 解法二
3.4学习体会
这道题其实超级简单,选择这道题的原因是这套题配的解题思路。
看到这道题的时候,我的想法就是:遍历一边链表,然后记下链表的长度,再用一个循环来使指针指向中间的位置。虽然跟上述两种做法的时间复杂度一样,都是O(n),但是,却用了两次循环,而且感觉自己的做法特别的笨。然后看到解法一的解题思路时,我在想为什么还要用数组,跟我的想法不是差不多,却还要在定义新的临时变量,结果看完代码…好吧,是我的思路不对,存进数组后直接输出就好了,根本不用再次遍历。然后看到解法二的时候,心里的想法:真的是妙啊,连新的临时变量都用不上了,然后就想到了链表倒数第k个数的那道题,有异曲同工之妙。
所以记下这道题,一是给自己一个新的逻辑思维跟解题思路,更是提醒自己举一反三,不要新的思路解法学了,却运用不到其他类似的题目上。