99道lisp练习题----(三)格雷码和haffman树

P49 (**) Gray code.(格雷码问题)

An n-bit Gray code is a sequence of n-bit strings constructed according to certain rules. For example,
n = 1: C(1) = ['0','1'].
n = 2: C(2) = ['00','01','11','10'].
n = 3: C(3) = ['000','001','011','010',110,111,101,100].

Find out the construction rules and write a predicate with the following specification:

% gray(N,C) :- C is the N-bit Gray code

Can you apply the method of "result caching" in order to make the predicate more efficient, when it is to be used repeatedly?

  当n为1的时候,格雷码为'0','1',当n为2时,将长度为1的格雷码取
  出,然后在第0个末尾添加0和1,形成两个新的格雷码,在第二个末尾添加1和
  0,又形成两个新的格雷码,当n为3时,依次类推

(defun gray-code-gen (n)
  (labels ((rec (i acc)
	     (if (= i n)
		 acc
		 (let ((cw nil))
		   (rec (1+ i)
			(mapcan #'(lambda (ele)
				    (progn
				      (setq cw (not cw))
				      (if cw
					  (list (concatenate 'string ele "0")
						(concatenate 'string ele "1"))
					  (list (concatenate 'string ele "1")
						(concatenate 'string ele "0")))))
				acc))))))
    (rec 1 (list "0" "1"))))


采用分治策略的C++实现代码如下:

#include <iostream>
#include <string>
#include <vector>
void GrayCodeGen( int n, bool reverse, const std::string &s,
      std::vector<std::string> &r ) {
      std::string temp = reverse ? "10" : "01";
      if( n == 0 ) {
          r.push_back( s );
      } else {
         GrayCodeGen( n - 1, false, s + temp[0],
                      r );
         GrayCodeGen( n -1, true, s + temp[1],
                      r );
      }
          
}
int main() {
    std::vector<std::string> result;
    GrayCodeGen( 3, false , "", result );
    for( std::vector<std::string>::iterator iter = result.begin(); iter != result.end();
         ++iter ) {
                std::cout << *iter << std::endl;
    }
    getchar();
    return 0;
}



P50 (***) Huffman code.(haffman编码)

First of all, consult a good book on discrete mathematics or algorithms for a detailed description of Huffman codes!

We suppose a set of symbols with their frequencies, given as a list of fr(S,F) terms. Example: [fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)]. Our objective is to construct a list hc(S,C) terms, where C is the Huffman code word for the symbol S. In our example, the result could be Hs = [hc(a,'0'), hc(b,'101'), hc(c,'100'), hc(d,'111'), hc(e,'1101'), hc(f,'1100')] [hc(a,'01'),...etc.]. The task shall be performed by the predicate huffman/2 defined as follows: 

% huffman(Fs,Hs) :- Hs is the Huffman code table for the frequency table Fs

haffman树的构造过程:不断从一个链表中取出权重最小的两个节点,将两个节
点从链表中删除,将这两个节点构造成一个子树,根节点为两个节点权重之和,
然后将根节点加入链表,重复这个过程,直到链表中剩下一个节点,该节点为树
的根节点. 该haffman树的构造过程仅用11行代码

(defun haffman-tree (lst)
  (let ((lst (sort lst #'< :key #'second)))
    (if (= (length lst) 1)
	(car lst)
	(let ((fi (first lst))
	      (se (second lst))
	      (left (nthcdr 2 lst)))
	  (haffman-encode
	   (cons (list 'parent (+ (second fi)
				  (second se))
		       fi se)
		 left))))))

(defun haffman-code (lst)
  (labels ((rec (lst acc)
	     (if (= (length lst) 2)
		 (list (cons (first lst) acc))
		 (nconc (rec (third lst) (concatenate 'string acc "0"))
			(rec (fourth lst) (concatenate 'string acc "1"))))))
    (rec lst "")))


C++的实现过程如下:

#include <stdio.h>
#include <iostream>
#include <list>
#include <vector>
#include <functional>
#include <algorithm>

struct Node {
       char c_; //\0 donates parent
       int w_;
       struct Node *left_, *right_;
       
       //leaf
       Node( char c, int w ) : c_(c), w_(w), 
         left_(NULL), right_(NULL) {}
       
       Node( int w, Node *l, Node *r ) : c_('\0'), w_(w),
             left_(l), right_(r) {}
             
       bool operator<( const Node & o) const {                 
            return w_ < o.w_;
       }
};
class PtrComp {
public:            
      bool operator()( const Node * p1, const Node* p2 ) {
           return *p1 < *p2;
      }
};

void printTree(const std::string &str, Node * n);

void HaffmanTree( std::list<Node> &vn ) {
  int n = vn.size();
  std::vector<Node *> vnptr;
  for(std::list<Node>::iterator i = vn.begin(); i != vn.end(); i++ ) 
    vnptr.push_back( &*i );
          
  for( int i = 0; i < n - 1 ; i++ ) {
    std::sort( vnptr.begin(), vnptr.end(), PtrComp() );
  
    Node *n0 = vnptr[0], *n1 = vnptr[1];

    vn.push_front( Node(n0->w_+n1->w_, n0, n1) );

    std::copy( &vnptr[2], &vnptr[vnptr.size()], &vnptr[0] );
    vnptr.pop_back(), vnptr.pop_back();
    vnptr.push_back( &vn.front() );
  }

}

void printTree(const std::string &str, Node * n) {
     if( n == NULL )
         return;
     if( n->c_ != '\0' ) {
         std::cout << n->c_ << " " << str << std::endl;
         return ;
     }
     
     printTree( str+'0', n->left_ );
     printTree( str+'1', n->right_ );
}
int main() {
    Node n[6] = { Node('a', 45), Node('b', 13), Node('c', 12),
                  Node('d', 16), Node('e', 9), Node('f', 5) };
    std::list<Node> vn( n, n+6 );
    HaffmanTree( vn );

    printTree( "", &vn.front() );
    getchar();
    return 0;
}

       
注意,上面的std::list<Node>不能换成std::vector<Node>,因为vector在加入元素后,如果原来的内存大小不能够容纳,则所有的元素需要重新申请内存


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验二 递归算法设计与应用 一. 实验目的和要求 1. 加深对递归算法的理解,并针对具体问题设计算法; 2. 分析算法的复杂性,寻找比较高效的算法,并实现。 3. 分析格雷码问题,并设计递归算法求解之。 二. 基本原理 递归是一种重要的程序设计方法。使用递归方法有时可使算法简洁明了,易于设计。 递归指算法自己调用自己, 有直接递归与间接递归两种。 递归方法用于解决一类满足递归关系的问题。即:对原问题的求解可转化为对其性质相同的子问题的求解。 . 该类算法设计与实现的要点 1. 递归关系(特性):产生递归的基础。 当算法中某步骤要通过解性质相同的子问题实现时,该步骤用递归调用实现。 2. 递归出口(结束条件):确定递归的层数。 当子问题的规模充分小时可直接求解时,递归结束。 3. 参数设置:参数表示了原问题及其不同的子问题。 参数表示了子问题的大小和状态,以区别原问题以及不同层次的子问题。 4. 算法功能的设定:严格规定递归算法要解决什么样的问题。 算法功能的正确设定是保证递归过程正确进行的前提。 四. 实验内容――格雷码问题 1.问题描述 对于给定的正整数n,格雷码为满足如下条件的一个编码序列: (1) 序列由2n个编码组成,每个编码都是长度为n的二进制位串。 (2) 序列中无相同的编码。 (3) 序列中位置相邻的两个编码恰有一位不同。 例如:n=2时的格雷码为:{00, 01, 11, 10}。 设计求格雷码的递归算法并实现。 2. 具体要求(若在ACM平台上提交程序,必须按此要求)――平台上1769题 输入:输入的第一行是一个正整数m,表示测试例个数。接下来几行是m个测试例的数据,每个测试例的数据由一个正整数n组成。 输出:对于每个测试例n,输出2n个长度为n的格雷码。(为方便查看,在每个格雷码内,两个位之间用一个空格隔开,如,00输出为:0 0)。两个测试例的输出数据之间用一个空行隔开,最后一个测试例后无空行。 3. 测试数据 输入:2 4 5 输出:0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 1 1 0 0 1 1 1 0 1 0 1 0 1 0 0 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 0 0 0 1 1 0 0 0 1 1 1 0 0 1 0 1 0 0 1 0 0 0 1 1 0 0 0 1 1 0 1 0 1 1 1 1 0 1 1 1 0 0 1 0 1 0 0 1 0 1 1 0 1 0 0 1 0 1 0 0 0 1 1 0 0 0 1 1 0 0 1 1 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 0 1 0 0 1 0 1 0 1 1 0 1 1 1 1 0 1 1 0 1 0 0 1 0 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 4. 设计与实现的提示 长度为n的格雷码是由长度为n-1的格雷码变换而成的。 可以用数组或字符串来存储格雷码。注意:对于较大的正整数n,用数组存储容易引起死机。 按照定义2n个长度为n的格雷码序列是不唯一的,若在ACM平台上提交程序,要求输出的编码序列与给出的范例具有相同的规律。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值