0.PTA得分截图
1.本周学习总结(0-4分)
1.1 总结线性表内容
1.线性表
-
线性表定义:线性表是具有相同特性的数据元素的一个有限序列
-
线性表的抽象数据类型描述:ADT结构
2.顺序表
- 初始化InitList(结构体定义)
typedef int ElemType;
typedef struct
{
ElemType data[MaxSize]; //存放顺序表元素
int length ; //存放顺序表的长度
} List;
typedef List *SqList;
void CreateList(SqList &L,int n){
int i;
L=new List;
L->length=n;
for(i=0;i<n;i++) cin>>L->data[i];
}
时间复杂度:O(1)
- 插入ListInsert
bool ListInsert(List &L,int i,ElemType e)
{ int j;
if (i<1 || i>L->length+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 true; //成功插入返回true
}
时间复杂度:O(n)
*删除ListDelete
bool ListDelete(List &L,int i,ElemType &e)
{
if (i<1 || i>L->length) //删除位置不合法
return false;
i--; //将顺序表逻辑序号转化为物理序号
e=L->data[i];
for (int j=i;j<L->length-1;j++)
L->data[j]=L->data[j+1];
L->length--; //顺序表长度减1
return true;
}
时间复杂度:O(n)
- 其他函数:销毁DestroyList(free函数/delete函数)、判定空表ListEmpty(bool类型的应用)、
长度读取ListLength、输出数据DispList、获取指定元素GetElem、按值查找元素LocateElem等
3.链表
- 初始化(结构体定义)
typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
Status InitList_L(LinkList &L){
L=new LNode;
L->next=NULL;
return OK;
}
时间复杂度:O(1)
-
建立链表
- 头插法(新增节点从链表头部插入)
图像表示
代码实现
void CreateListF(LinkList &L,ElemType a[],int n){
int i;
L=new LNode;
L->next=NULL;
LinkList nodePtr;
for(i=0;i<n;i++){
nodePtr=new LNode;
nodePtr->data=a[i];
nodePtr->next=L->next;
L->next= nodePtr;
}
}
- 尾插法(新增节点从链表尾部插入)
图像表示
代码实现
void CreateListR(LinkList &L,ElemType a[],int n){
int i;
LinkList nodePtr,tailPtr;
L=new LNode;
L->next=NULL;
tailPtr=L;//尾指针
for(i=0;i<n;i++) {
nodePtr=new LNode;
nodePtr->data=a[i];
rearPtr->next=s;//尾部插入新结点
rearPtr=s; }
nodePtr->next=NULL;
}
- 插入节点ListInsert
图像表示
代码实现
bool ListInsert(LinkList &L,int i,ElemType e){
int j=0;
LinkList p=L,s;
while(p&&j<i-1){
j++;p=p->next;
}
if(p==NULL) return false; //未找到第i-1个结点
s=new LNode;
s->data=e;
s->next=p->next; //插入p后面
p->next=s;
return true;
}
- 删除节点ListDelete
图像表示
代码实现
bool ListDelete_L(LinkList &L,int i,ElemType &e)
{
int j=0;
LinkList p=L,s,q;
while(p&&j<i-1){
p=p->next;j++;
}
if(p==NULL) return false;
q=p->next; //第i个位置
if(q==NULL) return false;
e=q->data;
p->next=q->next;//改变指针关系,删除
delete q;
return true;
}
- 其他函数:销毁链表DestroyList、判断空表ListEmpty、读取长度ListLength、
输出数据DispList、查找节点GetElem等
4.有序表
- 定义:所有元素以递增或递减方式有序排列
- 插入数据ListInsert
有序顺序表的代码实现
void ListInsert(SqList &L,ElemType e)
{ int i=0,j;
while (i<L->length && L->data[i]<e)
i++; //查找值为e的元素
for (j=ListLength(L);j>i;j--) //将data[i..n]后移一个位置
L->data[j]=L->data[j-1];
L->data[i]=e;
L->length++; //有序顺序表长度增1
}
有序单链表的代码实现
void ListInsert(LinkNode &L,ElemType e)
{ LinkNode pre=L,p;
while (pre->next!=NULL && pre->next->data<e)
pre=pre->next; //查找插入结点的前驱结点*pre
p=new LinkNode;
p->data=e; //创建存放e的数据结点*p
p->next=pre->next; //在*pre结点之后插入*p结点
pre->next=p;
}
- 删除数据ListDelete
有序顺序表的代码实现
int ListDelete(SqList &L,int i)
{
if(i<=0||i>L.length) return 0;
for(int j=i-1;j<L.length;i++)
{
L.elem[j]=L.elem[j+1];
}
L.length--;
return 1;
}
有序单链表的代码实现
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;
}
- 二路归并UnionList
图像表示
代码实现
void UnionList(SqList LA,SqList LB,SqList &LC)
{ int i=0,j=0,k=0;//i、j分别为LA、LB的下标,k为LC中元素个数
LC=new SqList; //建立有序顺序表LC
while (i<LA->length && j<LB->length)
{ if (LA->data[i]<LB->data[j])
{ LC->data[k]=LA->data[i];
i++;k++;
}
else //LA->data[i]>LB->data[j]
{ LC->data[k]=LB->data[j];
j++;k++;
}
}
while (i<LA->length) //LA尚未扫描完,将其余元素插入LC中
{ LC->data[k]=LA->data[i];
i++;k++;
}
while (j<LB->length) //LB尚未扫描完,将其余元素插入LC中
{ LC->data[k]=LB->data[j];
j++;k++;
}
LC->length=k;
}
5.双链表
双链表每个节点有2个指针域,一个指向后继节点,一个指向前驱节点。类型定义如下:
typedef struct DNode //声明双链表节点类型
{ ElemType data;
struct DNode *prior; //指向前驱节点
struct DNode *next; //指向后继节点
} DLinkList;
双链表有点:
• 从任一结点出发可以快速找到其前驱结点和后继结点;
• 从任一结点出发可以访问其他结点。
6.循环链表
循环链表是另一种形式的链式存储结构形式。
循环单链表:将表中尾结点的指针域改为指向表头结点,整个链表形成一个环。
由此从表中任一结点出发均可找到链表中其他结点。
循环双链表与非循环双链表的比较
与非循环双链表相比,循环双链表:
• 链表中没有空指针域
• p所指结点为尾结点的条件:p->next==L
• 一步操作即L->prior可以找到尾结点
1.2.谈谈你对线性表的认识及学习体会。
本学期在C++语法的基础上进行对线性表知识的代码编写,得益于C++的精短性,在代码可读性上得到了不错的提升。
在线性表的基础上拓展学习了双链表和循环链表的相关知识,使得我对链表的掌握与操纵更加熟练。
在经过本章的学习后,更加懂得了好算法的重要性,好的算法不仅能够使代码更加精炼,还能减小程序的时间与空间复杂度。
在接下来的学习中,能够对链的应用更加熟练,为后续编写程序打下良好的基础。
2.PTA实验作业(0-2分)
2.1.题目1:6-2 jmu-ds-有序表插入数据 (25分)
2.1.1代码截图
2.1.2本题PTA提交列表说明。
1.部分正确:在移动数组元素时没有考虑到数组越界的问题
2.部分正确:在插入完成后没有对数组的长度进行增加
3.答案正确
2.2 6-3 jmu-ds- 顺序表删除重复元素 (25分)
2.2.1代码截图
2.2.2本题PTA提交列表说明。
1.部分正确:在删除重复元素前移后面元素的循环时循环始条件使用错误
2.部分正确:没有考虑到当最后仅剩两个元素时依旧重复的问题
3.答案正确
2.3 6-8 jmu-ds-链表倒数第m个数 (20分)
2.3.1代码截图
2.3.2本题PTA提交列表说明。
1.运行时错误:开头定义的代运行p指针指向错误
2.部分正确:没有考虑无效位置情况下对应的返回内容
3.答案正确
3.阅读代码(0--4分)
3.1 题目及解题代码
题目:
代码:
ListNode* swapPairs(ListNode* head) {
ListNode *dummyHead = new ListNode(-1);
dummyHead->next = head;
ListNode *ptr = dummyHead;
while(ptr->next != nullptr && ptr->next->next != nullptr)
{
ListNode *firstNode = ptr->next;
ListNode *secondNode = ptr->next->next;
//交换节点
ptr->next = secondNode;
firstNode->next = secondNode->next;
secondNode->next = firstNode;
ptr = firstNode;
}
ListNode *retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
3.1.1 该题的设计思路
时间复杂度:O(n)
空间复杂度:O(1)
3.1.2 该题的伪代码
ListNode* swapPairs(ListNode* head)
{
声明哑结点dummyHead和指针ptr
分别并指向头节点head和头节点head的前驱
while(链表不为空)
{
firstNode遍历链表中的偶数节点
//交换节点
secondNode遍历链表中的奇数节点,并交换节点
}
end while
ListNode *retNode = dummyHead->next;
重置节点并释放空间
返回节点
}
3.1.3 运行结果
3.1.4分析该题目解题优势及难点。
优势:
本题的递归和非递归解法其实原理类似,都是更新每两个点的链表形态完成整个链表的调整
其中递归解法还可以作为典型的递归解决思路进行讲解
难点:
递归写法要观察本级递归的解决过程,形成抽象模型,因为递归本质就是不断重复相同的事情。
而不是去思考完整的调用栈,一级又一级,无从下手。
3.2 题目及解题代码
题目:
代码:
ListNode* oddEvenList(ListNode* head) {
if(!head || !head->next || !head->next->next)
return head;
//声明虚拟头结点
ListNode dummyOdd(0);
//dummyOdd.next = head;
auto p = &dummyOdd;
ListNode dummyEven(0);
//dummyEven.next = head->next;
auto q = &dummyEven;
int flag = 0;
while(head){
flag++;
if(flag % 2 != 0){
p->next = head;
p = p->next;
}
else{
q->next = head;
q = q->next;
}
head = head->next;
}
q->next = NULL;
p->next = dummyEven.next;
return dummyOdd.next;
}
3.2.1 该题的设计思路
时间复杂度: O(n)
空间复杂度: O(1)
3.2.2 该题的伪代码
ListNode* oddEvenList(ListNode* head){
if(head为NULL或一个节点或两个节点)
return head;
声明两个虚拟头结点:dummyOdd(0),dummyEven(0);
while(循环遍历每个头结点){
if(节点序号为奇数)
插入到dummyOdd链表中(尾插法,只改变指针指向,不申请其他节点)
else(节点序号为偶数)
插入到dummyEven链表中(尾插法,只改变指针指向,不申请其他节点)
将dummyEven链表尾节点置空,防止出现循环链表(如1->2->3->4->5->NULL);
将dummyOdd尾节点指向dummyEven头结点;
return dummyOdd头结点的下一个节点;
}
}
3.2.3 运行结果
3.2.4分析该题目解题优势及难点。
优势:
将奇节点放在一个链表里,偶链表放在另一个链表里,然后把偶链表接在奇链表的尾部
这个解法非常符合直觉思路也很简单
难点:
遍历整个链表至少需要一个指针作为迭代器
这里 odd 指针和 even 指针不仅仅是尾指针,也可以扮演原链表迭代器的角色
即使思路直观,但要写一个精确且没有bug的代码还是需要进行一番思索的