首页 > 编程语言 >一笔画路径生成(c++版)

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

时间:2023-01-12 21:22:07浏览次数:35  
标签:nodeArray 笔画 int 路径 c++ next NULL 节点 op

一笔画 路径生成(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开始,所以需要-1
		if (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++)
	{
		// 将全部标签置0
		for (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];
				else
					std::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为0
		it = 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;
		}
	}
}

标签:nodeArray,笔画,int,路径,c++,next,NULL,节点,op
From: https://www.cnblogs.com/NealLee/p/17047956.html

相关文章

  • C++中的size()、sizeof() 、strlen()、str.length()
    c/c++中获取字符串长度。有以下函数:size()、sizeof()、strlen()、str.length();一、数组或字符串的长度:sizeof()、strlen()1、sizeof():返回所占总空间的字节数2、str......
  • C++分别用顺序栈和链栈实现数制的转换相关代码
    //案例分析:将一个十进制数N转化为八进制数,在计算过程中,使得N模8得到八进制数的各个数依次进栈,//然后将八进制数依次输出,得到八进制数。#include<iostream>#include<cstdlib......
  • 无法将“vue”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写
    安装vue-cli脚手架之后,运行vue命令,报错。试了很多去缓存加环境变量的都没有成功。遇到一个简单且有效的加环境变量的方法运行npmconfiggetprefix显示一段路径地址......
  • 迷宫机器人最短路径使用tkinter绘制
    起因我想要写一个玩家和机器对战的迷宫游戏。这个项目我没有写完,我实现了最短机器人路径并绘制在tkinter上,以及玩家移动的功能。更多的关于GUI的设计太花时间了我没有写完......
  • 自己的devc++的语法配置
    效果如下......
  • c++ std string replaceAll函数
    std提供的string的replace方法,不太方便stringreplaceAll(string&str,stringoldStr,stringnewStr){string::size_typepos=str.find(oldStr);while(pos......
  • C++学习笔记 [ 2 ]
    C++问题的补充前言关于对之前遗留的补充malloc和new的区别const和引用的深入this指针的深入一、C++中对象的创建malloc和new创建对象//定义一个Pointe......
  • C++学习笔记(四)~substr()函数
    substr(pos,len)作用        返回一个新构造的串对象,其值初始化为该对象的子字符串的副本。子字符串是对象的一部分,从字符位置pos开始并跨越len个字符(或直到字符串......
  • C(C++)函数返回多个值
    【Q】C(C++)函数如何返回多个值?【A】1、指针:4票2、结构体:4票 返回多个数据,并且各个数据类型都不相同。 直接返回结构体,程序执行效率会受到影响。因为需要复制......
  • C++不要对函数返回值添加std::move()
    C++不要对函数返回值添加std::move()ReferencesC++函数返回局部变量的std::move()问题?ReturnStatementCopyelisionSummary编译器会进行返回值优化——复制省......