最快的循环链表制作工具算法'

时间:2017-02-11 15:35:12

标签: c# algorithm pseudocode

我有两个问题:

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#中编写代码。但是任何语言的伪代码都可以)

5 个答案:

答案 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

请注意,此解决方案预先假定输入数据格式正确。