Как в этом случае проверить, есть ли в связанном списке петли или нет - PullRequest
0 голосов
/ 02 июля 2019

Нужно создать связанный список, в который мы будем добавлять отделы, которые зависят друг от друга или нет.

ввод будет именем отдела, за которым следует название отдела, от которого они зависят, за некоторыми могут следовать пустые строки, что означает, что они не зависят ни от какого отдела.

Так что сначала будет выводится второй отдел и так далее

  1. одно отделение не может зависеть от себя (заботиться)

  2. нужно выяснить, есть ли у отдела циклическая зависимость (проблема)

ввод

продажи

маркетинговые счета

финансы продаж

, поэтому вывод будет

Маркетинговые отчеты по продажам

ввод

маркетинг продаж

маркетинговые счета

счетов продаж

выход

ошибка не может иметь циклическую зависимость

попробовал с приведенными ниже кодами проверить, есть ли в моем связанном списке циклы

private bool DoesItHasLoops()
        {
            var fast = myLinkedList.First;
            var slow = myLinkedList.First;
            while (fast != null && fast.Next != null)
            {
                fast = fast.Next.Next;
                slow = slow.Next;
                if (slow == fast)
                    return true;


            }
            return false;
        }

он не работает и не может найти петлю.

Ниже кода я добавляю строки в свой связанный список.

private LinkedList<strings> myLinkedList;

public void AddDepartments(string[] input)
{
   if (input.Count() > 1)
      {
        if (myLinkedList.Contains(input[0]))  
         {

          myLinkedList.AddBefore(adjList.Find(input[0]), input[1]);

          }
        else
         {
          myLinkedList.AddLast(input[1]);                            
          myLinkedList.AddLast(input[0]);
         }
      }
    else
    {
     myLinkedList.AddLast(input[0]);
     myLinkedList.AddLast(string.empty());

     }
}

это работает для всех случаев, только циклический случай не работает

Ответы [ 2 ]

0 голосов
/ 02 июля 2019

Мне кажется, что самый простой способ идентифицировать эти циклы - это найти те, в которых отдел с зависимостью также в какой-то момент указан в качестве самой зависимости, и пройти через цепочку зависимостей между ними, чтобы увидеть, соединяются ли они.Тем не менее, ваша архитектура выглядит немного не так.Я бы создал объект для хранения отдела и его списка зависимостей, а также флаг того, есть ли у него циклические ссылки, подобные этому:

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IList<int> Dependencies { get; set; }
    public bool HasCircularReferences { get; set; } 
}

Затем вы передаете список отделов и выполняете итерации.над каждым из них и найдите другие отделы, в которых этот отдел указан как зависимый.Затем вы возвращаетесь к ним через цепочку зависимостей, чтобы увидеть, вернетесь ли вы в текущий отдел.Вы можете использовать такой цикл (обратите внимание, что это статический метод, поскольку я тестировал его в консольном приложении):

private static void GetDependents(IList<int> dependencies, int seed)
{
    foreach (var dependent in dependencies)
    {
        var dependentDept = departments.Where(d => d.Id == dependent).FirstOrDefault();
        if (dependentDept.Dependencies.Count > 0)
        {
            if (dependencies.Contains(seed) || dependentDept.HasCircularReferences)
            {
                SetCircularReferenceFlag(seed, true);
                break;
            }
            GetDependents(dependentDept.Dependencies, seed);
        }
        else
        {
            SetCircularReferenceFlag(seed, false);
            break;
        }
    }
}

Это устанавливает флаг HasCircularReferences в отделе с использованием метода SetCircularReferenceFlag, который:

private static void SetCircularReferenceFlag(int departmentId, bool hasCircularReferences)
{
    var seedDept = departments.Where(d => d.Id == departmentId).FirstOrDefault();
    seedDept.HasCircularReferences = hasCircularReferences;
}

Затем метод GetDependents можно вызывать для каждого отдела в вашем списке следующим образом:

foreach (var department in departments)
{
    GetDependents(department.Dependencies, department.Id);
}

Отделы были настроены так, чтобы использовать объект Department, определенный выше:

private static List<Department> departments = new List<Department>
{
    new Department
    {
        Id = 1,
        Name = "sales",
        Dependencies = new List<int>{ 2 }
    },
    new Department
    {
        Id = 2,
        Name = "accounts",
        Dependencies = new List<int>{ 3 }
    },
    new Department
    {
        Id = 3,
        Name = "marketing",
        Dependencies = new List<int>{ 1 }
     },
     new Department
     {
        Id = 4,
        Name = "finance",
        Dependencies = new List<int>{ 1 }
     }
 };

При запуске через консольное приложение вы получаете следующие результаты:

enter image description here

0 голосов
/ 02 июля 2019

Я бы порекомендовал вам немного изменить архитектуру. Вместо использования этой конструкции, где все хранится в LinkedList, я бы пошел с Dictionary. Таким образом, вы можете легко связать отдел с отделом в зависимости от.

Таким образом, у вас будет Dictionary<string, List<string>>, где ключом является отдел и значение отделов, от которых он зависит:

private bool DoesItHasLoops(Dictionary<string, List<string>> departmentWithDepending)
{
    HashSet<string> checkedDepartments = new HashSet<string>();

    foreach (var department in departmentWithDepending)
    {
        checkedDepartments.Add(department.Key);
        if (HasConflict(department.Value, departmentWithDepending, checkedDepartments))
        {
            return true;
        }
        checkedDepartments.Clear();
    }
    return false;
}

private bool HasConflict(List<string> dependentDepartments,
    Dictionary<string, List<string>> departmentWithDepending,
    HashSet<string> checkedDepartments)
{
    foreach (var currentDepartment in dependentDepartments)
    {
        //Checks if this department was already checked, if yes you got a loop
        if (checkedDepartments.Contains(currentDepartment))
        {
            return true;
        }
        else
        {
            //Checks if the current department has a dependency
            if (departmentWithDepending.ContainsKey(currentDepartment))
            {
                //marks the current department as checked and and checks if the department the current department depends on has any conflicts(loops)
                checkedDepartments.Add(currentDepartment);
                if (HasConflict(departmentWithDepending[currentDepartment], departmentWithDepending, checkedDepartments))
                {
                    return true;
                }
                else
                {
                    checkedDepartments.Remove(currentDepartment);
                }
            }
        }
    }
    return false;
}

Поиск таких циклов не так прост, надеюсь, что описанный выше подход работает, я еще не тестировал его. Если у вас есть какие-либо вопросы, которые вы можете задать в разделе комментариев, я смог понять, почему этот код может быть трудно понять.

Кстати, я вызываю HasConflict (...) изнутри себя, чтобы найти любые последующие конфликты. Обычно я бы посоветовал не делать этого, поскольку вы можете получить бесконечный цикл. Вы должны делать это только в том случае, если вы на 100% уверены, что это никак не может застрять.

Edit:

departments: a, b, c, d

dependencies:
a (depends on) b, c
b (depends on) (nothing)
c (depends on) d
d (depends on) a, b

dependencies that are fine:
a -> b 
a -> c -> d -> b
c -> d -> b
d -> b

dependencies that would cause a conflict:
a -> c -> d -> a
c -> d -> a -> c
d -> a -> c -> d
...