add huffman implementation
This commit is contained in:
parent
7742eeb024
commit
427caabb1b
1 changed files with 129 additions and 0 deletions
129
huffman.rb
Normal file
129
huffman.rb
Normal file
|
@ -0,0 +1,129 @@
|
|||
class HuffNode
|
||||
attr_accessor :weight, :symbol, :left, :right, :parent
|
||||
|
||||
def initialize(params = {})
|
||||
@weight = params[:weight] || 0
|
||||
@symbol = params[:symbol] || ''
|
||||
@left = params[:left] || nil
|
||||
@right = params[:right] || nil
|
||||
@parent = params[:parent] || nil
|
||||
end
|
||||
|
||||
def walk(&block)
|
||||
walk_node('', &block)
|
||||
end
|
||||
|
||||
def walk_node(code, &block)
|
||||
yield(self, code)
|
||||
@left.walk_node(code + '0', &block) unless @left.nil?
|
||||
@right.walk_node(code + '1', &block) unless @right.nil?
|
||||
end
|
||||
|
||||
def leaf?
|
||||
@symbol != ''
|
||||
end
|
||||
|
||||
def internal?
|
||||
@symbol == ''
|
||||
end
|
||||
|
||||
def root?
|
||||
internal? and @parent.nil?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class NodeQueue
|
||||
attr_accessor :nodes, :huffman_root
|
||||
|
||||
def initialize(list)
|
||||
frequencies = {}
|
||||
list.each do |c|
|
||||
frequencies[c] ||= 0
|
||||
frequencies[c] += 1
|
||||
end
|
||||
@nodes = []
|
||||
frequencies.each do |c, w|
|
||||
@nodes << HuffNode.new(:symbol => c, :weight => w)
|
||||
end
|
||||
generate_tree
|
||||
end
|
||||
|
||||
def generate_tree
|
||||
while @nodes.size > 1
|
||||
sorted = @nodes.sort { |a,b| a.weight <=> b.weight }
|
||||
to_merge = []
|
||||
2.times { to_merge << sorted.shift }
|
||||
sorted << merge_nodes(to_merge[0], to_merge[1])
|
||||
@nodes = sorted
|
||||
end
|
||||
@huffman_root = @nodes.first
|
||||
end
|
||||
|
||||
def merge_nodes(node1, node2)
|
||||
left = node1.weight > node2.weight ? node2 : node1
|
||||
right = left == node1 ? node2 : node1
|
||||
node = HuffNode.new(:weight => left.weight + right.weight, :left => left, :right => right)
|
||||
left.parent = right.parent = node
|
||||
node
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HuffmanEncoding
|
||||
attr_accessor :root, :lookup, :input, :output
|
||||
|
||||
def initialize(input)
|
||||
@input = input
|
||||
@root = NodeQueue.new(input).huffman_root
|
||||
# @output = encode_list(input)
|
||||
end
|
||||
|
||||
def lookup
|
||||
return @lookup if @lookup
|
||||
@lookup = {}
|
||||
@root.walk do |node, code|
|
||||
@lookup[code] = node.symbol if node.leaf?
|
||||
end
|
||||
@lookup
|
||||
end
|
||||
|
||||
def encode(entry)
|
||||
self.lookup.invert[entry]
|
||||
end
|
||||
|
||||
def decode(code)
|
||||
self.lookup[code]
|
||||
end
|
||||
|
||||
def encode_list(list)
|
||||
code = ''
|
||||
list.each do |c|
|
||||
code += encode(c)
|
||||
end
|
||||
code
|
||||
end
|
||||
|
||||
def decode_string(code)
|
||||
code = code.to_s
|
||||
string = ''
|
||||
subcode = ''
|
||||
code.each_char do |bit|
|
||||
subcode += bit
|
||||
unless decode(subcode).nil?
|
||||
string += decode(subcode)
|
||||
subcode = ''
|
||||
end
|
||||
end
|
||||
string
|
||||
end
|
||||
|
||||
def to_s
|
||||
@output
|
||||
end
|
||||
|
||||
def [](char)
|
||||
encode(char)
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in a new issue