大家好。以上是我已分配的编码项目。我正在阅读说明,完全迷路了,因为我从未学会过如何编写无向图?不确定教授如何期望我们做到这一点,但我希望可以从专家那里得到一些帮助。你们建议我阅读任何读物(或技巧)以熟悉如何开始使用该程序?谢谢,谢谢!
答案 0 :(得分:0)
要解决的问题称为“单词变形”。您的讲师对使用无向图给出了一些限制,其中相邻节点与原点仅相差一个字符。不幸的是,要求不够明确。 “一个字符的区别是模棱两可的。如果我们使用replace-insert-delete惯用语,那么可以通过比较2个相等大小的字符串来使用其他功能。我认为是完整的方法。
最后,您需要通过图表找到最短的方法。
我可以为您介绍一种可能的解决方案。一个完整的工作代码示例。
通过图的非加权方式,因为从一个节点到下一个节点的旅行成本始终为1。所以实际上,我们在谈论的是无向非加权图。
我们在这里需要使用的主要算法是:
请注意,如果单词的长度相同,则不需要Levensthein。只需逐个字符比较字符并计算差异即可。那很简单。 (但是如前所述:指令有点不清楚)
两种算法都可以修改。例如:您不需要Levensthein距离大于1。您可以在找到距离1后终止距离计算。而且,在广度优先搜索中,您可以显示前进的路径。
好的,现在,如何实现无向图。有两种可能性:
对于这种情况,我建议使用向量方法。矩阵会比较稀疏,因此向量更好。
您需要的基本数据结构是一个包含顶点和邻居的节点。因此,您将单词(a std::string
作为顶点和“邻居”。那就是std::vector
到图中其他节点的索引位置。
图是节点的向量。并且节点邻居指向此向量中的其他节点。我们使用向量中的索引来表示邻居。所有这些我们打包成一个结构,并将其称为“ UndirectedGraph”。我们添加了一个“构建”功能来检查相邻性。在此函数中,我们将每个字符串与任何字符串进行比较,并检查差异是否小于2,因此0或1。0表示相等,而1是给定的约束。如果发现这种差异,则将其添加为相应节点中的邻居。
此外,我们添加了广度优先搜索算法。它在Wikipedia
中有描述为简化该算法的实现,我们向节点添加了“已访问”标志。
请参见下面的代码:
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <iomanip>
#include <numeric>
#include <algorithm>
#include <queue>
std::istringstream textFileStream(R"#(peach
peace
place
plane
plans
plays
slays
stays
stars
sears
years
yearn
)#");
using Vertex = std::string;
using Edge = std::vector<size_t>;
// One node in a graph
struct Node
{
// The Vertex is a std::string
Vertex word{};
// The edges are the index of the neighbour nodes
Edge neighbour{};
// For Breath First Search
bool visited{ false };
// Easy input and output
friend std::istream& operator >> (std::istream& is, Node& n) {
n.neighbour.clear();
std::getline(is, n.word);
return is;
}
friend std::ostream& operator << (std::ostream& os, const Node& n) {
os << std::left << std::setw(25) << n.word << " --> ";
std::copy(n.neighbour.begin(), n.neighbour.end(), std::ostream_iterator<int>(os, " "));
return os;
}
};
// The graph
struct UndirectedGraph
{
// Contains a vector of nodes
std::vector<Node> graph;
// build adjacenies
void build();
// Find Path
bool checkForPathFromStartToEnd(size_t start, size_t end);
bool checkForPath() {bool result = false;if (graph.size() > 1) {size_t s = graph.size() - 2;size_t e = s + 1;result = checkForPathFromStartToEnd(s, e); }return result; }
// Easy input and output
friend std::istream& operator >> (std::istream& is, UndirectedGraph& ug) {
ug.graph.clear();
std::copy(std::istream_iterator<Node>(is), std::istream_iterator<Node>(), std::back_inserter(ug.graph));
return is;
}
friend std::ostream& operator << (std::ostream& os, const UndirectedGraph& ug) {
size_t i{ 0 };
for (const Node& n : ug.graph)
os << std::right << std::setw(4) << i++ << ' ' << n << '\n';
return os;
}
};
// Distance between 2 strings
size_t levensthein(const std::string& string1, const std::string& string2)
{
const size_t lengthString1(string1.size());
const size_t lengthString2(string2.size());
if (lengthString1 == 0) return lengthString2;
if (lengthString2 == 0) return lengthString1;
std::vector<size_t> costs(lengthString2 + 1);
std::iota(costs.begin(), costs.end(), 0);
for (size_t indexString1 = 0; indexString1 < lengthString1; ++indexString1) {
costs[0] = indexString1 + 1;
size_t corner = indexString1;
for (size_t indexString2 = 0; indexString2 < lengthString2; ++indexString2) {
size_t upper = costs[indexString2 + 1];
if (string1[indexString1] == string2[indexString2]) {
costs[indexString2 + 1] = corner;
}
else {
const size_t temp = std::min(upper, corner);
costs[indexString2 + 1] = std::min(costs[indexString2], temp) + 1;
}
corner = upper;
}
}
size_t result = costs[lengthString2];
return result;
}
// Build the adjacenies
void UndirectedGraph::build()
{
// Iterate over all words in the graph
for (size_t i = 0; i < graph.size(); ++i)
// COmpare everything with everything (becuase of symmetries, omit half of comparisons)
for (size_t j = i + 1; j < graph.size(); ++j)
// Chec distance of the 2 words to compare
if (levensthein(graph[i].word, graph[j].word) < 2U) {
// And store the adjacenies
graph[i].neighbour.push_back(j);
graph[j].neighbour.push_back(i);
}
}
bool UndirectedGraph::checkForPathFromStartToEnd(size_t start, size_t end)
{
// Assume that it will not work
bool result = false;
// Store intermediate tries in queue
std::queue<size_t> check{};
// Set initial values
graph[start].visited = true;
check.push(start);
// As long as we have not visited all possible nodes
while (!check.empty()) {
// Get the next node to check
size_t currentNode = check.front(); check.pop();
// If we found the solution . . .
if (currentNode == end) {
// The set resultung value and stop searching
result = true;
break;
}
// Go through all neighbours of the current node
for (const size_t next : graph[currentNode].neighbour) {
// If the neighbour node has not yet been visited
if (!graph[next].visited) {
// Then visit it
graph[next].visited = true;
// And check following elements next time
check.push(next);
}
}
}
return result;
}
int main()
{
// Get the filename from the user
std::cout << "Enter Filename for file with words:\n";
std::string filename{};
//std::cin >> filename;
// Open the file
//std::ifstream textFileStream(filename);
// If the file could be opened . . .
if (textFileStream) {
// Create an empty graph
UndirectedGraph undirectedGraph{};
// Read the complete file into the graph
textFileStream >> undirectedGraph;
Node startWord{}, targetWord{};
std::cout << "Enter start word and enter target word\n"; // teach --> learn
std::cin >> startWord >> targetWord;
// Add the 2 words at the and of our graph
undirectedGraph.graph.push_back(startWord);
undirectedGraph.graph.push_back(targetWord);
// Build adjacency graph, including the just added words
undirectedGraph.build();
// For debug purposes: Show the graph
std::cout << undirectedGraph;
std::cout << "\n\nMorph possible? --> " << std::boolalpha << undirectedGraph.checkForPath() << '\n';
}
else {
// File could not be found or opened
std::cerr << "Error: Could not open file : " << filename;
}
return 0;
}
请注意:尽管已经实现了要求输入文件名的功能,但在本示例中并未使用它。我从istringstream阅读。您需要稍后删除istringstream并在现有语句中添加注释。
关于讲师的要求:我没有使用任何STL /库/ Boost搜索算法。 (做什么用的?在此示例中?)但是我当然使用其他C ++ STL容器。我不会重新发明轮子并提出新的“向量”或队列。而且我绝对不会使用“ new”或C-Style数组或指针算术。
玩得开心!
对其他所有人:对不起:我无法抗拒编写代码。 。