删除重复项并对列表进行排序

时间:2013-11-19 23:57:07

标签: list scheme

我正在尝试编写一个过程,该过程采用可能包含或不包含重复项的列表,然后按顺序返回该列表而不重复。到目前为止我想出的是:

(define (remove-duplicated list)
    (if (null? list)
       '()
        (if (= (car list) (cadr list))
            (cdr list)
            (cons (car list) (remove-duplicates (cdr list))))))  

除了对列表进行排序之外,我不太确定问题是什么。例如,如果我输入

(remove-duplicates '(3 3 4 5 6 6 7))

返回

(3 4 5 6 6 7)

2 个答案:

答案 0 :(得分:0)

输入列表可能排序这一事实让我不知所措。我要描述的内容将用于从任何列表中删除重复元素,是否已排序。对于删除列表中重复项的一般情况,您必须使用member搜索列表其余部分中的当前元素。

此外,你必须在两种情况下推进递归,并注意在最后一行你正在调用remove-duplicates(这是一些解释器中的内置程序,所以也许你不必须从头开始实现它!),但是你将程序命名为remove-duplicated。作为旁注,命名参数list是一个坏主意,它会与内置函数发生冲突 - 我冒昧地重命名它。这将解决问题,这是一个更通用的解决方案:

; if the input list is not sorted, use this
(define (remove-duplicated lst)
  (if (null? lst)
      '()
      (if (member (car lst) (cdr lst))  ; changes here
          (remove-duplicated (cdr lst)) ; and here
          (cons (car lst)
                (remove-duplicated (cdr lst)))))) 

现在,如果输入列表排序开头,这就是修复代码的方法。我的大多数评论都适用,除了您不必使用member且基本情况略有不同:

; if the input list is sorted, use this
(define (remove-duplicated lst)
  (if (or (null? lst) (null? (cdr lst))) ; changes here
      lst
      (if (= (car lst) (cadr lst))
          (remove-duplicated (cdr lst))  ; and here
          (cons (car lst)
                (remove-duplicated (cdr lst))))))

无论哪种方式,只要您使用正确的输入(第一个实现是针对排序的未排序的输入列表,第二个实现仅适用于排序列表),该过程将按预期工作):

(remove-duplicated '(3 3 4 5 6 6 7)) ; sorted input, both implementations work
=> '(3 4 5 6 7)

最后,如果您需要确保输出列表始终排序,但不保证输入列表已排序,那么请使用我的第一个remove-duplicated实现并在之后对其进行排序,检查您的解释器找出哪些分类程序可用 - 以下内容适用于Racket:

(sort (remove-duplicated '(3 6 3 7 4 5 6)) <) ; using my first remove-duplicated
=> '(3 4 5 6 7)

...或者首先对列表进行排序,然后使用我的第二个remove-duplicated实现。你有很多选择来解决这个问题!

(remove-duplicated (sort '(3 6 3 7 4 5 6) <)) ; using my second remove-duplicated
=> '(3 4 5 6 7)

答案 1 :(得分:0)

  

一个相当简单的程序,它将列入可能会或可能不会的列表   包括duplicats,然后返回该列表,没有任何重复   包含,并按排序顺序。

至少有两种方法可以做到这一点:

  • 删除重复项后对列表进行排序;或
  • 在列表排序后删除重复项。

Óscar López pointed out

  

[您的]实施失败,因为您只测试了两个   连续值,你必须搜索其余的当前元素   列表,使用成员。

如果在排序之前删除重复项,这将是一个问题,因为列表中的给定元素可能在列表中的任何其他位置具有重复项。但是,如果您首先对列表进行排序,那么可以保证任何重复的元素执行会立即跟随原始列表,因此您无需检查整个列表。如果对列表进行排序,则删除重复项会更容易,但删除重复元素后排序列表实际上并不容易,因此首先对列表进行排序并然后删除重复项确实有意义。 (我想你可能会更有效率,并编写自己的sort-and-remove-duplicates程序,但几乎肯定不是必需的。)

如果您假设list已经排序,则您的代码几乎正确。需要进行两项调整:

  1. 在基本情况下,您只是检查是否(null? list)。但是,对于非空列表,您可以比较(car list)(cadr list),但如果list只有一个元素,那么(cadr list)就是错误。幸运的是,只有一个元素的列表没有重复项,因此您的基本案例可以是(or (null? list) (null? (cdr list)))
  2. 第二个if然后部分需要(remove-duplicated (cdr list)),而不是(cdr list),因为list仍可能有更多重复项(例如,(x x x ...)(x x y y ...))。
  3. 这是您的代码,包含这些修改和一些注释:

    (define (remove-duplicated list)
      ;; remove duplicates from a *sorted* list.  Because the 
      ;; list is sorted, any duplicates of an element will
      ;; immediately follow the first occurrence of the element.
      ;;---------------------------------------------------------
      ;; If the list has the form () or (x)
      (if (or (null? list)
              (null? (cdr list)))
          ;; then it has no duplicates, so return it
          list
          ;; otherwise, if the list looks like (x x ...)
          (if (= (car list) (cadr list))
              ;; then you can discard the first element, but you
              ;; still need to remove duplicates from the rest of
              ;; the list, since there can be more duplicates later
              (remove-duplicated (cdr list))
              ;; otherwise, you need the first element of the list
              ;; and can simply remove-duplicated from the rest.
              (cons (car list) (remove-duplicated (cdr list))))))  
    

    这可以按预期工作:

    (remove-duplicated '(1 1 2 3 3 4 5 6))
    ;=> '(1 2 3 4 5 6)