简体   繁体   English

我将如何在一个简单的有向图中倒数?

[英]How would I go about counting backwards in a simple directed graph?

So I have this graph with books I'm iterating through and printing them out. 所以我有这张图,我正在遍历并打印出来。

public class Books : IBookFinder
{
    private Books(Books next, string book)
    {
        Next = next;
        Book = book;
    }

    public Books Next { get; }
    public string Book { get; }

    public Books Previous(string book)
    {
        return new Books(this, book);
    }

    public static Books Create(string book)
    {
        return new Books(null, book);
    }

    public string FromLeft(Books books, int numberFromLeft)
    {
        for (int i = 1; i < numberFromRight; i++)
        {
            books = books?.Next; //Go through the books and return null if books is null.
        }

        return books.book; //Should probably check for null here as it crashes if the input number is out of book range (something else than 1-4)
    }

    public string FromRight(Books books, int numberFromRight
    {
        //How to implement this bad boy?
    }
}

All is well and good, but I want to implement a method FromRight so that I can write out the name of the book from it's placement in the graph, given a number input. 一切都很好,但是我想实现一个FromRight方法,以便在给定数字输入的情况下,从书本在图中的位置写出书名。 For example, if inputting "3", it should output "Lord of the Rings". 例如,如果输入“ 3”,则应输出“指环王”。 How would I go about doing that? 我将如何去做? Any hints greatly appreciated. 任何提示,不胜感激。

class Program
{
    static void Main(string[] args)
    {
        var curr = Books
            .Create("Harry Potter")
            .Previous("Lord of the Rings")
            .Previous("Twilight")
            .Previous("Da Vinci Code");

        while (curr != null)
        {
            if (curr.Next != null)
            {
                Console.Write(curr.Book + " --- ");
            }
            else
            {
                Console.WriteLine(curr.Book);
            }
            curr = curr.Next;
        }

        Console.WriteLine("Input number to pick a book");

        var bookNumber = Console.ReadLine();
        int n;

        if (int.TryParse(bookNumber, out n)) //Checking if the input is a #
        {

        }
        else
        {
            Console.WriteLine("Input was not a number!");
        }
        Console.WriteLine(bookNumber);

        Console.ReadLine();
    }
}

UPDATE: 更新:

I've managed to figure out a way to do it, without having to make a doubly linked list, even though that is of course probably the optimal solution for this problem. 我设法找到了一种方法,而不必制作双向链表,尽管这当然可能是解决此问题的最佳方法。

I've made a helper function Count(), which takes the list and counts entries: 我制作了一个辅助函数Count(),该函数接受列表并计数条目:

    private int Count(Books books)
    {
        int count = 1;
        while (books.Next != null)
        {
            books = books.Next;
            count++;
        }

        return count;
    }

I then use the return value of this method to select books from the right: 然后,我使用此方法的返回值从右侧选择书籍:

    public string FromRight(Books books, int numberFromRight)
    {
        var bookCount = Count(books); //Getting the amount of books.
        for (int i = numberFromRight; i < bookCount; i++)
        {
            books = boos?.Next; 
        }

        return books.Book;
    }

Since you are creating this like a linked list, to go backwards you need to create a doubly linked list: https://en.wikipedia.org/wiki/Doubly_linked_list 由于您像创建链表一样创建此列表,因此要向后移动,您需要创建一个双链表: https : //en.wikipedia.org/wiki/Doubly_linked_list

Your current code has no representation of the previous node. 您当前的代码没有上一个节点的表示。 Set the previous node as that of the current while creating the next, then its iterating is just like you did originally. 在创建下一个节点时,将上一个节点设置为当前节点,然后像您最初那样对其进行迭代。

Iterate until there is no Next, then print until there is no previous. 迭代直到没有下一个,然后再打印直到没有上一个。

A single-linked list can only be iterated in a single direction, so it's not possible to go backwards. 单链列表只能在单个方向上进行迭代,因此不可能向后移动。

To get the nth element from the left, you simply need to go next n times. 要从左边获得第n个元素,您只需要前进n次即可。 But to figure out elements from the right, you first need to get to the end. 但是要从右边找出元素,您首先需要结束。 So to get the last element, you need to go next as often as possible. 因此,要获取最后一个元素,您需要尽可能多地进行下一步。 To get the next to last element, you need to go to the end and return the element before that. 要获得倒数第二个元素,您需要转到末尾并在此之前返回该元素。

You may see where this is going: In order to get the nth element from the right, you need to remember the last n elements while iterating through the linked list. 您可能会看到这种情况的发生:为了从右边获得第n个元素,您需要在迭代链表时记住最后的n个元素。 This also means that in the worst case, you remember every item when getting the first element from the left—but from the right. 这也意味着在最坏的情况下,当您从左侧(但从右侧)获取第一个元素时,您会记住一项。

Implementing this is not too difficult, you can do this very easily with a list of length numberFromRight but since you are inserting at the end and removing elements from the beginning you would be shifting elements all the time. 实施起来并不是很困难,您可以使用长度为numberFromRight的列表很容易地做到这numberFromRight但是由于您要在末尾插入并从开头删除元素,因此您将一直在移动元素。 So you better use a fixed-length queue. 因此,最好使用固定长度的队列。 Or you use a array with a variable pointer which avoids having to shift anything. 或者,您可以使用带有可变指针的数组,这样可以避免移动任何内容。 A FromRight method could look like this: FromRight方法可能如下所示:

public Element FromRight(int numberFromRight)
{
    // increment the offset by one, so that `0` means the last element,
    // and `1` means the one before last, etc.
    numberFromRight++;

    // create an array with `numberFromRight` slots
    Element[] arr = new Element[numberFromRight];

    // add the current item to the array
    arr[0] = this;

    // `i` is the index where to add the next element
    int i = 1 % numberFromRight;

    // iterate through all elements until the very end
    Element current = this;
    while (current.Next != null)
    {
        current = current.Next;

        // add the current element to the array
        arr[i] = current;

        // increment the index by one, overflowing back to the beginning of the array
        i = (i + 1) % numberFromRight;
    }

    // the element at position `i` is the nth element from the right.
    return arr[i];
}

(I've chosen to implement this without your Books type since you mixed Books and Customers in a confusing way and I didn't get that. This works for arbitrary linked lists.) (由于您以一种令人困惑的方式将“ Books和“ Customers混合使用,而我没有得到,因此我选择了不使用“ Books类型的情况来实现此功能。这适用于任意链接列表。)

If you find yourself accessing elements from the end more regularly, you should consider using a doubly linked list instead which also maintains a pointer to the left element. 如果发现自己从头开始更频繁地访问元素,则应考虑使用双向链接列表 ,该列表还维护着指向左侧元素的指针。 This means that more references need be be maintained (increasing the complexity for list operations a bit) but results in a way better performance (since iterating from the end is the same as iterating from the beginning). 这意味着需要维护更多的引用(稍微增加列表操作的复杂性),但会带来更好的性能(因为从头开始的迭代与从头开始的迭代相同)。

In general, you should always choose a data structure that fits your access pattern. 通常,您应该始终选择适合您的访问模式的数据结构。 No data structure is perfect for everything, so it's important to understand the differences and to know what you really need in your application so you can choose well. 没有任何一种数据结构可以完美地解决所有问题,因此了解差异并了解您的应用程序中真正需要的内容非常重要,这样您才能进行选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM