笔者是一名大一新生,写博文的目的是记录自己在学习嵌入式和C语言过程中的一些启发,同时能和大家一起交流学习,如果写的不好请谅解。(注:本文使用编程语言是C语言)
笔者在学习树的过程中受到多位博主和AI的启发,想到用树来构建多级菜单。
目录
#1.树的基础概念
树是一种非线性、层次化的基础数据结构,核心用于表达“一对多”关系,基本概念可概括为: 由n(n≥0)个节点和n-1条边组成,核心特征是无环和连通。
树的节点:根节点(最顶层,无父节点)、叶节点(无孩子节点),分支节点(有孩子节点)等等。其中同一父节点的子节点互为兄弟节点。
度:节点的度与树的度概念略有不同。节点的度是其拥有的孩子节点数量,树的度为所有节点的最大度数。
其余知识点:节点的深度是到根节点的路径边数,而高度是到最远叶子节点的路径边数;任意两节点间有且仅有一条路径。树的知识点和分类还有很多,这里我们不过多扩展,有兴趣的可以自行学习。
#2.多级菜单代码实现
通过了解树的知识我们会发现,树的结构和多级菜单相当符合,上下级菜单的关系就可以用父母节点与孩子节点的关系表示,而同级菜单之间的关系就是兄弟节点的关系。以下是代码实现:
树节点的结构体:
#define MAX_CHILD_NUM 5 //一个节点能指向的最多孩子数
typedef struct menu {
void (*show)(); //菜单页面的显示函数
void(*fun)(); //菜单页面的操作函数
struct menu* child[MAX_CHILD_NUM] ; //储存指向孩子节点指针的指针数组
struct menu* parent ; //指向父节点的指针
int currentindex; //当前菜单页面的索引
int childindex; //孩子节点的索引(判断孩子是否已满)
}MENU;
创建树节点:
MENU* CreatMenuTreeNode(void(*fun)(),void (*show)())
{
MENU* p = (MENU*)malloc(sizeof(MENU)); //分配空间
p->fun = fun;
p->show = show;
p->childindex = 0; //索引初始化为0;
p->parent = NULL ;
for (int i = 0; i < MAX_CHILD_NUM; i++)
{
p->child[i] = NULL; //指针指向NULL
}
return p;
}
连接父母与孩子节点:
int ConnectMenuTree(MENU*parentNode,MENU*childNode)
{
if (parentNode == NULL || childNode == NULL|| parentNode->childindex >= MAX_CHILD_NUM)
{
return -1; //失败返回-1
}
parentNode->child[ parentNode->childindex++] = childNode; //连接孩子节点
childNode->parent = parentNode; //连接父母节点
return 0; //连接成功返回0
}
主函数实例:
int main()
{
MENU* MainMenu = CreatMenuTreeNode(menu1_fun, menu1_show);
MENU* FirstChildMenu = CreatMenuTreeNode(menu2_fun, menu2_show);
MENU* CurrentMenu = MainMenu; //当前菜单
while(1)
{
CurrentMenu->fun(); //菜单操作函数
CurrentMenu->show(); //菜单显示函数
}
return 0;
}
#3.补充总结
在树节点的操作函数中需要添加切换到下一级菜单的操作和返回上一级菜单的操作(这个易于实现),举个简单例子:
if(......)
{
CurrentMenu = CurrentMenu->child[1];
}
具体的菜单页面可以用简单的图形库(Easyx库等)去绘制,这里不多赘述。而如果是嵌入式开发的话,也可以使用这个思路去实现。
关于返回上一级菜单的思路,笔者有受到AI的启发,或许可以构建一个栈来存储上一级菜单的指针,返回时让栈依次吐出即可。具体实现还未动手,如果有新的想法,可以互相交流学习一下。这是笔者的第一篇文章,哪里学的不好或者有问题请指正,以后可能还会发一下算法和嵌入式这两方面的内容。谢谢观看。
下午刚写完这篇文章,晚上就想着把上文提到的用栈的实现菜单返回给做一下。也是成功做出来了哈哈哈。
具体代码如下。
#4.栈在多级菜单的应用
栈的结构体:
typedef struct MenuStack {
MENU* menuStack[MAX_MENU_LEVEL];
int Stacktop;
}MenuStack;
栈的初始化:
void StackInit(MenuStack* MenuBack)
{
if (MenuBack == NULL)
{
return ;
}
for (int i = 0; i < MAX_MENU_LEVEL; i++)
{
MenuBack->menuStack[i] = NULL; //栈内指针全部赋为0
}
MenuBack->Stacktop = -1; //栈顶赋为-1 表示栈空
}
将当前菜单压入栈:
void StackPush(MenuStack* MenuBack,MENU*MenuCurrent)
{
if (MenuBack == NULL||MenuCurrent==NULL)
{
printf("空指针");
return;
}
if (MenuBack->Stacktop >= MAX_MENU_LEVEL -1 )
{
printf("栈满");
return ;
}
MenuBack->Stacktop++;
MenuBack->menuStack[MenuBack->Stacktop] = MenuCurrent; //将当前的菜单压入栈中
}
返回上一页菜单:
MENU* StackPop(MenuStack* MenuBack)
{
if (MenuBack == NULL)
{
printf("空指针");
return NULL;
}
if (MenuBack->Stacktop == -1)
{
printf("栈空");
return NULL;
}
MENU* temp = MenuBack->menuStack[MenuBack->Stacktop]; // 作为函数返回值
MenuBack->menuStack[MenuBack->Stacktop] = NULL; // 删除前一个菜单的记录
MenuBack->Stacktop--; // 栈顶-1
return temp;
}
使用示例;
if(//.......进入下一级菜单)
{
StackPush(MenuBack, CurrentMenu);
CurrentMenu = CurrentMenu->child[0];
}
.......
if (//......按下返回键)
{
CurrentMenu = StackPop(MenuBack);
}
总结:用栈实现菜单返回后,树的每个节点就可以不用存储指向父节点的指针,如果菜单的数量很多,可以节省箱单一部分空间(栈的大小只由菜单的层级有关,相比之下,需要的空间更少)。
1万+

被折叠的 条评论
为什么被折叠?



