集合的表示

15 篇文章 0 订阅

集合的表示


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)
  (cond ((null? set) (list x))
        ((< x (car set)) (cons x set))
        (else (cons (car set)
                    (adjoin-set3 x (cdr 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))))

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

  • ( n − 1 ) / / 2 (n-1)//2 (n1)//2
  • 1 1 1
  • n − ( 1 + ( n − 1 ) / / 2 ) n-(1+(n-1)//2) n(1+(n1)//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))))

结语

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值