我有两个问题:
1)将此列表放入"连接"的最快算法是什么?点菜吗?
2)这是一个现有的问题/算法,它有什么名称?
节点始终以循环方式连接,因此我的起始ID无关紧要。
鉴于此清单:
id node1 node2
A 4 9
B 6 1
C 1 3
D 3 10
E 7 2
F 4 2
G 9 8
H 10 5
I 7 5
J 6 8
node1& node2不是特定的顺序,因此id A可以是4 - 9,也可以是9 - 4。
输出应该是这个(如果它以A开头并不重要,只要输出是一个链)。
node ids: 4 - 9 - 8 - 6 - 1 - 3 - 10 - 5 - 7 - 2 - 4
ids: A G J B C D H I E F
(我在C#中编写代码。但是任何语言的伪代码都可以)
答案 0 :(得分:2)
我相信您正在寻找Eulerian path
答案 1 :(得分:1)
我不知道这是否是一些有名的数学问题。这是伪代码,它允许您以O(N)方式解决问题(复杂性和内存使用)。
1)创建数组(我们假设节点具有范围[0..N-1]中的唯一ID。并用节点填充它(id的节点应放在id位置)
2)选择任何节点并将其推送到单独的列表中(它将包含节点在"循环"顺序)。该列表中的最后一个节点将被命名为已处理节点。
3)在每一步中从1迭代到N -1选择不遍历已处理节点的邻居。将这种未遍历的节点推入循环列表。继续处理
注意:检查"未遍历"属性可以O(1)方式执行。只是看,它已经出现在循环列表中。它应该是最后(已处理)节点的邻居
主要缺点 - 假设这种算法存在唯一的欧拉路径。
答案 2 :(得分:1)
这是一个使用字典(哈希表)进行计算的提案。
我已命名" cell"问题中提供的一行表格(但我们不知道您的数据结构)。
似乎是O(n),因为词典提供了O(1)检索。
所有这些都假定初始数据与问题一致(至少我理解它)。
代码在C#中并进行了评论。如果评论的解释不够,请告诉我。
class Program
{
class Cell
{
public string Id { get; set; }
public int Node1 { get; set; }
public int Node2 { get; set; }
public int Min { get { return Math.Min( Node1, Node2 ); } }
public Cell( string name, int node1, int node2 )
{
Id = name;
Node1 = node1;
Node2 = node2;
}
public override string ToString()
{
return Id + "(" + Node1.ToString() + "," + Node2.ToString() + ")";
}
}
static void Main( string[] args )
{
var A = new Cell( "A", 4, 9 );
var B = new Cell( "B", 6, 1 );
var C = new Cell( "C", 1, 3 );
var D = new Cell( "D", 3, 10 );
var E = new Cell( "E", 7, 2 );
var F = new Cell( "F", 4, 2 );
var G = new Cell( "G", 9, 8 );
var H = new Cell( "H", 10, 5 );
var I = new Cell( "I", 7, 5 );
var J = new Cell( "J", 6, 8 );
var cells = new List<Cell>() { A, B, C, D, E, F, G, H, I, J };
// A dictionary to store the cells corresponding to each node values
Dictionary<int, List<Cell>> dict = new Dictionary<int, List<Cell>>();
// Add all the cells to the dictionary
foreach ( var cell in cells )
AddCell( dict, cell );
// Start with arbitrary first cell and remove it from the dictionary
var currentCell = GetCell( dict, A.Node1 );
RemoveCell( dict, currentCell );
// The result is a list of cells initialized with the first cell
var result = new List<Cell>() { currentCell };
// Calculation loop
bool doContinue = true;
while ( doContinue )
{
// Tries to get a next cell from the node1 of current cell...
var nextCell = GetCell( dict, currentCell.Node1 );
// ... or if not found, tries to get it from node2
if ( nextCell == null )
nextCell = GetCell( dict, currentCell.Node2 );
if ( nextCell == null )
// If not found, we stop
{
doContinue = false;
}
else
// If found
{
// Add the next cell to the result
result.Add( nextCell );
// Remove the cell
RemoveCell( dict, nextCell );
}
// The next cell becomes the current cell
currentCell = nextCell;
}
}
// Adding a cell puts the cell against its two nodes values
static void AddCell( Dictionary<int, List<Cell>> dict, Cell cell )
{
List<Cell> cells = null;
if ( dict.TryGetValue( cell.Node1, out cells ) == false )
{
cells = new List<Cell>();
dict[cell.Node1] = cells;
}
cells.Add( cell );
if ( dict.TryGetValue( cell.Node2, out cells ) == false )
{
cells = new List<Cell>();
dict[cell.Node2] = cells;
}
cells.Add( cell );
}
// Gets a cell from a node value
static Cell GetCell( Dictionary<int, List<Cell>> dict, int node )
{
var cell = null as Cell;
var cells = dict[node];
if ( cells.Count > 0 )
cell = cells.First();
return cell;
}
// Removes a cell from the dictionary for both node1 and node2 entries.
static void RemoveCell( Dictionary<int, List<Cell>> dict, Cell cell )
{
dict[cell.Node1].Remove( cell );
dict[cell.Node2].Remove( cell );
}
}
答案 3 :(得分:1)
起点是一个列表或数组,如:
1 {A, 4, 9}
2 {B, 6, 1}
3 {C, 1, 3}
4 {D, 3,10}
5 {E, 7, 2}
6 {F, 4, 2}
7 {G, 9, 8}
8 {H,10, 5}
9 {I, 7, 5}
10 {J, 6, 8}
如果我们可以将其更改为这样的列表或数组:
1 {C, 1, 3}
2 {F, 2, 4} (nodes swapped)
3 {D, 3,10}
4 {A, 4, 9}
5 {I, 5, 7} (nodes swapped)
6 {B, 6, 1}
7 {E, 7, 2}
8 {J, 8, 6} (nodes swapped)
9 {G, 9, 8}
10 {H,10, 5}
根据node1排序,然后我们可以将这个列表或数组作为链表读取:
start with item 1: {C, 1, 3}
read node2: 3
skip to item 3: {D, 3,10}
read node2: 10
skip to item 10: {H,10, 5}
...
skip to item 6: {B, 6, 1}
read node2: 1
end of list
result: C D H I E F A G J B
创建列表的第二个版本可以通过交换列表中的项目,或通过将项目复制到新列表(如果您有空间)就地完成。
唯一需要注意的是,有时您可能需要交换两个节点。在就地重新订购时,可能会这样:
look at item 1: {A, 4, 9}
if item 4 has a node1 different from 4, swap item 1 and 4
else, change item 1 to {A, 9, 4} and swap item 1 and 9
(-> item 1 and 4 swapped)
while current item is already in-place, skip to next
(-> stay at item 1)
look at new item 1: {D, 3,10}
if item 3 has a node1 different from 3, swap item 1 and 3
else, change item 1 to {D,10, 3} and swap item 1 and 10
(-> item 1 and 3 swapped)
while current item is already in-place, skip to next
(-> item 1 is now {C, 1, 3}, so skip to item 2)
...
创建新列表或数组时,这应该更简单:
look at item 1: {A, 4, 9}
if new list has no item 4, copy item 1 as item 4 to the new list
else, change item 1 to {A, 9, 4} and copy as item 9 to the new list
move to next item
如您所见,无需多次遍历列表;每个项目都被交换或复制一次,其目标由其node1或node2确定。
更新
我刚刚意识到订购商品的步骤数量可能(多)大于上述数量。如果是你首先将{A,4,8}移动到位置4并将{B,5,9}移动到位置5,然后你遇到{C,4,5},你就被卡住了。然后,您必须将{C,4,5}与其他两个项之一交换,交换另一个项中的节点,然后将其移动到位。这个新位置本身已经可以采取,等等,所以无法知道这两个选项中哪一个是较小的邪恶。在最坏的情况下,掉期数量将接近N 2 。
更新2
上述问题当然可以通过将项目存储为双向链表来解决。当你遇到例如{A,4,8},您将{A,8}存储为第4项,{A,4}存储为第8项,然后对于{B,5,9}存储{B,9}是第5项和{B ,5}作为项目9,然后对于{C,4,5},您添加到已存储的项目,以便项目4变为{A,8,C,5},项目5变为{B,9,C ,4}。然后,您可以在两个方向上遍历双向链表。这将增加需要完成的工作和使用的空间,但它仍然与列表中的项目数成线性关系。
答案 4 :(得分:1)
您可以通过以下方式执行此操作:
static IEnumerable<Edge<T>> OrderEdges<T>(this IEnumerable<Edge<T>> edges)
where T : IEquatable<T>
{
Debug.Assert(edges.Any());
var map = new Dictionary<T, Edge<T>>();
foreach (var e in edges)
{
if (map.ContainsKey(e.Node1))
{
Debug.Assert(!map.ContainsKey(e.Node2));
map.Add(e.Node2, e);
}
else
{
map.Add(e.Node1, e);
}
}
var current = edges.First();
var orderedEdges = new HashSet<Edge<T>>();
while (true)
{
orderedEdges.Add(current);
yield return current;
if (orderedEdges.Count == map.Count)
break;
var next = map[current.Node2];
current = orderedEdges.Contains(next) ? map[current.Node1] : next;
}
}
Edge<T>
类只是:
class Edge<T> where T: IEquatable<T>
{
public T Node1 { get; }
public T Node2 { get; }
public string Name { get; }
public Edge(string name, T node1, T node2)
{
Name = name;
Node1 = node1;
Node2 = node2;
}
public override string ToString() => Name;
}
如果我们经营这个小家伙:
var edges = new List<Edge<int>>() {
new Edge<int>("A", 4, 9), new Edge<int>("B", 6, 1),
new Edge<int>("C", 1, 3), new Edge<int>("D", 3, 10),
new Edge<int>("E", 7, 2), new Edge<int>("F", 4, 2),
new Edge<int>("G", 9, 8), new Edge<int>("H", 10, 5),
new Edge<int>("I", 7, 5), new Edge<int>("J", 6, 8) };
Console.WriteLine(string.Join(" -> ", edges.OrderEdges()));
我们得到了预期的结果:
A -> G -> J -> B -> C -> D -> H -> I -> E -> F
请注意,此解决方案预先假定输入数据格式正确。