DS01-线性表
0.PTA得分截图
1.本周学习总结(0-4分)
1.1 总结线性表内容:
1.1.1 顺序表结构体定义、顺序表插入、删除的代码操作等
- 顺序表结构体定义:
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize];//存放顺序表元素
int length; //存放顺序表长度
}List;
-
顺序表插入:
代码实现:
bool ListInsert(List& L, int i, ElemType e)
{
int j;
if(i<1 || i>L->lenth + 1)
return false; //参数错误时返回false
i- - ; //将顺序表逻辑序号转化为物理序号
for(j = L->length; j > i; j--) //将data[i..n]元素后移一个位置
L->data[j] = L->data[j - 1];
L- > data[i] = e; //插入元素e
L- > length++; //顺序表长度增1
return ture; //成功插入返回ture
}
时间复杂度为:O(n)。
- 顺序表的删除:
bool ListDelete(List& L, int i, ElemType& e)
{
int j;
if (i<1 || i>L->length)//删除位置不合法
return false;
i--; //将顺序表逻辑序号转化为物理序号
e = l->data[i];
for (j = i; j < L->length - 1; j++)
L->data[j] = L->data[j + 1];
L->length--; //顺序表长度减1
return ture;
}
时间复杂度:O(n)
- 区间删除数据:
void DelNode(SqList& L, int min, int max)
{
int index = 0;
int i;
while (index < L->length)
{
if (L->data[index] >= min && L->data[index] <= max)
{
for (i = index; i < L->length - 1; i++)
{
L->data[i] = L->data[i + 1];
}
L->length--;
}
else
index++;
}
}
1.1.2链表结构体定义、头插法、尾插法、链表插入、删除操作
- 链表结构体定义:
typedef struct LNode //定义单链表结点类型
{
ElemType data;
struct LNode *next; //指向后继结点
} LNode, *LinkList;
- 头插法:
思路:
代码实现:
void CreateListF(LinkList &L,int n)
{
L=new LNode;
int i=0;
L->next=NULL;
LinkList s;
for(i=0;i<n;i++)
{
s=new LNode;
s->next=L->next;
L->next=s;
cin>>s->data;
}
}
- 尾插法:
思路:
代码实现:
void CreateListR(LinkList &L,int n)
{
L=new LNode;
int i=0;
L->next=NULL;
LinkList s;
LinkList r=new LNode;
r=L;
for(i=0;i<n;i++)
{
s=new LNode;
r->next=s;
s->next=NULL;
r=s;
cin>>s->data;
}
}
-
链表插入:
思路:
-
链表删除:
思路:
1.1.3 有序表,尤其有序单链表数据插入、删除,有序表合并
-
有序表:
定义:所有元素以递增或递减方式有序排列的线性表。
由此得出有序表与线性表的关系:
-
有序单链表数据插入:
void ListInsert(LinkList &L, ElemType e) {
LinkList p = new(LNode);
p = L;
LinkList node = new(LNode);
while (1) {
if (p != nullptr&&p->next!=nullptr) {
if (e >= p->data&&e<=p->next->data) {
node->data = e;
node->next = p->next;
p->next = node;
return;
}
p = p->next;
}
else
break;
}
node->data = e;
p->next = node;
p = p->next;
p->next = nullptr;
return;
}
- 有序单链表数据删除:
void ListDelete(LinkList &L, ElemType e) {
LinkList p = new(LNode);
p = L;
if (p->next == nullptr)
return;
while (1) {
if (p != nullptr&&p->next != nullptr) {
if (e == p->next->data) {
p->next = p->next->next;
return;
}
}
if (p == nullptr)
break;
p = p->next;
}
cout << e << "找不到!" << endl;
}
- 有序表合并:
思路:
代码实现:
void MergeList(LinkList& L1, LinkList L2)
{
LinkList ptr1, ptr2, tail, temp;
ptr1 = L1->next;
ptr2 = L2->next;
L1->next = NULL;
tail = L1;
while (ptr1 != NULL && ptr2 != NULL)
{
if (ptr1->data < ptr2->data)
{
temp = ptr1;
ptr1 = ptr1->next;
}
else if (ptr2->data < ptr1->data)
{
temp = ptr2;
ptr2 = ptr2->next;
}
else
{
temp = ptr1;
ptr1 = ptr1->next;
ptr2 = ptr2->next;
}
tail->next = temp;
tail = temp;
}
while (ptr1 != NULL)
{
tail->next = ptr1;
tail = ptr1;
ptr1 = ptr1->next;
}
while (ptr2 != NULL)
{
tail->next = ptr2;
tail = ptr2;
ptr2 = ptr2->next;
}
tail->next = NULL;
}
1.1.4 循环链表、双链表结构特点
-
循环链表:
循环链表是另一种形式的链式储存结构形式,它的节点类型与非循环单链表的相同,但它的最后一个结点的指针域指向头结点,整个链表形成一个环。
示意图:
-
双链表结构:
1.定义:
typedef struct NNode
{
ELemType data;
struct DNode* prior; //指向前驱结点
struct DNode* next; //指向后继结点
}DLinkList;
2.示意图:
1.2 谈谈你对线性表的认识及学习体会
在刚开始进入本章的时候,还有些懵逼,可能是上学期期末不认真,第一想法是:咋突然就到C++了,但经过了一周的自我调整,慢慢把心理给扭转过来。
在C++的编写中,发现了C++语言较于C语言更加精短,最明显的感受就是输入输出不用繁琐地括号双引号再括号。
在线性表的编译中,一方面在慢慢回顾这上学期的所学,一方面在扩展这新的内容:循环链表和双链表
还有本章新学习到的概念:时间复杂度与空间复杂度,在算法的学习中,正慢慢地培养着思考出更简洁的算法结构来编译。
2.PTA实验作业(0-2分)
2.1 6-8 jmu-ds-链表倒数第m个数 (20分)
2.1.1 代码截图:
2.1.2 本题PTA提交列表说明:
Q1:答案错误
A1:刚开使我想着先计算出总长度在用减法计算出整数的第n-m个数,结果输出的答案是m-1个
Q2:多种错误
A2:第一次的编码让我知道了如何找到倒数第m个数的位置,我尝试着用更快的方法,但明显的,我思路出现了错误。
Q2Q3Q4:答案错误
A3:我查了查网上的思路:用一个指针着先走m步,然后再用两个指针同时行走,当第一个指针到达尾端时,第二个指针的位置就是倒数第m个数。
编写完反复修改总有一个测试点不会过就放弃了这个思路。
Q5:答案正确
A4:再次思考后,我决定先将指针逆置,然后在正数m就相当于倒数的第m个位置,答案正确。
2.2 6-11 jmu-ds-链表分割 (20分)
2.2.1 代码截图:
2.2.2 本题PTA提交列表说明:
Q1:内存超限
A1:第一次的编写,我定义了多个指针,每个指针申请了空间,最后导致内存超限
Q2:答案正确
A2:减少了定义的指针,重新构建了分割的思路,通过了题目。
2.3 7-1 两个有序序列的中位数 (25分)
2.3.1 代码截图
2.3.2 本题PTA提交列表说明:
Q1Q2:答案错误和多种错误
A1:第一次编写,我看到题目没仔细阅读题目的中位数定义而是按照数学的中位数定义来编写,导致不是答案错误就是多种错误。
Q3:答案正确
A2:再次阅读完题目后,将原先的代码进行修改并运行完,检查和题目所给的例题答案一致,提交后答案正确。
3.阅读代码
3.1 题目及解题代码
代码:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode *head = NULL;
if(l1 == NULL) return l2;
if(l2 == NULL) return l1;
if(l1->val <= l2->val)
{
head = l1;
head->next = mergeTwoLists(l1->next,l2);
}
else
{
head = l2;
head->next = mergeTwoLists(l1,l2->next);
}
return head;
}
};
3.1.1 该题的设计思路
时间复杂度O(n+m)
空间复杂度O(n+m)
3.1.2 该题的伪代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
定义头指针head并赋值为空;
判断L1、L2头指针如果为空则返回另一个的头指针
判断L1与L2的值的大小来确定将L1还是L2赋值给head并进行递归
}
};
3.1.3 运行结果:
3.1.4 分析该题目解题优势及难点
优势:比较简单,学过递归算法的都能够理解。
难点:在空间复杂度上比较复杂。
3.1 题目及解题代码
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head==NULL) return NULL;
if(k==0) return head;
ListNode * start=new ListNode(0);
start->next=head;
ListNode* tmpHead=start;
ListNode* tmpRear=head;
int len=0,tmpk=k;
while(tmpHead->next!=NULL &&tmpk>0)//这一步是为了判断len和k的关系
{
tmpk--;
len++;
tmpHead=tmpHead->next;
}
if(k%len==0 && tmpHead->next==NULL ) return head;//①
if(tmpk==0)//③
{
tmpHead=tmpHead->next;
while(tmpHead->next!=NULL)
{
tmpHead=tmpHead->next;
tmpRear=tmpRear->next;
}
}
else if(len<k)//②
{
tmpk=k%len;
tmpHead=head;
while(tmpk-->0)
{
tmpHead=tmpHead->next;
}
while(tmpHead->next!=NULL)
{
tmpHead=tmpHead->next;
tmpRear=tmpRear->next;
}
}
tmpHead->next=start->next;
start->next=tmpRear->next;
tmpRear->next=NULL;
return start->next;
}
};
3.2.1 该题的设计思路
时间复杂度:O(n)
空间复杂度:O(1)
3.2.2 该题的伪代码
/**
- Definition for singly-linked list.
- struct ListNode {
-
int val;
-
ListNode *next;
-
ListNode(int x) : val(x), next(NULL) {}
- };
/
class Solution {
public:
ListNode rotateRight(ListNode* head, int k) {
判断头节点为空退出
将k=0的情况单独讨论
定义指针start并将start的下一个赋值为空
定义指针tmpHead赋值为start
定义指针tmpRear赋值为头指针
while循环判断len和k的关系
如果k是len的整数被则返回头指针
如果 k>len,这样就要取新的k=k%len
如果k<len,找最后一个节点的前第k个节点
}
};
3.1.3 运行结果:
3.1.4 分析该题目解题优势及难点
优势:旋转链表有利于对循环链表和双链表的学习,能够理解旋转链表就能够理解循环链表和双链表。
难点:链表的长度处理与NULL处理,选择的方式,旋转的次数大则容易出错。