合肥工业大学 2023级 数据结构 实验二 完整报告
1.实验目标
- 熟练掌握线性表的链式存储结构。
- 熟练掌握单链表的有关算法设计和实现。
- 根据具体问题的需要,设计出合理的表示数据的链式存储结构,并设计相关算法。
2.实验要求
- 本次实验中的链表结构指带头结点的单链表;
- 单链表结构和运算定义,算法的实现以库文件方式实现,不得在测试主程序中直接实现;比如存储、算法实现放入文件:linkedList.h
- 程序运行、测试正确;
- 实验程序有较好可读性,各运算和变量的命名直观易懂,符合软件工程要求;
- 程序有适当的注释,程序的书写要采用缩进格式。
3.实验内容
编写算法实现下列问题的求解。
1.
将单链表L中的奇数项和偶数项结点分解开(元素值为奇数、偶数),分别放入新的单链表中,然后原表和新表元素同时输出到屏幕上,以便对照求解结果。
实验测试数据基本要求:
第一组数据:单链表元素为 (1,2,3,4,5,6,7,8,9,10,20,30,40,50,60)
第二组数据:单链表元素为 (10,20,30,40,50,60,70,80,90,100)
2.
求两个递增有序单链表L1和L2中的公共元素,放入新的单链表L3中。
实验测试数据基本要求:
第一组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10,18,20,30)
第二组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (2,4,5,7,8,9,12,22)
第三组
第一个单链表元素为 ()
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10)
3.
删除递增有序单链表中的重复元素,要求时间性能最好。
实验测试数据基本要求:
第一组数据:单链表元素为 (1,2,3,4,5,6,7,8,9)
第二组数据:单链表元素为 (1,1,2,2,2,3,4,5,5,5,6,6,7,7,8,8,9)
第三组数据:单链表元素为 (1,2,3,4,5,5,6,7,8,8,9,9,9,9,9)
4.
递增有序单链表L1、L2,不申请新结点,利用原表结点对两表进行合并,并使得合并后成为一个集合,合并后用L1的头结点作为头结点,删除多余的结点,删除L2的头结点。要求时间性能最好。
实验测试数据基本要求:
第一组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10,18,20,30)
第二组
第一个单链表元素为 (1,3,6,10,15,16,17,18,19,20)
第二个单链表元素为 (2,4,5,7,8,9,12,22)
第三组
第一个单链表元素为 ()
第二个单链表元素为 (1,2,3,4,5,6,7,8,9,10)
5.
已知一个带有表头结点的单链表,结点结构如下图。假设该链表只给出了头指针list。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,算法输出该结点的data值,并返回1;否则,只返回0。要求:
(1)描述算法的基本设计思想
(2)描述算法的详细实现步骤
(3)根据设计思想和实现步骤,采用程序设计语言描述算法(使用C 或C++语言实现),关键之处请给出简要注释。
4.数据结构设计
// 定义单链表的节点结构体
typedef struct slNode
{
int data; // 节点存储的数据
slNode *next; // 指向下一个节点的指针
} node, *linkList; // node为节点类型,linkList为指向节点的指针类型
5.算法设计
1.
【算法设计思想】
- 创建两个新的链表头节点,分别表示奇数链表和偶数链表。
- 初始化指针p1和p2分别指向奇数链表和偶数链表的尾部。
- 使用指针p遍历原链表,根据节点数据的奇偶性创建新节点,并将新节点添加到对应的奇数链表或偶数链表的尾部。
- 更新奇数链表和偶数链表的尾指针。
- 继续遍历原链表,直到所有节点处理完毕。
【算法描述】
// 奇偶分类函数
// 参数L为原链表的头指针,L1为奇数链表的头指针,L2为偶数链表的头指针
void classification(linkList L, linkList &L1, linkList &L2)
{
L1 = new node; // 创建奇数链表的头节点
L1->next = NULL;
L2 = new node; // 创建偶数链表的头节点
L2->next = NULL;
node *p1 = L1; // p1作为奇数链表的尾指针
node *p2 = L2; // p2作为偶数链表的尾指针
node *p = L->next; // p遍历原链表
// 遍历原链表,根据节点数据的奇偶性分别添加到奇数链表和偶数链表中
while (p)
{
node *u = new node; // 创建新节点
u->data = p->data; // 设置新节点的数据
u->next = NULL; // 新节点的下一个节点为空
if (p->data % 2) // 如果节点数据为奇数
{
p1->next = u; // 将新节点添加到奇数链表尾部
p1 = p1->next; // 更新奇数链表的尾指针
}
else // 如果节点数据为偶数
{
p2->next = u; // 将新节点添加到偶数链表尾部
p2 = p2->next; // 更新偶数链表的尾指针
}
p = p->next; // 继续遍历原链表
}
}
2.
【算法设计思想】
- 创建一个新的链表L3,用于存储公共元素。
- 初始化指针u,i和j,分别用于遍历新链表L3、链表L1和链表L2。
- 遍历链表L1和L2,比较它们的元素:
- 如果L1中的元素大于L2中的元素,则移动L2的遍历指针。
- 如果L1中的元素小于L2中的元素,则移动L1的遍历指针。
- 如果L1和L2中的元素相等,则将该元素添加到新链表L3中,并更新指针u,i和j。
【算法描述】
// 寻找重复元素函数
// 参数L1和L2为两个链表的头指针,返回包含公共元素的链表头指针
linkList findSame(linkList L1, linkList L2)
{
linkList L3 = new node; // 创建新链表L3,用于存储公共元素
L3->next = NULL;
node* u = L3; // u作为新链表L3的尾指针
node* i = L1->next; // i遍历链表L1
node* j = L2->next; // j遍历链表L2
// 遍历L1和L2
while (i != NULL && j != NULL)
{
if (i->data > j->data) // 如果L1中的元素大于L2中的元素
{
j = j->next; // 移动L2的遍历指针
}
else if (i->data < j->data) // 如果L1中的元素小于L2中的元素
{
i = i->next; // 移动L1的遍历指针
}
else // 如果L1和L2中的元素相等
{
node* r = new node; // 创建新节点
r->data = i->data; // 设置新节点的数据
r->next = NULL; // 新节点的下一个节点为空
u->next = r; // 将新节点添加到L3尾部
u = r; // 更新L3的尾指针
i = i->next; // 移动L1的遍历指针
j = j->next; // 移动L2的遍历指针
}
}
return L3; // 返回包含公共元素的链表L3
}
3.
【算法设计思想】
- 首先,检查链表的长度是否小于2,如果是,则无需去重,直接返回false。
- 然后,初始化两个指针b和f,其中b作为慢指针,f作为快指针,分别指向链表的第一个元素和第二个元素。
- 接下来,使用一个临时节点temp来删除重复元素。在一个while循环中,不断比较快指针f指向的元素和慢指针b指向的元素,如果它们不相等,则将不重复的元素连接到链表,并更新慢指针b和快指针f。
- 如果发现重复元素,就将重复元素的指针保存到temp中,然后更新快指针f,同时删除重复元素。
- 然后,将慢指针b的next指针指向快指针f,以确保链表的末尾正确连接。
- 最后,返回true,表示去重成功。
【算法描述】
// 从链表中删除重复的元素
// 参数L为链表的头指针,如果去重成功返回true,否则返回false
bool removeDuplicates(linkList L)
{
// 如果列表长度小于2,无需去重
if (L->next->next == NULL) return false;
// 初始化索引
node* b = L->next; // b作为慢指针
node* f = b->next; // f作为快指针
node *temp; // 临时节点用于删除重复元素
while (f != NULL)
{
// 如果当前元素不等于索引处的元素,说明找到了一个新的不重复元素
if (f->data != b->data)
{
b->next = f; // 将不重复的元素连接到链表
b = f; // 更新慢指针
f = f->next; // 更新快指针
continue;
}
temp = f; // 保存重复元素的指