DS博客作业01--线性表
0.PTA得分截图
1.本周学习总结
1.1 线性表内容总结
1.1.1顺序表大概
- 在计算机内存中以数组的形式保存的线性表,通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,存储结构是顺序结构。
1.1.2顺序表结构体定义
#define MaxSize 10000
typedef struct
{
int data[MaxSize];
int length;
}SqList;
1.1.3顺序表插入操作
void InsertSeqlist(SeqList *L, int x, int i)
{
int j;
if(L->length == Maxsize)
printf("表已满");
if(i < 1 || i > L->length + 1)
printf("位置错误"); // 检查插入位置是否合法
for(j = L->length;j >= i;j--)
L->data[j] = L->data[j - 1]; // 插入位置以后依次向后移动
L->data[i - 1] = x; //插入
L->length++; //长度加一
}
-
思路:找到位置后将该位置以后的元素依次向后移动,产生一个重复值,然后将要插入值替换掉这个重复值,并将length加一,完成插入。
-
图解:
1.1.4顺序表删除操作
void DeleteSeqList(SeqList *L, int i)
{
int j;
if(i < 1 || i > L->length) //寻找的位置非法
printf("%d 为非法位置\n",i);
for(j = i;j < L->length;j++)
L->data[j - 1] = L->data[j]; //该位置以后依次左移,“吃掉”该位置
L->length--; //长度减一
}
-
思路:找到要删除的位置后将该位置以后的所有元素依次左移,覆盖该位置,并将length减一,完成删除。
-
图解:
1.2.1链表大概
- 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
1.2.2链表结构体定义
typedef struct ListNode
{
int data; //放数据
struct ListNode *next; //指向下一个节点的指针
}ListNode, *LinkList;
1.2.3头插法操作
void CreateListF(LinkList& L, int n)
{
L = (LinkList)malloc(sizeof(LNode*));
L->next = NULL;
int i;
LNode* block;
for (i = 1; i <= n; i++)
{
block = (LinkList)malloc(sizeof(LNode*));
cin >> block->data;
block->next = L->next;
L->next = block;
}
}
1.2.4尾插法操作
void CreateListR(LinkList& L, int n)
{
L = (LinkList)malloc(sizeof(LNode*));
L->next = NULL;
int i;
LNode* block;
LNode* tail;
tail = L;
for (i = 1; i <= n; i++)
{
block = (LinkList)malloc(sizeof(LNode*));
scanf("%d", &(block->data));
block->next = NULL;
tail->next = block;
tail = tail->next;
}
}
1.2.5有序链表删除操作操作
void ListDelete(LinkList& L, int e) //链表已有序
{
LNode* nextPtr;
nextPtr = (LinkList)malloc(sizeof(LNode));
nextPtr = L;
if (nextPtr->next == NULL)
{
printf("链表为空!\n");
return;
}
while (nextPtr && nextPtr->next)
{
if (nextPtr->next->data == e) //找到该删除的节点
{
nextPtr->next = nextPtr->next->next; //指向该节点的下一个节点
return;
}
nextPtr = nextPtr->next;
}
printf("%d找不到!\n", e); //完成循环即找不到节点
return;
}
-
思路:找到要删除的节点后将该节点的前一个节点指向该节点的下一个节点,完成删除。
-
图解:
1.2.5有序链表插入操作操作
void ListInsert(LinkList& L, int e) //链表已有序
{
LNode* guidePtr;
guidePtr = (LinkList)malloc(sizeof(LNode));
LNode* blockPtr;
blockPtr = (LinkList)malloc(sizeof(LNode));
guidePtr = L;
while (guidePtr && guidePtr->next)
{
if (guidePtr->data <= e && guidePtr->next->data >= e) //寻找合适位置
{
blockPtr->data = e;
blockPtr->next = guidePtr->next;
guidePtr->next = blockPtr;
return;
}
guidePtr = guidePtr->next;
}
blockPtr->data = e; //完成循环,即需要插到表尾
guidePtr->next = blockPtr;
return;
}
-
思路:在找到位置后,先将要插入的值赋予一个新生成的节点blockPtr,将blockPtr->next指向该位置,然后将该位置的前一个结点的next指向blockPtr,完成插入。
-
图解:
1.2.6有序链表合并
void MergeList(LinkList& L1, LinkList L2)
{
LNode* guidePtr1;
LNode* guidePtr2;
LNode* temp;
LNode* block;
guidePtr1 = L1->next;
guidePtr2 = L2->next;
temp = L1;
while (guidePtr1 && guidePtr2)
{
if (guidePtr1->data < guidePtr2->data)
{
block = new LNode;
block->data = guidePtr1->data;
temp->next = block;
temp = block;
guidePtr1 = guidePtr1->next;
}
else if (guidePtr1->data > guidePtr2->data)
{
block = new LNode;
block->data = guidePtr2->data;
temp->next = block;
temp = block;
guidePtr2 = guidePtr2->next;
}
else
{
block = new LNode;
block->data = guidePtr1->data;
temp->next = block;
temp = block;
guidePtr1 = guidePtr1->next;
guidePtr2 = guidePtr2->next;
}
}
if (guidePtr1)
temp->next = guidePtr1;
if (guidePtr2)
temp->next = guidePtr2;
}
-
思路:先用temp保存L1的头节点,然后开始比较两条链的值,将两者的最小值以尾插法插入temp中,其中拥有最小值的链需挪动至下一个节点,另一条不动,重复以上操作,直至有一条链走到链尾,若出现相同的数值,则需同时挪动两条链,循环结束后若有链还有剩余节点,则将该剩余节点接在temp后面,完成合并。
-
图解:
1.3.1循环链表
-
①特点:表中最后一个结点的指针域指向头结点,整个链表形成一个环。
-
②判断空链表的条件:
headhead->next;
rearrear->next;
1.3.2双链表
- 特点:每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
1.2.对线性表的认识及学习体会
由于上次用的链表做的课设,对链表的这方面的知识印象比较深刻,同时通过本次博客作业我意识到画一张草图对能帮助我们更好地理解链表。顺序表,对设计与链表、顺序表有关的程序的帮助是极大的。
2.PTA实验作业
2.1 链表分割
2.1.1代码截图
2.1.2本题PTA提交列表说明
Q:答案错误
A:
以下是错误的代码,在while循环内时没有补充判断到尾节点就跳出循环的语句,导致原本就处于尾节点的guidePtr会指向NULL的下一个区域,这显然是错误的,加上判断语句后便通过了测试点。
while (guidePtr)
{
frontPtr->next = guidePtr;
frontPtr = guidePtr; //记录当前,L1
/*if (guidePtr->next == NULL)
break;*/
guidePtr = guidePtr->next;
nextPtr = guidePtr->next; //记录下一个,L2
guidePtr->next = L2->next;
L2->next = guidePtr;
guidePtr = nextPtr;
}
2.2 顺序表删除重复元素
2.2.1代码截图
2.2.2本题PTA提交列表说明
Q:部分正确
A:测试点“空表”一项显示答案错误,系我人为添加了一句提醒空链表的输出语句,而题干未曾要求,空表情况下直接return;即可。
2.3 两个有序序列的中位数
2.3.1代码截图
2.2.2本题PTA提交列表说明
Q:部分正确
A:全是“N取最大时”这一测试点显示“答案错误”无法通过,起先我以为是因为在main函数内直接定义a[100000]数组所致,于是尝试着用顺序表解题,但还是过不了这一测试点,虽然我现在还不知道
为什么具体原因是什么,但我意识到应该是自己思路有问题,与开辟数组无关,于是便采用另一种思路,开辟一个第三数组,将S1与S2按从小到大的顺序放入,再输出中间数,这样便通过了测试点。
3.阅读代码
3.1 题目及解题代码
-
题干
-
解题代码
3.1.1 该题的设计思路
用unordered_map开辟一个哈希表,然后用迭代算法。可以简单粗暴地这么理解:常见的哈希表下标是数字,以数字相连成表,而本题是以头节点、头节点的下一个节点(以next指针,而不是random)······为下标相连成表,而下标所对应的值是原链表的一个个节点。
3.1.2 该题的伪代码
3.1.3 运行结果
3.1.4分析该题目解题优势及难点
-
优点:只用两层for循环就完成了复制复杂链表,用unordered_map构建了一个以next相连的哈希表,通过在下标存放next以及random这两个指针所指向的地址,使新链表节点之间查找、连接彼此更加快速。
-
难点:由于该复杂链表有一个随机指向链表中的任意节点或者null的randrom随机指针,使得如何同时记录next指针与random指针成为一个难点。