假设我有一个整数数组,我需要返回一个数组,其中包含可按3整除的所有数字。
我给出了界面:
List<int> ListElementsDivisibleBy3Recursive(List<int> input)
所以它必须采用一个列表并返回一个列表,并递归过滤它。我尝试从列表的开头弹出元素,直到我找到符合所需条件的数字,以及我被卡住的地方。我不知道如何步入下一个元素并保持与条件匹配的元素。这就是我到目前为止所做的:
static List<int> ListElementsDivisibleBy3Recursive(List<int> input)
{
if (input.Count == 0)
return input;
else if (input.ElementAt(0) % 3 == 0)
{
return ListElementsDivisibleBy3Recursive(input); <--I have no idea what to do here
}
else
{
input.RemoveAt(0);
return ListElementsDivisibleBy3Recursive(input);
}
}
如果我被允许将指针传递到我所在列表中的位置会更容易,但我所拥有的只是输入列表和要使用的返回列表。如何删除列表中的元素,同时保留与条件匹配的元素?或者我是否会以错误的方式解决这个问题?我不确定我应该如何理解这一点。我也意识到我的基本情况也是错误的。
答案 0 :(得分:1)
这是我能看到的唯一方法。每个递归都会删除最后一个元素,直到列表为空,然后当递归展开时,满足条件的元素将被添加回列表的末尾。
List<int> ListElementsDivisibleBy3Recursive(List<int> input) {
if (input.Count > 0) {
int last = input.Last();
input.RemoveAt(input.Count - 1);
input = ListElementsDivisibleBy3Recursive(input);
if (last % 3 == 0)
input.Add(last);
}
return input;
}
虽然从方法中返回任何内容都是没有意义的,因为input
会根据需要进行修改,而不管返回值如何。所以这实现了相同的结果:
void ListElementsDivisibleBy3Recursive(List<int> input) {
if (input.Count > 0) {
int last = input.Last();
input.RemoveAt(input.Count - 1);
ListElementsDivisibleBy3Recursive(input);
if (last % 3 == 0)
input.Add(last);
}
}
答案 1 :(得分:0)
整个问题很奇怪。
要解决处理当前位置的初始问题,您可以跟踪当前索引(在我的代码中名为count
的变量中),然后执行:
return currentList.AddRange(ListElementsDivisibleBy3Recursive(input.Skip(count).ToList()));
使用Skip
删除已处理的元素。当然,在那时,你有LINQ,所以你不妨这样做:
return currentList.AddRange(ListElementsDivisibleBy3Recursive(input.SkipWhile(i => i % 3 != 0).ToList()));
当然,那个需要枚举两次,因为你没有从SkipWhile
中获取值。但你根本不需要递归,你可以写:
return currentList.Where( i => (i % 3) == 0).ToList();
答案 2 :(得分:0)
如果你真的想通过递归解决这个问题,你可以这样做
static List<int> FilterMod3Internal(List<int> state, List<int> initialList, int index)
{
if (index >= initialList.Count())
{
return state;
}
else
{
var number = initialList[index];
if (number%3 == 0)
{
state.Add(number);
}
return FilterMod3Internal(state, initialList, index + 1);
}
}
static List<int> FilterMod3(List<int> list)
{
return FilterMod3Internal(new List<int>(), list, 0);
}
但正如其他答案所注意到的那样,在迭代(或至少是尾递归)的情况下,以递归方式执行几乎所有操作都是一个坏主意。
如果要在不使用更多内存的情况下过滤现有集合,可以这样做
static void FilterListMod3Internal(List<int> list, int index)
{
if (index >= list.Count())
{
return;
}
var number = list[index];
if (number%3 != 0)
{
list.RemoveAt(index);
FilterListMod3Internal(list, index);
}
else
{
FilterListMod3Internal(list, index + 1);
}
}
这两个版本都是递归的,但是它们需要有某种内部函数,它有额外的参数来维持函数调用之间的状态。
你也可以概括这种方法
static List<T> FilterInternal<T>(List<T> state, List<T> initialList, int index, Func<T, bool> filterFunction)
{
if (index >= initialList.Count())
{
return state;
}
else
{
var item = initialList[index];
if (filterFunction(item))
{
state.Add(item);
}
return FilterInternal(state, initialList, index + 1, filterFunction);
}
}
static List<T> Filter<T>(List<T> list, Func<T, bool> filterFunction)
{
return FilterInternal<T>(new List<T>(), list, 0, filterFunction);
}
而且不会消耗额外内存的那个
static void FilterListInternal<T>(List<T> list, int index, Func<T, bool> filterFunction)
{
if (index >= list.Count())
{
return;
}
var item = list[index];
if (!filterFunction(item))
{
list.RemoveAt(index);
FilterListInternal(list, index, filterFunction);
}
else
{
FilterListInternal(list, index + 1, filterFunction);
}
}
static void FilterList<T>(List<T> list, Func<T, bool> filterFunction)
{
FilterListInternal<T>(list, 0, filterFunction);
}
当您希望项目位于结果集合中时,filterFunction
应返回true,否则返回true。这很容易改变,所以,试试吧,你想要的。
答案 3 :(得分:0)
奇怪的问题: - )
这是一个更灵活的解决方案:IList的扩展方法 RecursiveFilter 接受谓词并返回IEnumerable(可用于构造List,Array或其他东西)。
using System;
using System.Collections.Generic;
namespace Sandbox
{
class Program
{
static void Main()
{
var array = new[] { 1, 6, 8, 3, 6, 2, 9, 0, 7, 3, 5 };
var filtred = array.RecursiveFilter(i => i % 3 == 0);
foreach (var item in filtred)
Console.WriteLine(item);
}
}
public static class MyCollectionExtensions
{
public static IEnumerable<T> RecursiveFilter<T>(this IList<T> collection, Func<T, bool> predicate, int index = 0)
{
if (index < 0 || index >= collection.Count)
yield break;
if (predicate(collection[index]))
yield return collection[index];
foreach (var item in RecursiveFilter(collection, predicate, index + 1))
yield return item;
}
}
}
答案 4 :(得分:0)
递归遍历列表以过滤它对于可变数组支持列表不是特别合适的算法。这种方法更适合处理不可变结构。处理不可变列表(通常建模为具有当前项和指向另一个节点的链接的节点)时,递归解决方案非常适合此问题:
public class Node<T>
{
public Node(T value, Node<T> tail)
{
Value = value;
Tail = tail;
}
public T Value { get; private set; }
public Node<T> Tail { get; private set; }
}
public static Node<int> ListElementsDivisibleBy3Recursive(Node<int> node)
{
if (node == null)
return node;
else if (node.Value % 3 == 0)
return new Node<int>(node.Value, ListElementsDivisibleBy3Recursive(node.Tail));
else
return ListElementsDivisibleBy3Recursive(node.Tail);
}
我们有一个简单的递归情况,其中节点为空(每个列表的末尾都有一个Tail
null
)。然后我们有两个递归的情况,要么当前节点的值与过滤器匹配,我们创建一个新的列表,该节点在头部,尾部是列表其余部分的递归解决方案,或者当前节点的值不应该包括在内,解决方案只是列表尾部的递归。
List<T>
根本不适合这种类型的算法,因为它依赖于能够计算“包含第一项之后的所有内容的列表”以及还能够轻松计算“相同的新列表”到旧列表,但添加了一个项目“,这两个都不是List<T>
可以轻松或有效执行的操作,但不可变列表通常将作为主要操作。
答案 5 :(得分:-2)
return input.Where( i => (i % 3) == 0).ToList();