集合的表示

集合的表示

文章目录


SICP 练习 2.59 - 2.65 部分题解。

引言

数据对象表示的选择可能深刻影响使用数据的程序的性能

存储结构就是数据对象的表示,逻辑结构就是数据对象的方法。可以把数据对象的表示与程序的其他部分隔开。

帮助函数

(define (equal? x y)
  (cond ((and (symbol? x) (symbol? y)) (eq? x y))
        ((and (number? x) (number? y)) (= x y))
        ((and (null? x) (null? y)) true)
        ((and (list? x) (list? y)) (and (equal? (car x) (car y))
                                        (equal? (cdr x) (cdr y))))
        (else false)))


(define (filter predicate? items)
  (cond ((null? items) '())
        ((predicate? (car items))
         (cons (car items) (filter predicate? (cdr items))))
        (else (filter predicate? (cdr items)))))


(define (extend seq1 seq2)
  (cond ((null? seq1) seq2)
        (else (cons (car seq1) (extend (cdr seq1) seq2)))))

无序无重复的表

成员?

任何元素都不是空集的成员。

(define (element-of-set1? x set)
  (cond ((null? set) false)
        ((equal? x (car set)) true)
        (else (element-of-set1? x (cdr set)))))

添加

只添加新元素。

(define (adjoin-set1 x set)
  (if (element-of-set1? x set)
      set
      (cons x set)))

交集就是,集合1中在集合2中的元素组成的集合。

(define (intersection-set1 set1 set2)
  (cond ((or (null? set1) (null? set2)) '())
        ((element-of-set1? (car set1) set2)
         (cons (car set1)
               (intersection-set1 (cdr set1) set2)))
        (else (intersection-set1 (cdr set1) set2))))

从集合1向集合2中添加集合2没有的元素。

(define (union-set1 set1 set2)
  (cond ((null? set1) set2)
        ((element-of-set1? (car set1) set2)
         (union-set1 (cdr set1) set2))
        (else (cons (car set1) (union-set1 (cdr set1) set2)))))

无序可重复的表

(define (element-of-set2? x set)
  (cond ((null? set) false)
        ((equal? x (car set)) true)
        (else (element-of-set2? x (cdr set)))))

(define (adjoin-set2 x set)
  (cons x set))

(define (intersection-set2 set1 set2)
  (filter (lambda (x) (element-of-set2? x set2)) set1))

(define (union-set2 set1 set2)
  (extend set1 set2))

似乎,不查重只会使求交集的性能下降,占用空间变多,但程序简单了。

有序表

(define (element-of-set3? x set)
  (cond ((null? set) false)
        ((equal? x (car set)) true)
        ((> x (car set)) (element-of-set3? x (cdr set)))
        (else false)))

(define (adjoin-set3 x set)
  (extend (filter (lambda (i) (<= i x)) set)
          (cons x (filter (lambda (i) (> i x)) set))))

(define (intersection-set3 set1 set2)
  (cond ((or (null? set1) (null? set2)) '())
        ((equal? (car set1) (car set2))
         (cons (car set1) (intersection-set3 (cdr set1) (cdr set2))))
        ((< (car set1) (car set2)) (intersection-set3 (cdr set1) set2))
        (else (intersection-set3 set1 (cdr set2)))))

(define (union-set3 set1 set2)
  (cond ((null? set1) set2)
        ((null? set2) set1)
        ((let ((x1 (car set1)) (x2 (car set2)))
           (cond ((= x1 x2) (cons x1 (union-set3 (cdr set1) (cdr set2))))
                 ((< x1 x2) (cons x1 (union-set3 (cdr set1) set2)))
                 (else (cons x2 (union-set3 set1 (cdr set2)))))))))

顺序带来了效率提升,因为可以通过第一个元素得知其他元素的最小值。

二叉树

定义

(define (entry tree) (car tree))

(define (left-branch tree) (cadr tree))

(define (right-branch tree) (caddr tree))

(define (make-tree entry left right)
  (list entry left right))

成员

(define (element-of-set4? x set)
  (cond ((null? set) false)
        ((= x (entry set)) true)
        ((< x (entry set))
         (element-of-set4? x (left-branch set)))
        ((> x (entry set))
         (element-of-set4? x (right-branch set)))))

一次只会展开一个分支,对数复杂度。

添加

(define (adjoin-set4 x set)
  (cond ((null? set) (make-tree x '() '()))
        ((= x (entry set)) set)
        ((< x (entry set))
         (make-tree (entry set)
                    (adjoin-set4 x (left-branch set))
                    (right-branch set)))
        ((> x (entry set))
         (make-tree (entry set)
                    (left-branch set)
                    (adjoin-set4 x (right-branch set))))))

展平

[左子树] + [根] + [右子树],中序遍历。

(define (tree->list-1 tree)
  (if (null? tree)
      '()
      (extend (tree->list-1 (left-branch tree))
              (cons (entry tree)
                    (tree->list-1 (right-branch tree))))))

迭代形式:

(define (tree->list-2 tree)
  (define (copy-to-list tree result-list)
    (if (null? tree)
        result-list
        (copy-to-list (left-branch tree)
                      (cons (entry tree)
                            (copy-to-list (right-branch tree)
                                          result-list)))))
  (copy-to-list tree '()))

省去了 extend。

充气

(define (list->tree elements)
  (car (partial-tree elements (length elements))))

(define (partial-tree elts n)
  (if (= n 0)
      (cons '() elts)
      (let* ((left-size (quotient (- n 1) 2))
             (left-result (partial-tree elts left-size))
             (left-tree (car left-result))
             (non-left-elts (cdr left-result))
             (right-size (- n (+ left-size 1)))
             (this-entry (car non-left-elts))
             (right-result (partial-tree (cdr non-left-elts) right-size))
             (right-tree (car right-result))
             (remaining-elts (cdr right-result)))
        (cons (make-tree this-entry left-tree right-tree)
              remaining-elts))))

将表分为左子树、根、右子树三部分,每部分长度:

  • (n1)//2(n-1)//2(n−1)//2
  • 111
  • n(1+(n1)//2)n-(1+(n-1)//2)n−(1+(n−1)//2)

偶数结点的树,左子树会比右子树少一个结点。

交,并

虽然树与中序遍历结果是多对一的关系,上述转换“展平”与“充气”却是互逆的。又由于中序遍历的结果是有序的,所以可以使用有序列表的交、并。

(define (intersection-set4 set1 set2)
  (list->tree (intersection-set3 (tree->list-2 set1)
                                 (tree->list-2 set2))))

(define (union-set4 set1 set2)
  (list->tree (union-set3 (tree->list-2 set1)
                          (tree->list-2 set2))))

结语

虽然对数据的操作依赖于表示,但可以将程序中与表示有关的部分隔离,在必要时替换表示,甚至存储机制。

集合的表示集合的表示 __Lysias__ 发布了38 篇原创文章 · 获赞 5 · 访问量 4985 私信 关注
上一篇:ES6的Set用法


下一篇:Python 内置数据结构之 set