datastructures 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/README.md +118 -2
  2. data/lib/datastructures.rb +4 -1
  3. data/lib/datastructures/adjacency_list.rb +82 -0
  4. data/lib/datastructures/linked_list.rb +179 -0
  5. data/lib/datastructures/tree_node.rb +73 -0
  6. data/lib/datastructures/version.rb +2 -2
  7. data/test/benchmarks.rb +36 -0
  8. data/test/coverage/assets/0.7.1/application.css +1110 -0
  9. data/test/coverage/assets/0.7.1/application.js +626 -0
  10. data/test/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
  11. data/test/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
  12. data/test/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
  13. data/test/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
  14. data/test/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
  15. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
  16. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
  17. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
  18. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
  19. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
  20. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
  21. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
  22. data/test/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
  23. data/test/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
  24. data/test/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
  25. data/test/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
  26. data/test/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
  27. data/test/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
  28. data/test/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
  29. data/test/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
  30. data/test/coverage/assets/0.7.1/favicon_green.png +0 -0
  31. data/test/coverage/assets/0.7.1/favicon_red.png +0 -0
  32. data/test/coverage/assets/0.7.1/favicon_yellow.png +0 -0
  33. data/test/coverage/assets/0.7.1/loading.gif +0 -0
  34. data/test/coverage/assets/0.7.1/magnify.png +0 -0
  35. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  36. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  37. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  38. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  39. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  40. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  41. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  42. data/test/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  43. data/test/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  44. data/test/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  45. data/test/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  46. data/test/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  47. data/test/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  48. data/test/coverage/index.html +72 -0
  49. data/test/test_adjacency_list.rb +79 -0
  50. data/test/test_datastructures.rb +5 -1
  51. data/test/test_linked_list.rb +96 -0
  52. data/test/test_stack.rb +1 -1
  53. data/test/test_tree.rb +77 -0
  54. metadata +68 -3
data/README.md CHANGED
@@ -26,7 +26,7 @@ A queue is a simple container-based structure that mimics a real-life queue (e.g
26
26
  Usage:
27
27
 
28
28
  ```ruby
29
- require 'datastructure'
29
+ require 'datastructures'
30
30
  queue = DataStructures::Queue.new
31
31
  queue.enqueue('first')
32
32
  queue.enqueue('second')
@@ -46,7 +46,7 @@ The stack is the sibling of the queue. It mimicks a real-life stack (e.g. of pap
46
46
  Usage:
47
47
 
48
48
  ```ruby
49
- require 'datastructure'
49
+ require 'datastructures'
50
50
  stack = DataStructures::Stack.new
51
51
  stack.push('first')
52
52
  stack.push('second')
@@ -57,4 +57,120 @@ stack.bottom # => 'first'
57
57
  stack.pop # => 'second'
58
58
  stack.pop # => 'first'
59
59
  stack.pop # => RuntimeError, "Stack underflow: nothing to pop"
60
+ ```
61
+
62
+ ## Day 3: Tree
63
+
64
+ A tree is a directed graph where any two nodes are connected by only one edge, and there is a specified 'root' node, away from which all edges are directed. For any edge, the node that it points from is the *parent*, and the node it points to is the *child*. This implementation is currently very crude, and I hope to expand it by:
65
+
66
+ - separating the node and tree classes
67
+ - including search and shortest path algorithms
68
+ - include more traversal algorithms
69
+
70
+ Currently the implementation offers the ability to construct a tree and traverse it manually using family methods (*parent*, *child*, *siblings*, *descendents*). Nodes can have associated data.
71
+
72
+ ```ruby
73
+ require 'datastructures'
74
+
75
+ # start with a root TreeNode
76
+ tree = DataStructures::TreeNode.new('root node')
77
+ tree.is_root? # => true
78
+ tree.is_leaf? # => true
79
+
80
+ # children are also TreeNodes
81
+ child1 = DataStructures::TreeNode.new('child')
82
+ tree.add_child(child1)
83
+ tree.child_count # => 1
84
+ tree.add_child(DataStructures::TreeNode.new('another child'))
85
+ tree.child_count # => 2
86
+ tree.is_root? # => true
87
+ tree.is_leaf? # => false
88
+ child1.is_leaf? # => true
89
+
90
+ # we can traverse by querying the family
91
+ tree.siblings # => []
92
+ child1.siblings.map{ |sibling| sibling.data } # => ["another child"]
93
+
94
+ child1.parent == tree # => true
95
+ child1.parent == child.siblings.first.parent # => true
96
+
97
+ tree.descendents.map { |d| d.data } # => ["child", "another child"]
98
+ ```
99
+
100
+ ## Day 4: Linked List
101
+
102
+ A linked list is a group of items which are ordered, and where the ordering is determined solely by the information each item contains about its neighbours.
103
+
104
+ This implementation is a doubly-linked list, which means that each item retains a reference to the *next* and *previous* items in the list.
105
+
106
+ The advantage of a linked list over a traditional array is that elements can be inserted and deleted efficiently.
107
+
108
+ ```ruby
109
+ ll = DataStructures::LinkedList.new('one')
110
+ ll.first.data # => 'one'
111
+ ll[0] # => 'one'
112
+ ll << 'two' # => ["one", "two"]
113
+ ll.size # => 2
114
+ ll.length # => 2
115
+ ll.last.data # => 'two'
116
+ ll[ll.size - 1] # => 'two'
117
+
118
+ # linked lists can act as stacks
119
+ ll.push 'three' # => ["one", "two", "three"]
120
+ ll.size # => 3
121
+ ll.pop # => "three"
122
+ ll.size # => 2
123
+ puts ll # => ["one", "two"]
124
+
125
+ # or as queues
126
+ ll.push 'three' # => ["one", "two", "three"]
127
+ ll.shift # => 'one'
128
+ ll.size # => 2
129
+ ll.first.data 'two'
130
+
131
+ # we can insert and delete
132
+ ll.insert(1, 'one point five') # => ["two", "one point five", "three"]
133
+ ll.delete(1)
134
+ ll.to_s # => ['two', 'three']
135
+ ```
136
+
137
+ ## Day 5: Adjacency List
138
+
139
+ An adjacency list is a structure for representing a graph. Nodes are named; the name can be any hashable object. Data can be stored in nodes, but not edges. The graph can be traversed manually by querying the neighbours of a node.
140
+
141
+ The implementation is currently basic. I plan to improve it by:
142
+
143
+ * implementing depth- and breadth-first search
144
+ * implementing Dijkstra's algorithm for finding the shortest path between two nodes
145
+ * visualise the graph (maybe d3.js)
146
+
147
+ ```ruby
148
+ require 'datastructures'
149
+
150
+ al = DataStructures::AdjacencyList.new
151
+
152
+ al.add('one', 1, [2])
153
+ al.add('two', 2, [3])
154
+ al.add('three', 3, [1])
155
+ al.add('four', 4, [2])
156
+
157
+ al.get_node_value(1) # => 'one'
158
+ al.set_node_value(1, 'new value')
159
+
160
+ al.neighbours(1) # => [2]
161
+ al.adjacent?(2, 3) # => true
162
+ al.adjacent?(1, 4) # => false
163
+
164
+ al.add_edge(1, 4)
165
+ al.adjacent?(1, 4) # => true
166
+
167
+ al.delete_edge(2, 3)
168
+ al.adjacent?(2, 3) # => false
169
+
170
+ al.to_s
171
+ # 1 (new value) => [2, 4]
172
+ # 2 (two) => []
173
+ # 3 (three) => [1]
174
+ # 4 (four) => [2]
175
+
60
176
  ```
@@ -1,3 +1,6 @@
1
1
  require 'datastructures/version'
2
2
  require 'datastructures/queue'
3
- require 'datastructures/stack'
3
+ require 'datastructures/stack'
4
+ require 'datastructures/tree_node'
5
+ require 'datastructures/linked_list'
6
+ require 'datastructures/adjacency_list'
@@ -0,0 +1,82 @@
1
+ module DataStructures
2
+
3
+ # Implements an Adjacency list with indexed nodes
4
+ class AdjacencyList
5
+
6
+ ALNode = Struct.new(:value)
7
+
8
+ attr_accessor :edges
9
+
10
+ # Returns a new AdjacencyList
11
+ # Nodes are accessed with unique names if +:named+ is true,
12
+ # otherwise they are accessed with integer indices (default).
13
+ def initialize(named=false)
14
+ @nodes = {}
15
+ @edges = {}
16
+ end
17
+
18
+ # Assignment - adds a new node with +:value+, and
19
+ # +:nodeidentifier+, and optionally an array of
20
+ # identifiers of other nodes defining +:edges+.
21
+ # Returns self, so that assignments can be chained.
22
+ def add(value, nodeidentifier, edges=Array.new)
23
+ node = ALNode.new(value)
24
+ @nodes[nodeidentifier] = node
25
+ @edges[nodeidentifier] = edges
26
+ self
27
+ end
28
+
29
+ # Removal - deletes the node at +:nodeidentifier+, which should be
30
+ # an integer index if this is an indexed adjacency list, or the name
31
+ # of the node if this is a names adjacency list.
32
+ def delete(nodeidentifier)
33
+ node = @nodes[nodeidentifier]
34
+ @nodes[nodeidentifier] = nil
35
+ @edges.delete node
36
+ end
37
+
38
+ # Removal - deletes the edge(s) +:edges+ connected to the node
39
+ # referenced by +:nodeidentifer+.
40
+ def delete_edge(nodeidentifier, *edges)
41
+ alledges = @edges[nodeidentifier]
42
+ edges.each { |edge| alledges.delete edge }
43
+ end
44
+
45
+ # Returns the value of the node with +:nodeidentifier+
46
+ def get_node_value nodeidentifier
47
+ @nodes[nodeidentifier].value
48
+ end
49
+
50
+ # Set with value of node at +:nodeidentifier+ to +:value+
51
+ def set_node_value(nodeidentifier, value)
52
+ @nodes[nodeidentifier].value = value
53
+ end
54
+
55
+ # Adds an edge from node with identifier +:x+ to node
56
+ # with identifier +:y+.
57
+ def add_edge(x, y)
58
+ @edges[x] << y
59
+ end
60
+
61
+ # True if +:x+ and +:y+ are connected by an edge.
62
+ def adjacent?(x, y)
63
+ @edges[x].include?(y) || @edges[y].include?(x)
64
+ end
65
+
66
+ # Return an array of identifiers of all nodes connected to
67
+ # node at +:nodeidentifier+ by edges.
68
+ def neighbours nodeidentifier
69
+ @edges[nodeidentifier]
70
+ end
71
+
72
+ # Return a string representation of the graph
73
+ def to_s
74
+ s = ""
75
+ @nodes.each do |identifier, node|
76
+ s += "#{identifier} (#{node.value}) => #{@edges[identifier]} \n"
77
+ end
78
+ s
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,179 @@
1
+ module DataStructures
2
+ # Implements a doubly Linked List.
3
+ class LinkedList
4
+
5
+ require 'pp'
6
+
7
+ LLNode = Struct.new(:data, :next, :previous)
8
+
9
+ attr_accessor :first
10
+ attr_accessor :last
11
+ attr_accessor :size
12
+
13
+ alias :length :size
14
+
15
+ # Returns a new LinkedList
16
+ #
17
+ # If no arguments are sent, the new LinkedList will be empty.
18
+ # When one or more objects are sent as arguments, the LinkedList
19
+ # is populated with those objects in the order sent.
20
+ def initialize *entries
21
+ @size = 0
22
+ unless entries.empty?
23
+ @first = LLNode.new(entries.shift, nil, nil)
24
+ @last = @first
25
+ @size = 1
26
+ self.push(*entries) unless entries.empty?
27
+ end
28
+ end
29
+
30
+ # Returns true if the LinkedList is empty
31
+ def empty?
32
+ @size == 0
33
+ end
34
+
35
+ # Element Reference - Returns the element at +index+
36
+ def [] index
37
+ current = @first
38
+ index.times do
39
+ current = current.next
40
+ end
41
+ current.data
42
+ end
43
+
44
+ # Element Assignment - Sets the element at +index+ to
45
+ # +:data+
46
+ def []= index, data
47
+ if index > @size - 1
48
+ # fill in the gaps
49
+ ((index - @size) + 1).times do
50
+ self.push nil
51
+ end
52
+ @last.data = data
53
+ else
54
+ # replace existing value
55
+ current = @first
56
+ index.times do
57
+ current = current.next
58
+ end
59
+ current.data = data
60
+ end
61
+ self
62
+ end
63
+
64
+ # Insert a node with +:data+ at +:index+.
65
+ # All nodes +:index+ and above get moved along one.
66
+ def insert index, data
67
+ old_node = @first
68
+ index.times do
69
+ old_node = old_node.next
70
+ end
71
+ new_node = LLNode.new(data, old_node, old_node.previous)
72
+ old_node.previous.next = new_node
73
+ old_node.previous = new_node
74
+ self
75
+ end
76
+
77
+ # Delete the node at +:index+
78
+ def delete index
79
+ current = @first
80
+ index.times do
81
+ current = current.next
82
+ end
83
+ current.previous.next = current.next
84
+ current.next.previous = current.previous
85
+ end
86
+
87
+ # Calls the given block once for each element in +self+, passing
88
+ # that element as a parameter
89
+ def each &block
90
+ current = @first
91
+ @size.times do
92
+ yield current.data
93
+ current = current.next
94
+ end
95
+ end
96
+
97
+ # Append - Pushes the given object(s) on to the end of this
98
+ # Linked List. The expression returns the list itself, so several
99
+ # appends may be chained together. See also #pop for the opposite effect.
100
+ def push *elements
101
+ elements.each do |element|
102
+ node = LLNode.new(element, nil, @last)
103
+ @first = node if @first.nil?
104
+ @last.next = node unless @last.nil?
105
+ @last = node
106
+ @size += 1
107
+ end
108
+ self
109
+ end
110
+
111
+ alias :<< :push
112
+
113
+ # Removes the last element from +self+ and returns it.
114
+ # Raises an underflow error if empty.
115
+ def pop
116
+ raise "Linked List underflow: nothing to pop." if self.size == 0
117
+ last = @last
118
+ @last = @last.previous
119
+ @size -= 1
120
+ last.data
121
+ end
122
+
123
+ # Prepends objects to the front of +self+, moving other elements
124
+ # upwards. See also #shift for the opposite effect.
125
+ def unshift *elements
126
+ elements.each do |element|
127
+ node = LLNode.new(element, @first, nil)
128
+ @last = node if @last.nil?
129
+ @first.previous = node unless @first.nil?
130
+ @first = node
131
+ @size += 1
132
+ end
133
+ self
134
+ end
135
+
136
+ # Removes the first element of self and returns it (shifting all
137
+ # other elements down by one.
138
+ def shift
139
+ raise "Linked List underflow: nothing to shift." if self.size == 0
140
+ first = @first
141
+ @first = @first.next
142
+ @size -= 1
143
+ first.data
144
+ end
145
+
146
+ # Returns the first index equal to +data+ (using == comparison).
147
+ # Counts from the beginning of the list.
148
+ def index data
149
+ current = @first
150
+ i = 0
151
+ while !current.nil?
152
+ return i if current.data == data
153
+ current = current.next
154
+ i += 1
155
+ end
156
+ nil
157
+ end
158
+
159
+ # Returns an array containing the data from the nodes in the list
160
+ def to_a
161
+ current = @first
162
+ array = []
163
+ while !current.nil?
164
+ array << current.data
165
+ current = current.next
166
+ end
167
+ array
168
+ end
169
+
170
+ # Returns a string representation of the list
171
+ def to_s
172
+ self.to_a.to_s
173
+ end
174
+
175
+ end # LinkedList
176
+
177
+ end # Biopsy
178
+
179
+ ll = DataStructures::LinkedList.new(*[1,2,3])
@@ -0,0 +1,73 @@
1
+ module DataStructures
2
+ # Implements a Tree data structure. TreeNode represents a single node,
3
+ # and has methods for accessing parents, siblings and children.
4
+ class TreeNode
5
+
6
+ attr_accessor :children
7
+ attr_accessor :parent
8
+ attr_accessor :data
9
+
10
+ # new TreeNode object.
11
+ # +:data+ is the content of the node, can be any Ruby object
12
+ # +:parent+ optionally specify the parent node. Must be a TreeNode. Not specifying a parent makes this a root node.
13
+ def initialize(data,parent=nil)
14
+ @data = data
15
+ @parent = nil
16
+ raise "parent must be a TreeNode" unless @parent.nil? || @parent.class == TreeNode
17
+ self.clear
18
+ end
19
+
20
+ # add a child node.
21
+ #+:child+ the node to add as a child. must be a TreeNode
22
+ def add_child child
23
+ child.parent = self
24
+ @children << child
25
+ end
26
+
27
+ # remove a child node.
28
+ #+:child+ the child node to remove. must be a TreeNode
29
+ def remove_child! child
30
+ @children.delete child
31
+ end
32
+
33
+ alias :<< :add_child
34
+
35
+ # count the direct children of this node
36
+ def child_count
37
+ @children.size
38
+ end
39
+
40
+ # true if the node is a leaf, i.e. has no children
41
+ def is_leaf?
42
+ @children.empty?
43
+ end
44
+
45
+ alias :empty? :is_leaf?
46
+
47
+ # true if this node is root, i.e. has no parent
48
+ def is_root?
49
+ @parent.nil?
50
+ end
51
+
52
+ # return an array of the siblings of this node. Nil if root.
53
+ def siblings
54
+ if self.is_root?
55
+ nil
56
+ else
57
+ @parent.children.reject { |sibling| sibling.equal? self }
58
+ end
59
+ end
60
+
61
+ # return an array of all descendents
62
+ def descendents
63
+ @children.map { |child| [child, child.descendents] }.flatten
64
+ end
65
+
66
+ # remove all children
67
+ def clear
68
+ @children = Array.new
69
+ end
70
+
71
+ end # TreeNode
72
+
73
+ end # Biopsy