【问题标题】:Custom LinkedList: Picking alternate integer elements自定义 LinkedList:选择替代整数元素
【发布时间】:2016-04-23 16:01:24
【问题描述】:

我有一个Integer[21, 9, 13, 47, 5, 10, 19, 36, 20, 11, 13] 的列表,我正在尝试编写一个递归函数,它将原始列表中的所有其他整数作为列表返回
(即[21, 13, 5, 19, 20, 13])。

我正在使用 LispList 的自定义实现。该对象称为LispList,它是不可变的 并且(显然)没有索引。它提供了以下方法:

E head() - 返回调用它的列表的第一项。
LispList tail() - 返回一个新的列表,包括
LispList cons(E item) - 接受一个参数并返回一个新列表,其头部为参数,尾部为参数是调用它的列表。
boolean isEmpty() - 如果调用它的列表是空列表,则返回 true,否则返回 false。
静态 LispList empty() - 返回一个空列表。

经过几个小时的尝试,我想出了一种方法,我使用初始化为 2 的计数器并使用它来跟踪偶数编号的元素,应将其取出并放入新的 LispList 并返回。

但是,当我运行代码时,它会抛出 NullPointerException,我不明白为什么。也许我只是盯着屏幕太久了!

这是从main调用的方法:

public static <T> LispList<T> pickEveryOther(LispList<T> ls) {
        int counter = 2; 
        LispList<T> ls1 = pickEveryOtherHelper(ls.tail(), counter);
        return ls1;
}


和辅助方法:

public static <T> LispList<T> pickEveryOtherHelper(LispList<T> ls, int counter) {
        LispList<T> wantLs = LispList.empty();
        if(ls.isEmpty()) {
            return LispList.empty();
        }
        else {
            if(counter % DIVIDER == 0) {
                // item we want
                wantLs = wantLs.cons(ls.head());
                counter++;
            }
            else {
                // don't want item, look in tail
                pickEveryOtherHelper(ls.tail(), counter);
            }
            return wantLs;
        }
    }

非常感谢您的努力。

【问题讨论】:

  • DIVIDER=2 顺便说一下(在方法pickEveryOtherHelper中)。这是一个全球性的,抱歉我忘了提。

标签: java list recursion


【解决方案1】:

这应该可以解决问题,只取头部,然后尝试取尾部两次并递归。只需考虑这样一个事实,即它可以在每个尾部之后变成一个空列表。然后将头部添加到递归的结果中。

public static <T> LispList<T> everyOther(LispList<T> lispList) {
    if (lispList.isEmpty()) {
        return lispList.empty();
    }
    T head = lispList.head();
    LispList<T> tail = lispList.tail();
    if (!tail.isEmpty()) {
        tail = tail.tail();
    }
    tail = everyOther(tail);
    return tail.cons(head);
}

【讨论】:

  • 正要说它会在空列表上崩溃,但你修复了它。 :-) 应该只做return lispList,而不需要else 跟随return。保持不缩进。
  • 我包含了空白,以便它与其余代码更具风格,因为它返回列表的编辑副本,而不是列表本身。这样您就可以获得结果并在不更改原始文件的情况下进行处理。不过,你是对的。我把它编辑了。
  • 鉴于“LispList [...] 是不可变的”,“'不改变原件”是不必要的考虑。因此,对于一个空列表或只有一个元素的列表,您可以返回列表本身。 My code 稍长一些,以实现该优化。无论哪种方式都很好,并且可以提供所需的结果。
  • 当我用我的客户端运行它时,它仍然会在你调用 head() 的行上抛出一个 NullPointer lisplist
  • @orion_IX 如果head()isEmpty() 返回false 后抛出NPE,那么您的LispList 有缺陷。请参阅my answer 中的工作实现,它与此答案中的解决方案一起使用。
【解决方案2】:

解决方案是在每次递归时从列表中弹出 2 个值,并使用这 2 个值之一构建一个新列表。

您并没有真正指定“每个其他”是指第 1、第 3、第 5、... 还是第 2、第 4、第 6、...,所以这里有两种解决方案。

public static <T> LispList<T> pickOdd(LispList<T> ls) {
    if (ls.isEmpty())
        return ls; // return empty
    LispList<T> tail1 = ls.tail();
    if (tail1.isEmpty())
        return ls; // return of(ls.head())
    LispList<T> tail2 = tail1.tail();
    if (tail2.isEmpty())
        return tail2.cons(ls.head()); // return of(ls.head())
    return pickOdd(tail2).cons(ls.head());
}
public static <T> LispList<T> pickEven(LispList<T> ls) {
    if (ls.isEmpty())
        return ls; // return empty
    LispList<T> tail1 = ls.tail();
    if (tail1.isEmpty())
        return tail1; // return empty
    LispList<T> tail2 = tail1.tail();
    if (tail2.isEmpty())
        return tail1; // return of(ls.tail().head())
    return pickEven(tail2).cons(tail1.head());
}

为了测试它,我确实实现了LispList,带有两个辅助方法(of(...)toString()):

final class LispList<E> {
    private final E           head;
    private final LispList<E> tail;
    private LispList(E head, LispList<E> tail) {
        this.head = head;
        this.tail = tail;
    }

    /** returns the first item of the list it is called on */
    public E head() {
        if (isEmpty()) throw new IllegalStateException();
        return this.head;
    }
    /** returns a new list consisting of all but the first item of the list it is called on */
    public LispList<E> tail() {
        if (isEmpty()) throw new IllegalStateException();
        return this.tail;
    }
    /** takes an argument and returns a new list whose head is the argument and whose tail is the list it is called on */
    public LispList<E> cons(E item) {
        return new LispList<>(item, this);
    }
    /** returns true if the list it is called on is the empty list, returns false otherwise */
    public boolean isEmpty() {
        return this.tail == null;
    }
    /** returns an empty list */
    public static <T> LispList<T> empty() {
        return new LispList<>(null, null);
    }

    @SafeVarargs
    public static <T> LispList<T> of(T ... values) {
        LispList<T> ls = empty();
        for (int i = values.length - 1; i >= 0; i--)
            ls = ls.cons(values[i]);
        return ls;
    }
    @Override
    public String toString() {
        StringJoiner joiner = new StringJoiner(", ", "[", "]");
        for (LispList<E> ls = this; ! ls.isEmpty(); ls = ls.tail())
            joiner.add(String.valueOf(ls.head()));
        return joiner.toString();
    }
}

测试

LispList<Integer> ls = LispList.of(21, 9, 13, 47, 5, 10, 19, 36, 20, 11, 13);
System.out.println("List: " + ls);
System.out.println("Odd : " + pickOdd(ls));
System.out.println("Even: " + pickEven(ls));

输出

List: [21, 9, 13, 47, 5, 10, 19, 36, 20, 11, 13]
Odd : [21, 13, 5, 19, 20, 13]
Even: [9, 47, 10, 36, 11]

【讨论】:

  • 我想要奇数值,因此是“(即 [21, 13, 5, 19, 20, 13])”。 +1 非常简洁的解决方案。
猜你喜欢
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
相关资源
最近更新 更多