DS01-线性表
0.PTA得分截图
1.本周学习总结
1.1 总结线性表内容
线性表
线性表的抽象数据类型描述
ADT List
{
数据对象D={ai|ai属于Elemset,i=1,2,···,n,n>=0}
数据关系:R1={<ai-1,ai>|ai-1,ai属于D,i=2,···,n}
基本操作:
InitList(&L):构造一个空的线性表L
DestroyList(&L):销毁线性表L占用内存空间
ListEmpty(L):若线性表L为空表,则返回TRUE,否则返回FALSE
ListLength():返回线性表L数据元素个数
GetElem(L,i,&e):用e返回线性表L中第i个数据元素的值
ListInsert(&L,i,e):线性表中插入e
ListDelete(&L,i,&e):线性表中删掉e
}ADT List
脑图
1、顺序表
(1)定义结构体:
typedef struct
{
ElemType data[MaxSize]; //存放顺序表元素
int length; //存放顺序表的长度
}List,*SqList;
(2)顺序表的基本操作
顺序表可以随机存取任一元素,但在插入和删除操作中就需要我们去移动大量的元素。
i:获取第i个元素
而L->data[i-1]就可以得到我们想要的第i个元素。
ii:插入元素
进行一次循环,将大于6的元素往后移一个单位再插入6,最后不要忘记让顺序表的长度+1.
平均时间复杂度O(n)
iii:删除元素
假设我们要删除的元素是5,那么我们只需要把5后面的元素往前移一个单位就可以实现操作。
平均时间复杂度O(n)
2、单链表
(1)定义结构体:
typedef struct LNode
{
ElemType data; //数据域
struct LNode *next //指针域
}LNode,*LinkList;
(2)单链表的基本操作
i:构造空表
L=new LNode;
L->next=NULL;
ii:建立单链表
①头插法
需要新建一个节点,从而通过
s->next=L->next;
L->next=s;
这两句代码来实现
②尾插法
尾插法则是通过将节点一个一个按顺序插入
所以,在利用头插法进行后,其形成链表为输入元素的倒序,而尾插法则为正序。
iii:销毁链表
由于单链表中一个一个的节点都是进行动态申请过的,所以需要利用循环去一个一个删除,而不能想顺序表那样直接将一整条链删除。
iv:插入数据元素
即对节点的指向发生改变就可以实现插入
v:删除数据元素
假设我们要删除5这一元素,我们只需要让前者3指向5的next7,就可以实现操作。
3、有序表
即所有元素都以递增或递减的方式进行有序排列。
而有序表又可分为有序顺序表和有序单链表,不同类型的有序表其结构体也不同,相应的操作也类似于其顺序表或单链表的相应操作。
归并算法:
思路1:新建一个链表L3,遍历链表L1和L2将较小者放入L3中
思路2:直接在L1中进行归并,这样不用新建一个链表,大大减小了空间复杂度。
4、双链表
即每个节点都有2个指针域,一个指前,一个指后。
(1)定义结构体:
typedef struct DNode
{
ElemType data;
struct DNode *prior;
struct DNode *next;
}DLinkList;
(2)双链表的基本操作
i:插入
通过语句完成
s->next=p->next;
p->next->prior=s;
s->prior=p;
p->next=s;
ii:删除
通过语句完成
p->next->next-<prior=p;
p->next=p->next->next;
其插入和删除都应满足:先改前驱,再改后继
5、循环链表
(1)循环单链表
可以知道9的next即为1。
特点:①从循环链表中的任何一个结点的位置都可以到达其他所有结点。
②其结束条件是p->next!=NULL
(2)循环双链表
特点:①链表中没有空指针域
②头结点的prior即为尾结点,尾结点的next即为头结点。
1.2.谈谈对线性表的认识及学习体会
其实在学的时候就会觉得线性表很多很杂,而对这两周的知识进行梳理的话会发现大体是分为单链表和有序表,对他们的一些基本操作进行掌握线性表也能知道个底吧。像pta上的中位数那道题像是对归并算法的拓展...
学习体会:
平时的作业中可以发现我在程序阅读题这方面的能力很差,我自己进去不到别人的思维里面。所以还是得多看看别人的代码,多强化这方面。画图真的很重要,有些题目画图就可以很好的理解了,但是吧,平时在打pta上我自己有时候画图画着画着都会把自己给绕晕了...我需要自己多一些独立思考,不能一有问题就问别人。真的有时候自己想出来的东西才能记很久。
2.PTA实验作业
2.1.题目1:两个有序序列的中位数
2.1.1代码截图
2.1.2本题PTA提交列表说明
结果 | 原因 |
---|---|
部分正确 | 将重复元素删除 |
部分正确 | 使用的循环太多,若数据较大,耗时将过长。 |
答案正确 | 最后将两个还未遍历完的链表改为用if语句,就答案正确了。 |
2.2.题目2:jmu-ds-区间删除数据
2.2.1代码截图
2.2.2本题PTA提交列表说明
结果 | 原因 |
---|---|
答案错误 | 理解题目错误 |
部分正确 | 只过了最后一个全部删除的测试点 |
答案正确 | 我的i上下不一致,上面从1开始,下面从0开始,更改之后正确。 |
2.3.题目3: jmu-ds-链表分割
2.3.1代码截图
2.3.2本题PTA提交列表说明
结果 | 原因 |
---|---|
段错误 | 没有为L2申请空间 |
答案错误 | 没有把数据之间的指向关系弄清楚 |
3.阅读代码
3.1 旋转链表
代码:
3.1.2 该题的伪代码
if(为空链表)return 0;
新建链表tmp=head
while
{
遍历链表;
记录长度;
}
tmp->next=head;//形成循环链表
判断k的数值
while(k--)
{
使tmp前进;
}
断开循环单链表形成一个单链表
大体思路如下
这时3指向4的链断裂,而4就变成了头结点,新成的新链为45123.
3.1.3 运行结果
3.1.4分析该题目解题优势及难点
刚开始看见这个题目的时候,我想的是去移动链表就像之前做过的题目一样,后来看了题解发现他是通过建立一个循环单链表,然后再在一个合适的位置上断开这个环就可以得到我们想要的单链表了。就很灵活的运用到了循环链表这个知识点。
优势:其时间复杂度为O(n),在操作上比较简便,思路上也会比较清晰。
难点:这种思路也不太好想出来。
3.2 K个一组翻转链表
代码:
3.2.2 该题的伪代码
新建 phead,pre,last;
phead->next=head;
pre=phead;
last=head;
while 遍历链表获取长度为count
count/=k;//得到循环次数
while(k不为1&&count--)
{
n=k;
新建q,p,r结点;
令q=head->next,p=head;
while(n>2)
{
将结点进行尾连接,后面一个节点连接前面一个节点;
n--;
}
head->next=q->next;
将链表反转,更新头结点的位置
}
主要思路如下:
3.2.3 运行结果
3.2.4分析该题目解题优势及难点
通过对链表的灵活运用,遍历链表到达一个k之后改变指向而实现链表逆转。我看到有关于这道题的很多做法,递归,栈,迭代等,确确实实拓展了我的思维。而这种做法也算是比较简便的。
难点:在关于链表指向上需要有一定的思维能力,像这题中利用q和p将在第k个元素之前的指向进行改变,变成后一个结点指向前一个结点,最后再重新更新头结点的位置。