您的位置:首页 > 其它

递归 尾递归_当递归开始救援

2020-08-17 22:57 477 查看

递归 尾递归

When practicing solving algorithm problems, we often see questions that make us wonder if we would ever encounter similar situations in the real world (e.g. spiral traversal of a matrix).

在练习解决算法问题时,我们经常会看到一些问题,使我们想知道我们是否会在现实世界中遇到类似情况(例如矩阵的螺旋遍历)。

This time, however, I came across an interesting algorithm challenge that makes practical sense to me.

但是,这次,我遇到了一个有趣的算法挑战,这对我来说很有意义。

Here’s the task:

这是任务:

You are given a list of tasks, some of which depend on others.Write a function

tasksInOrder
that takes a subset of those tasks and returns an ordered list of all the tasks to run.

您将获得任务列表,其中一些任务取决于其他任务。编写一个功能

tasksInOrder
,该函数接受这些任务的子集并返回要运行的所有任务的有序列表。

To illustrate:

为了显示:

const tasks = [
{
task: "make a sandwich",
depends: [ "buy groceries" ]
},
{
task: "buy groceries",
depends: [ "go to the store" ]
},
{
task: "go to the store",
depends: []
}
]// tasksInOrder(tasks, ["make a sandwich"])
// -> [ 'go to the store', 'buy groceries', 'make a sandwich' ]// tasksInOrder(tasks, ["buy groceries", "make a sandwich"])
// -> [ 'go to the store', 'buy groceries', 'make a sandwich' ]

We all make to-do lists in our daily lives, so I was glad to finally see a function that we can actually put to good, practical use.

我们都会在日常生活中列出待办事项清单,因此我很高兴终于看到一个可以实际使用的实用功能。

蛮力法 (Brute Force Approach)

As I read the challenge, the first thing that came to mind was a linked-list data structure, as each task has a dependency, or

node
, which points to another task.

当我阅读挑战时,首先想到的是链接列表数据结构,因为每个任务都有一个依赖项或

node
,它指向另一个任务。

With that, I was able to quickly write out a straightforward (but flawed) solution that traverses both the task list and the given subset.

这样,我便能够快速写出一个遍历任务列表和给定子集的简单(但有缺陷)的解决方案。

function tasksInOrder(tasks, subset) {
let result = []
for (let task of tasks) {
if (task.depends.length !== 0) {
result.unshift(task.depends[0])
}
}
for (let sub of subset) {
result.push(sub)
} return [...new Set(result)]
}

The solution above does output the desired results in the two sample cases:

上面的解决方案确实在两种示例情况下输出了预期的结果:

// tasksInOrder(tasks, ["make a sandwich"])
// -> [ 'go to the store', 'buy groceries', 'make a sandwich' ]// tasksInOrder(tasks, ["buy groceries", "make a sandwich"])
// -> [ 'go to the store', 'buy groceries', 'make a sandwich' ]

However, this solution would fail if out task list is not in order:

但是,如果不在任务列表中,则此解决方案将失败:

const tasksNotInOrder = [ 
{
task: "buy groceries",
depends: [ "go to the store" ]
},
{
task: "make a sandwich",
depends: [ "buy groceries" ]
},
{
task: "go to the store",
depends: []
}
]// tasksInOrder(tasksNotInOrder, ["buy groceries"])
// expected -> [ 'go to the store', 'buy groceries' ]
// got -> [ 'buy groceries', 'go to the store' ]

So, how might we follow the dependencies of the given subset that keep recurring in the tasks list in the right order?

那么,我们如何遵循给定子集的依赖关系,这些依赖关系会以正确的顺序在任务列表中不断重复出现

递归方法 (Recursive Approach)

In order to grab all the dependencies of all the subtasks in the subset, we can:

为了获取子集中所有子任务的所有依赖关系,我们可以:

  1. Grab all the dependencies of one subtask

    抓住一个子任务的所有依赖关系

  2. Add the dependencies to an array by prepending them, so we can put them in order

    通过在它们前面添加依赖项来将其添加到数组中,以便我们可以按顺序排列它们

  3. Repeat step#2 until the subtask has no dependency

    重复步骤2,直到子任务没有依赖性

Since the recursive solution occurs in the subtasks, we can separate concerns by creating a helper function that focuses on recursion:

由于递归解决方案出现在子任务中,因此我们可以通过创建一个专注于递归的辅助函数来分离关注点:

function tasksInOrder(tasks, subset) {
let tasksList = []
for (let subTask of subset) {
let foundTask = tasks.find(taskObj => taskObj.task === subTask)
// invoke helper function
getDependedTasks(foundTask, tasksList, tasks)
}
}// helper function
function getDependedTasks(currentTask, tasksList, tasks) {
// prepend the current task
tasksList.unshift(currentTask)
// base case: when we hit the task with no dependency
if (currentTask.depends.lenth === 0) {
return
}
// recursive case:
// (1) find the task which the current task depends on
// (2) run the function recursively with the found task
let nextTask = tasks.find(taskObj => taskObj.task === currentTask.depends[0])
return getDependedTasks(nextTask, tasksList, tasks)
}

And voilà! With this approach, we see an output of an orderly tasks list, no matter how disorganized the original list is.

和瞧! 通过这种方法,无论原始列表有多混乱,我们都会看到有序任务列表的输出。

Do you see any potential flaws in the recursive approach? Can you think of any other way to tackle this challenge? As always, please let me know in the comments.

您是否发现递归方法有任何潜在的缺陷? 您能想到其他方法来应对这一挑战吗? 与往常一样,请在评论中让我知道。

翻译自: https://medium.com/swlh/when-recursion-comes-to-rescue-98d726fe1a7b

递归 尾递归

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: