一笔画路径生成(c/c++)

2023-10-07 17:50
文章标签 c++ 路径 生成 笔画

本文主要是介绍一笔画路径生成(c/c++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一笔画 路径生成(c++)

练习图的遍历、回溯

新建一个OnePen类;
使用setNodeNum()方法设置节点数量;
使用setNodeJoin()设置节点连线;
执行drawLine()方法即可得出该图的一笔画路线;

  • main.cpp
#include <iostream>
#include "OnePen.h"void test01()
{OnePen op;op.setNodeNum(4);op.setNodeJoin(1, 2);op.setNodeJoin(1, 3);op.setNodeJoin(2, 3);op.setNodeJoin(2, 4);op.printTable();op.drawLine();
}void test02()
{OnePen op;op.setNodeNum(5);op.setNodeJoin(1, 4);op.setNodeJoin(1, 5);op.setNodeJoin(2, 4);op.setNodeJoin(2, 5);op.setNodeJoin(3, 4);op.setNodeJoin(3, 5);op.setNodeJoin(4, 5);op.printTable();op.drawLine();
}int main()
{test02();system("pause");return 0;
}
  • OnePen.h
#pragma once
#include <iostream>
#include <vector>class OnePen
{
private:struct node				// 节点{int data;			// 数据域bool flag;			// 标记(1已使用0未使用)node* next;			// 下一个};node* nodeArray;		// 节点数组int nodeNum;			// 节点数量std::vector<int> path;	// 路径bool checkAlone();bool sign(int p1, int p2, int flag);bool endCheck();int draw(int p);
public:OnePen();void setNodeNum(int num);void setNodeJoin(int begin, int end);void printTable();void drawLine();
};
  • OnePen.cpp
#include "OnePen.h"// 构造函数,初始化成员变量
OnePen::OnePen()
{this->nodeNum = 0;this->nodeArray = NULL;
}// 设置节点数量,并生成初始数组
void OnePen::setNodeNum(int num)
{if (num <= 1){std::cout << "节点数必须大于 1。" << std::endl;}else{this->nodeNum = num;this->nodeArray = new node[num];for (int i = 0; i < num; i++){this->nodeArray[i].data = i;this->nodeArray[i].flag = false;this->nodeArray[i].next = NULL;}}
}// 连接节点
void OnePen::setNodeJoin(int begin, int end)
{// 首尾不能相同if (begin == end){std::cout << "首尾节点不能相同,请重新确认!";std::cout <<"(Error: .setNodeJoin(" << begin << "," << end << ");)" << std::endl;return;}// 其中一节点不存在bool beginExist = false, endExist = false;for (int i = 0; i < this->nodeNum; i++){// 数据存储节点是从0开始,但是从用户角度节点是从1开始,所以需要-1if (this->nodeArray[i].data == begin - 1)beginExist = true;if (this->nodeArray[i].data == end - 1)endExist = true;}if (!beginExist || !endExist){std::cout << "节点不存在,请重新确认!";std::cout << "(Error: .setNodeJoin(" << begin << "," << end << ");)" << std::endl;return;}// 找到 begin 节点的最后一个邻接节点,然后插入新的邻接节点node* beginNode = &this->nodeArray[begin - 1];	// begin节点while (NULL != beginNode->next){if (beginNode->next->data == end - 1){// 已存在从 begin -> end 的路线break;}beginNode = beginNode->next;}if (beginNode->next == NULL){node* temp = new node;temp->data = end - 1;temp->flag = 0;temp->next = NULL;beginNode->next = temp;}// 找到 end 节点的最后一个邻接节点,然后插入新的邻接节点node* endNode = &this->nodeArray[end - 1];		// end节点while (NULL != endNode->next){if (endNode->next->data == begin - 1){// 已存在从 end -> begin 的路线break;}endNode = endNode->next;}if (endNode->next == NULL){node* temp = new node;temp->data = begin - 1;temp->flag = 0;temp->next = NULL;endNode->next = temp;}
}// 输出邻接表
void OnePen::printTable()
{std::cout << "当前邻接表为:" << std::endl;std::cout << "-----------------------------" << std::endl;for (int i = 0; i < this->nodeNum; i++){std::cout << this->nodeArray[i].data + 1 << " | ";node* temp = this->nodeArray[i].next;while (temp != NULL){std::cout << " ->" << temp->data + 1;temp = temp->next;}std::cout << std::endl;}std::cout << "-----------------------------\n" << std::endl;
}// 检查是否存在独立的点(即出度和入度皆为0)
bool OnePen::checkAlone()
{for (int i = 0; i < this->nodeNum; i++){if (this->nodeArray[i].next == NULL){return true;}}return false;
}// 设置标记(p1->p2 的 flag 设置为 flag)
bool OnePen::sign(int p1, int p2, int flag)
{node* it = this->nodeArray[p1].next;while (it != NULL){if (it->data == p2){it->flag = flag;return true;}it = it->next;}return false;
}// 最终检查(邻接表的全部flag都为1即返回true)
bool OnePen::endCheck()
{node* temp;for (int i = 0; i < this->nodeNum; i++){temp = this->nodeArray[i].next;while (NULL != temp){if (temp->flag == 0){return false;}temp = temp->next;}}return true;
}// 开始画线
void OnePen::drawLine()
{// 检查是否存在独立的点if (this->checkAlone()){std::cout << "- 单独的节点:\n\n>>> ";for (int i = 0; i < this->nodeNum; i++){if (this->nodeArray[i].next == NULL){std::cout << this->nodeArray[i].data << "\t";}}std::cout << "\n\n- 请将全部节点连接!\n" << std::endl;return;}// 统计有多少种方法int count = 1;// 从每个节点为初始节点依次走一次for (int i = 0; i < this->nodeNum; i++){// 将全部标签置0for (int j = 0; j < this->nodeNum; j++){node* t = this->nodeArray[j].next;while (t != NULL){t->flag = 0;t = t->next;}}// 将路径清空this->path.clear();// 开始画线draw(i),其中i为初始节点,返回结果为是否走得通if (this->draw(i)){std::cout << "-----------------------------" << std::endl;std::cout << ">>> 第 " << count << " 种解法:\n>>> ";count++;for (int i = 0; i < this->path.size(); i++){if (i == 0)std::cout << this->path[i];elsestd::cout << " ->" << this->path[i];}std::cout << "\n" << std::endl;}}
}// 以point节点为开始节点走下一步
int OnePen::draw(int point)
{// 当前节点添加到路径(由于存储和显示不同,所以+1)this->path.push_back(point + 1);// 完成(到该点已经全部路线都走完了就逐层返回1)if (this->endCheck()){return 1;}node* past = new node;					// 走过节点列表(已经走过并且走不通)node* last = past;						// 走过节点列表 的最后一个节点(方便添加新的节点)int peerNode = -1;						// 目标节点(将要走的节点,初始化为不可能的-1)node* it = this->nodeArray[point].next;	// 当前节点的第一个邻接点// 找到下一个要走的节点,并且标记while (it != NULL){if (it->flag == 0)			// 尚未走过的目标节点{peerNode = it->data;	// 记录目标节点it->flag = 1;			// 标记目标节点past->data = peerNode;	// 目标节点加入past列表past->next = NULL;break;}it = it->next;}// 此路不通(遍历完当前节点的所有邻接点都没找到flag=0的节点即无路可走了)if (it == NULL){return 0;}// 将目标节点到当前节点的线标记this->sign(peerNode, point, 1);// 开始递归,如果递归结果返回0(即此路不通)开始进入循环找新的目标节点while (!this->draw(peerNode)){// 进入循环证明当前past列表的最新元素走不通,移除this->path.pop_back();// 1. 将刚刚走过的标记取消(让其他节点可以走)this->sign(point, peerNode, 0);this->sign(peerNode, point, 0);// 2. 找到一个节点需要即不在past列表里,并且flag为0it = this->nodeArray[point].next;while (it != NULL){if (it->flag == 0){bool b = true; // 检查这个节点是否存在past列表(1这个节点能走,0这个节点不能走)// 遍历走过列表node* ps = past;while (ps != NULL){// 如果当前节点存在走过列表里面,这个节点不能走if (it->data == ps->data){b = false;break;}ps = ps->next;}// 找到目标节点if (b){peerNode = it->data;// 添加到走过列表node* past2 = new node;past2->data = peerNode;past2->next = NULL;last->next = past2;		// 添加到past列表的最后last = past2;			// last指向past列表的最后元素// 标记(当前节点到目标节点的路线已使用)this->sign(point, peerNode, 1);this->sign(peerNode, point, 1);// 退出循环break;}}it = it->next;}// 3. 遍历完邻接点也没找到可用的节点,没有可以走的路线了,此路不通if (it == NULL){return 0;}}
}
  • 测试(上面的main.cpp的test02函数)
    在这里插入图片描述

  • 执行结果
    在这里插入图片描述

这篇关于一笔画路径生成(c/c++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/159426

相关文章

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

nginx生成自签名SSL证书配置HTTPS的实现

《nginx生成自签名SSL证书配置HTTPS的实现》本文主要介绍在Nginx中生成自签名SSL证书并配置HTTPS,包括安装Nginx、创建证书、配置证书以及测试访问,具有一定的参考价值,感兴趣的可... 目录一、安装nginx二、创建证书三、配置证书并验证四、测试一、安装nginxnginx必须有"-

Java实战之利用POI生成Excel图表

《Java实战之利用POI生成Excel图表》ApachePOI是Java生态中处理Office文档的核心工具,这篇文章主要为大家详细介绍了如何在Excel中创建折线图,柱状图,饼图等常见图表,需要的... 目录一、环境配置与依赖管理二、数据源准备与工作表构建三、图表生成核心步骤1. 折线图(Line Ch

MySQL9.0默认路径安装下重置root密码

《MySQL9.0默认路径安装下重置root密码》本文主要介绍了MySQL9.0默认路径安装下重置root密码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录问题描述环境描述解决方法正常模式下修改密码报错原因问题描述mysqlChina编程采用默认安装路径,

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API