C语言数据结构课设:带环相交链表及其UI界面
设计目的:
掌握链表的基本操作。
掌握带环链表的相关操作算法。
内容:
1. 输入数据元素构造单链表后,将元素值为 m 和 n(从键盘输入,如有多个相同元素值,
仅考虑首个出现的元素)的节点建立连接,注意判断节点出现的先后关系,将后面出现
的节点(假设为 n)的链域连到先出现的节点(假设为 m),将原 n 节点的后续节点搬
迁到原单链表的头部,形成以下双头相交链表(如果使用带头结点的链表,搬迁过程中
请自行额外增加一个头节点);
2. 判断链表是否有环路,请使用环路判断函数(输入为链表头指针),支持使用 2 个不同的链表头部指针进行查询。
3. 将环路链接取消,恢复成单链表,并根据表头指针(输入)求解链表的中间节点(输出
中间节点的值并返回指针),注意:不要将链表转换成数组来求解;
需求分析:
考虑到程序是给用户使用的,方便用户使用是很重要的设计原则,需要一个界面友好,并且提供程序测试方案的程序。具体需求如下:
- 需要一个基于easyx的UI图形界面菜单来执行程序要求;
- 需要构建带头结点的单链表,根据元素输入,修改删除节点,图形化打印链表;
- 需要一个函数创建双头相交链表;
- 需要一个函数判断是否存在环路;
- 需要一个函数还原双头相交链表并求取链表中间结点;
链表基础函数:
LinkList InitList();
初始条件:无;
操作结果:初始化一个结点,返回头结点;
int ListDetele(LinkList head);
初始条件:一个含头结点的单链表的头结点,输入结点序号;
操作结果:删除该序号节点;
int ListGet(LinkList head, int i, DataType x);
初始条件:一个含头结点的单链表的结点,结点序号,待赋值的变量;
操作结果:删除单链表的一个元素,将该元素赋值给变量;
int ListInsert(LinkList head);
初始条件:一个含头结点的单链表的头结点,输入结点序号及待插入元素;
操作结果:在该序号结点前插入一个元素,返回头结点;
int ListInsertx(LinkList head);
初始条件:一个含头结点的单链表的头结点,输入结点元素及待插入元素;
操作结果:在多个该序号元素前插入一个元素;
int ListLength(LinkList L);
初始条件:一个含头结点的单链表的头结点;
操作结果:返回单链表长度;
void Destroy(LinkList* head);
初始条件:一个含头结点的单链表的头结点;
操作结果:销毁单链表;
void PrintList(LinkList L);
初始条件:一个含头结点的单链表的头结点;
操作结果:输出打印该单链表;
程序特殊功能函数:
LinkList CreatePhoto(LinkList L); //当需要图形化链表时加入所需元素数据,可以节省空间
初始条件:一个含头结点的单链表的头结点;
操作结果:将构造图形结构体元素基于data赋值,返回头结点;
void Rectangle(Rec r); //形成长方形,可以构建可视化链表
初始条件:一个构造图形结构体变量;
操作结果:生成基于该变量储存元素来构成的矩形与三角形并在easyx绘图窗口打印;
void Show(LinkList L); //用图形展示当前单链表
初始条件:一个含头结点的单链表的头结点;
操作结果:通过图形界面打印构造的图形及data数据,可视化打印呈现链表;
LinkList function2(LinkList L); //创建带环相交链表,返回第二指针
初始条件:一个含头结点的单链表的头结点;
操作结果:构造带环相交链表,返回第二个头结点;
void function3(LinkList L, LinkList S); //快慢指针法判断是否成环
初始条件:带环相交链表的两个头结点或者两个单链表的头结点;
操作结果:快慢指针判断是否成环,并输出;
LinkList function4(LinkList L, LinkList S); //还原单链表,输出中间值,返回中间结点
初始条件:带环相交链表的两个头结点;
操作结果:将带环相交链表还原成单链表,输出中间值,返回中间结点;
//以上是代码函数部分的声明。
具体实现展示:
先进行三种构建链表函数的调用,确保一开始存在函数
进入二级菜单(此处也可改造成UI图形界面)进行操作
可视化展现链表(仅限单链表)
构成带环相交链表,分为三部分来展示
选择指针来判断是否成环
还原成单链表,输出中间结点
还原后判断无环
部分代码实现:
声明及头文件
#include <stdio.h>
#include <graphics.h> //引用图形库头文件
#include <conio.h>
#include <time.h>
#include <windows.h>
#include <math.h>
#define WIDTH 900
#define HEIGHT 500 //窗口宽高
typedef int DataType; //定义数据类型
typedef struct
{
int right;//右边框的横坐标
int top;//上边框的纵坐标
int left;//左边框的横坐标
int bottom;//下边框的纵坐标
int g[200];//记录数据
int gl; //记录数据位数
}Rec; //矩形结构体,防止后期遗漏
typedef struct node {
DataType data;
Rec r;
struct node* next;
}ListNode, * LinkList;
//ListNode* p;
//LinkList head; //linklisth为地址,专门定义头指针
int wall = 0;
/两种构建链表方法/
LinkList CreatListS(); //自输入创建链表
LinkList creatrandom(); //创建含有自定义随机数的数组链表
///链表基础函数///
LinkList InitList();
int ListDetele(LinkList head);
int ListGet(LinkList head, int i, DataType x);
int ListInsert(LinkList head);
int ListInsertx(LinkList head);
int ListLength(LinkList L);
void Destroy(LinkList* head);
void PrintList(LinkList L); //输出链表
/程序特殊功能函数/
LinkList CreatePhoto(LinkList L); //当需要图形化链表时加入所需元素数据,可以节省空间
void Rectangle(Rec r); //形成长方形,可以构建可视化链表
void Show(LinkList L); //用图形展示当前单链表
LinkList function2(LinkList L); //创建带环相交链表,返回第二指针 circlemaker
void function3(LinkList L, LinkList S); //快慢指针法判断是否成环
LinkList function4(LinkList L, LinkList S); //还原单链表,输出中间值,返回中间结点
void menu(LinkList L, LinkList S); //主菜单函数
void welcome(LinkList L, LinkList S); //图形界面菜单
void printf_back(LinkList L, LinkList S); //返回
部分功能函数
LinkList function2(LinkList L) { //返回第二指针 circlemaker
system("cls");
PrintList(L);
LinkList S=NULL;
int g = 0, m = 0, n = 0,h=0;
DataType a = 0, b = 0; //a,b为元素值
printf("\n\n\n\t\t\t请输入您想要的a,b值\n\t\t\t");
scanf_s("%d %d", &a, &b);
ListNode* p, * q = NULL, * k = NULL;
p = (node*)malloc(sizeof(node)); //生成新的结点
if (!p) {
printf("头结点创建失败,空间不足");
exit(0);
}
p = L;
while (g < ListLength(L)) //一次循环可以查找出两个指针,且可以指向尾指针
{
p = p->next; //使P最终指向末尾的指针
if (!q) {
if (p->data == a)
{
//q = (node*)malloc(sizeof(node)); //生成新的结点
q = p;
}
}
if (!k) {
if (p->data == b)
{
//k = (node*)malloc(sizeof(node)); //生成新的结点
k = p;
}
}
{if (k && !q) h = 1;}
g++;
}
if (!(q && k))
{ printf("\n\t\t\t此链表没有您的预期值是否重新输入 ");
int a;
printf("\n\n 输入 1 重新输入,0 退出程序: ");
scanf_s("%d", &a);
if (a == 0)
exit(0);
else
function2(L);
}
if(h==1) { LinkList temp = k; k = q; q = temp; }
S = (node*)malloc(sizeof(node)); //生成新的结点
if (!S) {
printf("头结点创建失败,空间不足");
exit(0);
} //相等也包含在其中
ListNode* o=NULL,*R=NULL;
o = L;
printf("\n\t\t\t链表如下:\n");
if(k->next!=NULL){
wall = 1;
S->next = k->next;
R = k->next;
p->next = q;
k->next = q;
printf("\n\n\t\t\t头结点S到相交处 ");
do
{ printf("->%d", R->data);
R = R->next;
}while (R !=k);
printf("->%d", R->data);
}
wall++;
p->next = q;
k->next = q;
printf("\n\t\t\t公共部分: ");
do
{
printf("->%d", q->data);
q = q->next;
}while (q != k->next);
printf("\n\t\t\t头结点L到相交处 ");
do
{
o = o->next;
printf("->%d", o->data);
} while (o != k);
return S;
}
调试报告:
编译时的错误:在本次程序设计过程中,我碰到了最多的问题就是指针它为野指针以及访问权限冲突两个错误,碰到这种问题,我想尽办法才解决,关键是他的问题一般不存在于报错的位置,往往存在于之前构造函数处,这个主要原因是因为,构造函数时出现的逻辑错误,导致不能运行,唯有在调试的时候,系统才会报错,这个问题的研究,十分有意义,提升了自我,让我知道了链表的神奇,在程序的调试过程中此报错存在于Function2以及Function3中,通过改变他的逻辑结构来解决此问题。
调试方案:
进入菜单一,选择测试构建单链表,进入二级菜单,选择(6)构建带环相交链表,输入出现的链表中的两个元素,选择(1)返回菜单,继续操作,选择(7)判断是否有环,返回选择(8)还原链表。PS:若要修改单链表请在构建带环相交链表之前进行。
欢迎各位在评论区留言讨论相关问题,本代码还存在需要提高的地方,若需要原全部代码请私信,谢谢!