【问题标题】:Remove duplicates and sort a list删除重复项并对列表进行排序
【发布时间】:2013-12-03 18:50:43
【问题描述】:

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

(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)

【问题讨论】:

    标签: list scheme


    【解决方案1】:

    输入列表可能已排序这一事实让我不以为然。我将要描述的内容将适用于从任何列表中删除重复元素,无论是否排序。对于删除列表中重复项的一般情况,您必须使用 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)))))) 
    

    现在,如果输入列表开始时排序,这就是修复代码的方法。我的大多数 cmets 都适用,除了您不必使用 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)
    

    【讨论】:

    • OP 想要一个没有重复的排序列表。如果list已经排好序了,那么在下一个元素相同的时候直接丢弃一个元素就可以了;这些是唯一可以在 sorted 列表中重复的地方。不过,正如我在 my answer 中解释的那样,OP 仍然需要从 (cdr list)remove-duplicated
    • @JoshuaTaylor 列表已排序的事实让我忘记了。我修正了我的答案,感谢您指出这一点
    • 好吧,我不确定这个问题是否清楚地表明列表 排序的,但是如果 OP,第一个和第二个元素的比较更有意义假设它已排序。
    【解决方案2】:

    一个相当简单的过程,它会接受一个可能会或可能不会的列表 包括重复项,然后返回没有任何重复项的列表 包括在内,并按排序顺序。

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

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

    Óscar López pointed out那个

    [Your] 实现失败,因为您只测试了两个 连续值,您必须在其余部分中搜索当前元素 列表中,使用成员。

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

    如果您假设 list 已经排序,那么您的代码几乎是正确的。有两个必要的调整:

    1. 在基本情况下,您只检查是否(null? list)。但是,对于一个非空列表,然后比较(car list)(cadr list),但如果list 只有一个元素,则(cadr list) 是错误的。幸运的是,只有一个元素的列表没有重复项,因此您的基本情况可以是 (or (null? list) (null? (cdr list)))
    2. 第二个ifthen 部分必须是(remove-duplicated (cdr list)),而不是(cdr list),因为list 仍然可以在更远的位置有更多重复项(例如,(x x x ...) 或@ 987654335@)。

    这是您的代码,经过这些修改和一些 cmets:

    (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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-18
      • 2017-10-09
      • 2011-09-18
      • 2014-04-29
      • 2023-03-07
      • 2018-01-26
      • 2019-08-29
      相关资源
      最近更新 更多