算法:课程顺序

时间:2018-05-19 03:24:28

标签: python python-3.x algorithm

鉴于课程数量及其先决条件,我必须返回学生可以参加课程的正确顺序,即学生如果没有完成课程就不能参加课程。 -requisite。请参阅以下样本输入和输出以进行说明

课程顺序

示例输入:
  - 课程数量:4
  - 先决条件清单:[[1,2],[3,2],[2,4]] // [i,j]表示我是先决条件 输出:[1,3,2,4][3,1,2,4]

示例输入:
     - 课程数量:2
     - 先决条件清单:[[1,2],[2,1]]
输出:[] //由于循环依赖性而没有可能的解决方案

这是我天真的解决方案:

class Solution:
    def __init__(self):
        self.order = []
        self.mapping = {}

    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """

        for pre_req in prerequisites:
            if pre_req[1] not in self.mapping:
                self.mapping[pre_req[1]] = [pre_req[0]]
            else:
                self.mapping[pre_req[1]].append(pre_req[0])

        # for i in range(1, numCourses+1):
        #     if i not in self.mapping:
        #         self.order.append(i)

        j = 0
        # for key in self.mapping:
        #     i += 1
        #     if key not in self.order:
        #         self.helper(key, i)
        #     break
        for i in range(1, numCourses+1):
            if i in self.mapping:
                self.helper(i, j)



        return self.order

    def helper(self, key, i):
        if len(self.mapping[key]) == 0:
            if key not in self.order:
                self.order.append(key)

                for ki in self.mapping:
                    if key in self.mapping[ki]:
                        self.mapping[k].remove(value)
                self.mapping.pop(key, None)   
            return


        for k in self.mapping:
            for value in self.mapping[k]:    
                if value not in self.mapping:
                    if value not in self.order:
                        self.order.append(value)
                        for ki in self.mapping:
                            if value in self.mapping[ki]:
                                self.mapping[k].remove(value)
                    else:
                        self.mapping[k].remove(value)
                else:
                    self.helper(k, i+1)
                if len(self.mapping[key]) == 0:
                    if key not in self.order:
                        self.order.append(key)

                        for ki in self.mapping:
                            if key in self.mapping[ki]:
                                self.mapping[k].remove(value)



s = Solution()
prereq = [[1,2],[3,2],[2,4]]
courses = 4
res = s.canFinish(courses, prereq)
print(res)
  • 我的代码适用于以下输入:
  • 课程数量:4
  • 先决条件清单:[[1,2],[3,2],[2,4]]
  • 我的代码不处理循环依赖
  • 我的解决方案不是最佳的
  • 我想知道这个问题的正确和最佳解决方案
  • 在投票前,请评论您需要添加的额外信息

=============================更新============== ==================

  • 它没有处理循环依赖,因为在辅助函数中我只检查前提条件,它是key字典中mapping的值,并且是先决条件,即值作为不同的在mapping字典中键入然后我在self.order中添加,我正在递归地执行此操作
  • 因此,我不会检查密钥和值以及相同的值是否是其自身密钥的关键。
    • 显然它不是最佳的,因为我没有检查循环依赖
    • 我认为可以有更好的数据结构来解决这个问题
    • 字典的想法:mapping = {course1 : [prereq1, prereq2], course2: [prereq1, prereq3]}
    • 帮助函数检查mapping是否有prereq1作为键,然后将prereq1的值添加到self.order

3 个答案:

答案 0 :(得分:2)

这是topological sorting的经典用例。如果您将先决条件视为有向图,其中存在从先决条件到另一个课程的边缘,您可以简单地找到图形的拓扑类型,您将获得答案。当图形具有循环时,未定义拓扑排序,类似地,您的课程不应具有循环,因为您永远无法参加这些课程。在python中,您可以使用简单的深度优先搜索进行拓扑排序,跟踪进入和退出节点的时间。退出节点时(在查看所有子节点之后),将该节点添加到列表中。例如:

from collections import defaultdict
graph = defaultdict(set)

def getCourses(prereq):
    for p in prereq:
        # build a simple graph structure: keys are node value is a set of children
        graph[p[1]]
        graph[p[0]].add(p[1])

    visited = set()  
    seen = set()
    courses = []

    def dfs(node): # depth-first search
        if node in visited: return
        if node in seen:
            raise ValueError("Error cycle in prerequisites")
        seen.add(node)
        for e in graph[node]:
            dfs(e) #recurse on children
        seen.remove(node)
        visited.add(node)
        courses.insert(0, node)


    for k in graph.keys():
        if k in visited:
            continue        
        try: dfs(k)
        except ValueError: # in case of cycle
            return []

    return courses

print(getCourses([[1,2],[3,2],[2,4]]))

答案 1 :(得分:1)

这是我的解决方案。我跟踪这个对象的课程:

class Course():
  '''A course object. Holds a pre-req, maybe'''
  def __init__(self, id):
    self.id = id
    self.reqs = []

  def add_req(self, course):
    self.reqs.append(course)

  def has_req(self):
    return len(self.reqs) > 0

  def reqs_satisfied(self, path):
    return set(self.reqs).issubset(set(path))

因此,如果有任何课程,我可以查看其要求。

我只对将所有课程添加到列表中感兴趣,遵循以下规则:

  1. 只有在尚未添加课程时才会添加课程。
  2. 如果可以添加课程,则意味着添加了所有先决条件。
  3. 如果无法添加课程,那是因为我们找不到满足先决条件的方法(在这种情况下,当存在循环先决条件时,因为我们不会让自己进入循环)

    这意味着

    class Solution():  
    
      def __init__(self, course_data, prereq_data):
        '''Create courses, and fill in pre-reqs'''
        self.courses = {}
        for id in course_data:
          self.courses[id] = Course(id)
    
        # [i, j], assuming course i is the pre-req for course j
        for data in prereq_data:
          pre_req = self.courses[data[0]]
          course = self.courses[data[1]]
          course.add_req(pre_req)     
    
      def build_path(self):
    
        all_courses = list(self.courses.values())
        num_courses = len(all_courses)
        path = []
    
        # Add each course recursively
        while len(all_courses) > 0:
          course = all_courses.pop(0)      
          self.add_course(course, path, [])
    
        # Check if the path is valid (ie: all courses are taken)
        if len(path) == num_courses:
          return path
        else:
          return []
    
      def add_course(self, course, path, visited):
        # ignore courses that have already been added.
        # also, ignore courses that have been visited already (avoid cycle)
        if course in path or course in visited:
          return
        visited.append(course)
    
        # if this course has requirement, add it. Possible that we don't add it
        for req in course.reqs:
          self.add_course(req, path, visited)
    
        # try adding this course, after pre-reqs have been checked. 
        if (course.reqs_satisfied(path)):
          path.append(course)
    
    path = Solution([1,2,3,4], [[1,2],[3,2],[2,4]]).build_path()
    print("Solution: ", [course.id for course in path])
    
    path = Solution([2,1,7,4], []).build_path()
    print("Solution: ", [course.id for course in path])
    

答案 2 :(得分:0)

from collections import defaultdict

class CourseSelection(object):
  def __init__(self):
    self.num_of_courses = 0
    self.prereq_map = defaultdict(list)
    self.seen = set()
    self.is_cycle = False

  def add_prereq(self, parent, prereq):
    self.num_of_courses += 1
    self.prereq_map[parent].append(prereq)    

  def topological_util(self, courses_order, visited, node):

    if self.is_cycle:
      return

    self.seen.add(node)

    for prereq in self.prereq_map[node]:
      if prereq in self.seen:
        print("Cycle Found")
        self.is_cycle = True
        return True
      if prereq not in visited:  
        visited.add(prereq)  
        self.topological_util(courses_order, visited, prereq)

    self.seen.remove(node)
    courses_order.append(node)


  def topological_sort(self, start_node):
    visited = set([])
    courses_order = []
    for i in range(self.num_of_courses):
      # print(i, visited)
      if i not in visited:
        visited.add(i)
        self.topological_util(courses_order, visited, i)
    print(courses_order)

cs = CourseSelection()
cs.add_prereq(5, 0)
cs.add_prereq(5, 2)
# g.add_prereq(2, 5)
cs.add_prereq(2, 3)
cs.add_prereq(3, 1)
cs.add_prereq(4, 0)
cs.add_prereq(4, 1)
cs.topological_sort(0)