0.PTA得分截图
1.本周学习总结
1.1 总结线性表内容
1.顺序表结构体定义、顺序表插入、删除的代码操作
顺序表的定义:
线性表的顺序存储又称为顺序表。
它用一组地址连续的存储单元,依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。
第 1 个元素存储在线性表的起始位置,第 i 个元素的存储位置后面紧接着存储的是第 i+1 个元素。
因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。
顺序表结构体的定义:typedef int ElemType即是定义数据元素类型,可以适应更多类型;
typedef struct的内容就是定义了定义顺序表类型,只是定义了一个类型,而不是变量;
顺序表结构的定义,对于在代码的后续操作起着关键性作用,所以在结构体的定义中要仔细。
顺序表插入、删除的代码操作:顺序表的插入和删除操作,在顺序表开始寻找到相对性的数值,
就开始执行操作。删除操作针对于区间删除来说,先从第一个for循环开始执行,定义三个变量,
找到重复元素就开始删除操作。顺序表的插入删除都是要遍历链表,找到相对应的元素进行操作。
2.链表结构体定义、头插法、尾插法、链表插入、删除操作
链表结构体的定义:对于单链表而言,先定义的一个结构体用来描述单链表的结点。从这个结构
定义中,我们知道,结点由存放数据元素的数据域存放后继结点地址的指针域组成。对于ElemType
就是定义一个结构体成员,struct Node* next的语句对于链表的结点指向的操作,其中每个数据
分为两部分,一部分是数据存储的位置,称为数据域,另外指针所存储的地方,称为指针域。
头插法建链表操作:从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,
然后将新结点插入到当前链表的表头结点之后,直至读入结束标志为止。首先对于链表L申请空间,再
L->next=NULL指向空指针,LinkList s,申请新的链表s,用s来保留L的->next,再通过s插入数据
尾插法建链表:将新结点插到当前链表的表尾上,增加一个尾指针,使之指向当前链表的表尾。
尾插法建链表比从头插法建链表操作多一个尾结点tail,用tail->next尾部插入数据
链表插入:
链表删除:
3.有序表,尤其有序单链表数据插入、删除,有序表合并
有序单链表数据插入,删除:所谓有序表,其中所有元素以递增或者递减方式有序排列;有序表
包含于线性表;有序表和线性表中元素之间的逻辑关系相同,其区别是运算实现的不同。
有序单链表插入:
有序单链表删除:
有序表合并:
伪代码:
新建顺序表LC;
i表示LA的下标,j表示LB的下标
while(idata[i]data[j]) 则LC中插入元素LA->data[i],i++;
else 插入元素LB->data[j],j++
LC数组长度加1;
}
查看LA或者LB是否扫描完毕,没扫描玩把剩余元素复制插入LC
归并(没有显示重复数据)
4. 循环链表、双链表结构特点
双链表:双链表每个节点有2个指针域。一个指向后续节点,一个指向前驱结点。
类型定义:
typedef struct DNode { //定义双链表结点类型
ElemType data; //数据域
struct DNode *prior,*next; //前驱和后继指针
} DNode *DLinklist;
双链表中的插入结点操作语句:
s->next=p->next;
p->next->prior=s;
s->prior=p;
p->next=s;
双链表删除结点操作:
p->next->next->prior=p;
p->next=p->next->next;
循环链表:循环链表是另一种形式的链式存储结构形式。将表中尾结点的指针域改为
指向表头结点,整个链表形成一个环。由此从表中任意节点出发均可以找到链表中其他
节点。循环双链表与非循环相比,链表中没有空指针域;p所指结点为尾结点的条件:
p->next==L;一步操作既可以找到尾结点
5.线性表特点
表中元素的个数有限。
表中元素具有逻辑上的顺序性,在序列中各元素排序有其先后次序。
表中元素都是数据元素,每一个元素都是单个元素。
表中元素的数据类型相同,这意味着每一个元素占有相同大小的存储空间。
表中元素具有抽象性。即仅讨论元素间的逻辑关系,不考虑元素究竟表示什么内容。
线性表是一种逻辑结构,表示元素之间一对一的相邻关系。
顺序表和链表是指存储结构,两者属于不同层面的概念
6.线性表基本操作
InitList(&L):初始化表。构造一个空的线性表。
Length(L):求表长。返回线性表 L 的长度,即 L 中数据元素的个数。
LocateElem(L,e):按值查找操作。在表 L 中査找具有给定关键字值的元素。
GetElem(L,i):按位査找操作。获取表 L 中第 i 个位置的元素的值。
ListInsert(&L,i,e):插入操作。在表 L 中第 i 个位置上插入指定元素 e。
ListDelete(&L,i,&e):删除操作。删除表 L 中第 i 个位置的元素,并用 e 返回删除元素的值。
PrintList(L):输出操作。按前后顺序输出线性表 L 的所有元素值。
isEmpty(L):判空操作。若 L 为空表,则返回true,否则返回false。
DestroyList(&L):销毁操作。销毁线性表,并释放线性表 L 所占用的内存空间。
1.2谈谈你对线性表的认识及学习体会。
线性表因存储结构的不同,分为顺序表和链表。顺序表适合于查找、修改第i个结点的值(其时间复杂度为O(1)),
但插入或者删除结点就要每次都移动数组,比较麻烦。链表适合用于插入删除某个结点,比较灵活,也比较绕。
在打pta的过程中,由于大多数都没有注意到一些基础函数,在打编程题的时候就一直各种错误,尤其是初始化链表,
一直出现段错误,在链表中还要注意不能出现野指针,所以要提高正确率,最好要学会背代码....
2.PTA实验作业
2.1.题目1:有序链表合并
2.1.1代码截图
2.1.2本题PTA提交列表说明。
Q1:最开始的时候出现段错误。
A1:没有对L3进行初始化
Q2:中间部分出现部分正确
A2:因为在复制上一段代码的时候,在重复数据出现时候忘记p=p->next
2.2.题目2:链表倒数第m个数
2.2.1代码截图
2.2.2本题PTA提交列表说明
Q1:编译错误从VS转移代码的时候最后漏掉}
A1:加上}
Q2:一开始的运行超时是while里面忘记让用来遍历的pre指针往后移了,导致while出不来,就运行超时了。
A2:添加了一个pre=pre->next
Q3:编译错误就是承接上一个错误,添加了pre=pre->next,变量时ptr
A3:pre=pre->next改成ptr=ptr->next
2.3.题目3:一元多项式的乘法与加法运算
2.3.1代码截图
2.3.2本题PTA提交列表说明
Q1:编译错误
A1:加逗号
Q2:段错误,在for循环发生错误,for (int i = N - 1;i >= 0;i++)编写错误
A2:改为for (int i = N - 1;i >= 0;i--)
Q3:部分正确,就是答案的格式错误
A3:结尾改为cout << "0 0"
3.阅读代码
3.1 题目及解题代码
题目:
解题代码:
#include
#include
using namespace std;
# define MAXSIZE 1000
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int length;
}Vector;
Vector* CircleLeftMove(Vector* v, int p) {
Vector* v_temp = (Vector*)malloc(sizeof(Vector));
v_temp->length = p;
int i;
for (i = 0; i < v->length; i++) {
v_temp->data[i] = v->data[i];
v->data[i] = v->data[i + p];
}
for (i = 0; i < p; i++) {
v->data[v->length - p + i] = v_temp->data[i];
}
return v;
}
3.1.1 该题的设计思路
借助辅助数组v_temp存储原表的前p个元素,并把原顺序表中p之后的元素顺序前移,
然后将v_temp中暂存的p个数的元素依次放回到原顺序表的后续单元。
时间复杂度为O(n)
空间复杂度为O (p)
3.1.2 该题的伪代码
定义data的数组空间大小,链表长度
申请v_temp空间
v_temp->length = p存储前p个元素
for(原顺序表中p之后的元素顺序前移)
v_temp中暂存的p个数的元素依次放回到原顺序表的后续单元;
return v;
3.1.3 运行结果
3.1.4分析该题目解题优势及难点。
1.使用静态分配的方式创建一维数组:数组的大小和空间固定,一旦占满再加入新的数据会导致程序崩溃
2.运用辅助数组v_temp来存储元素,再进行前移再放回,思路很清晰
3.2题目及解题代码
题目:
解题代码:
#include
#include
using namespace std;
# define MAXSIZE 1000
typedef int ElemType;
typedef struct {
ElemType data[MAXSIZE];
int length;
}Vector;
int getMidByCompare(Vector v1, Vector v2) {
int num1 = 0, num2 = 0;
int end1 = v1.length - 1;
int end2 = v2.length - 1;
int mid1, mid2;
while (num1 != end1 || num2 != end2) {
mid1 = (num1 + end1) / 2;
mid2 = (num2 + end2) / 2;
if (v1.data[mid1] == v2.data[mid2]) {
return v1.data[mid1];
}
if (v1.data[mid1] < v2.data[mid2]) {
if ((num1 + end1) % 2 == 0) {
num1 = mid1;
end2 = mid2;
}
else {
num1 = mid1 + 1;
end2 = mid2;
}
}
else {
if ((num2 + end2) % 2 == 0) {
end1 = mid1;
num2 = mid2;
}
else {
end1 = mid1;
num2 = mid2 + 1;
}
}
}
return v1.data[num1] < v2.data[num2] ? v1.data[num1] : v2.data[num2];
}
3.2.1该题的设计思路
1.若v1.data[mid1] == v2.data[mid2],则mid1和mid2即为所求中位数,立即返回。
2.若v1.data[mid1] < v2.data[mid2]2,则序列1中比mid1还要小数必不可能为所求中位数,故舍弃序列1中较小的一半;同理,这时也要舍弃序列2中较大的一半。
3.若v1.data[mid1] > v2.data[mid2], 则舍弃序列1中大于mid1的数,同时也要舍弃序列2中较小的一半,直到逻辑上的序列只含一个元素时,较小者即为所求中位数。
4.时间复杂度:O(log2n)。
5.空间复杂度:O(1)。
3.2.2该题的伪代码
定义data的数组空间大小,链表长度
int num1,num2;
num结点始终指向序列中的第一个元素的下标
定义end结点始终指向序列中的最后一个元素的下标
int mid1,mid2;
while(还没找到最后一个元素循环)
{
找到中位数下标;
if(相等) return ;
if(v1.data[mid1] < v2.data[mid2])
{
if(v1的元素个数为奇数) 舍弃序列1中位数前的元素;
else 弃序列1中位数及中位数前的元素;
}
else 序列1的中位数大于序列2的中位数
{
if(v2的元素个数为奇数) 舍弃序列1中比中位数大的元素;
else 舍弃序列2的中位数及比中位数小的元素 ;
}
}
return v1.data[num1] < v2.data[num2] ? v1.data[num1] : v2.data[num2];
3.2.3运行结果
3.2.4分析该题目解题优势及难点。
该题要找出中位数,先用了num来指向序列中的第一个元素的下标 ,和end指向序列中的最后
一个元素的下标 ;感觉看起来还是有点麻烦,不是很能懂;难点在于找到序列1和2对比,
还要再舍弃。